Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions src/harness/agy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub(crate) mod stream;
pub use catalog::Catalog;

use super::adapter::{Adapter, Invocation, SkillLoading, without};
use super::interpretation::{Interpretation, Retained};
use super::interpretation::Interpretation;
use super::settings::{Terminal, ask_setting};
use super::{Choice, Harness, ModelAndEffort, Settings};
use crate::progress;
Expand Down Expand Up @@ -142,11 +142,7 @@ impl Adapter for Agy {
super::instructions::link_fallback(worktree, "GEMINI.md", &["AGENTS.md"])
}
fn interpretation(&self, _worktree: &Path, _prompt: &str) -> Interpretation {
Interpretation::new(
self.name(),
Box::new(stream::AgyProgress::default()),
Retained::None,
)
Interpretation::new(self.name(), Box::new(stream::AgyProgress::default()))
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/harness/claude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::process::Command;
use anyhow::{Context, Result, bail};

use super::adapter::{Adapter, Invocation, SkillLoading};
use super::interpretation::{Interpretation, Retained, Stream};
use super::interpretation::{Interpretation, Stream};
use super::settings::{Terminal, ask_setting};
use super::{Choice, Harness, ModelAndEffort, Settings, said};
use crate::progress;
Expand Down Expand Up @@ -67,7 +67,7 @@ impl Adapter for Claude {
ask_claude(outside, current).map(Some)
}
fn interpretation(&self, _worktree: &Path, _prompt: &str) -> Interpretation {
Interpretation::new(self.name(), Box::new(Stream::claude()), Retained::None)
Interpretation::new(self.name(), Box::new(Stream::claude()))
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/harness/codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::path::Path;
use anyhow::Result;

use super::adapter::{Adapter, Invocation, SkillLoading};
use super::interpretation::{Interpretation, Retained};
use super::interpretation::Interpretation;
use super::settings::{Terminal, ask_checked_setting};
use super::{Choice, Harness, ModelAndEffort, Settings};
use crate::progress;
Expand Down Expand Up @@ -132,7 +132,6 @@ impl Adapter for Codex {
Interpretation::new(
self.name(),
Box::new(stream::CodexProgress::in_worktree(worktree)),
Retained::None,
)
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/harness/grok.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::path::Path;
use anyhow::Result;

use super::adapter::{Adapter, Invocation, SkillLoading, without};
use super::interpretation::{Interpretation, Retained, Stream};
use super::interpretation::{Interpretation, Stream};
use super::settings::{Terminal, ask_checked_setting};
use super::{Choice, Harness, ModelAndEffort, Settings};

Expand Down Expand Up @@ -139,7 +139,7 @@ impl Adapter for Grok {
Ok(Some(ModelAndEffort { model, effort }))
}
fn interpretation(&self, _worktree: &Path, _prompt: &str) -> Interpretation {
Interpretation::new(self.name(), Box::new(Stream::grok()), Retained::None)
Interpretation::new(self.name(), Box::new(Stream::grok()))
}
}

Expand Down
75 changes: 29 additions & 46 deletions src/harness/interpretation.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
//! Interpret a Harness session once, including optional retained records.

use std::path::PathBuf;
use std::process::{ExitStatus, Output};

use anyhow::{Result, anyhow};

use super::{muse, opencode, said};
use super::said;
use crate::interrupt;

mod stream;
Expand All @@ -20,7 +19,7 @@ use security::Security;
pub struct Interpretation {
cli: &'static str,
decoder: Box<dyn Decoder>,
retained: Retained,
retained: Option<Box<dyn Retained>>,
security: Option<Security>,
models: models::Models,
}
Expand All @@ -33,12 +32,25 @@ pub struct Completion {

#[derive(Default)]
pub struct Report {
/// Ordered progress supplied after retained completion.
pub completion_progress: Vec<String>,
pub warnings: Vec<String>,
pub summary: Option<String>,
/// Models reported by session output or retained records.
pub models: Vec<String>,
}

impl Report {
/// Retained adapters opt into completion Model progress after reconciliation.
pub(super) fn set_model_progress(&mut self) {
self.completion_progress = self
.models
.iter()
.map(|model| format!("Model: {model}"))
.collect();
}
}

#[derive(Debug)]
pub struct Ended {
pub session_id: Option<String>,
Expand Down Expand Up @@ -101,46 +113,10 @@ pub(super) enum ModelScope {
AttributionOnly,
}

pub(super) enum Retained {
None,
Muse(Option<PathBuf>),
OpenCode(PathBuf),
}

impl Retained {
fn reconcile(self, facts: &mut Facts) -> Result<()> {
match self {
Self::None => {}
Self::Muse(root) => {
if let Some(log) = root
.as_deref()
.zip(facts.ended.session_id.as_deref())
.and_then(|(root, id)| muse::log::read(root, id))
{
facts.report.summary = log.summary();
if log.message.is_some() {
facts.ended.final_message = log.message;
}
facts.report.models = log.models;
}
}
Self::OpenCode(worktree) => {
if let Some(id) = facts.ended.session_id.as_deref()
&& let Some(export) = opencode::export::read(&worktree, id)?
{
// A readable export is authoritative, even without text.
facts.ended.final_message = export.message;
facts.report.summary = export.summary;
facts.report.models = export.models;
if export.failure.is_some() {
facts.outcome = TurnOutcome::Failed;
facts.diagnostic = facts.diagnostic.take().or(export.failure);
}
}
}
}
Ok(())
}
/// Optional retained reconciliation belongs to the supplying Harness adapter.
/// Owned implementations follow Interpretation into the transcript reader.
pub(super) trait Retained: Send {
fn reconcile(self: Box<Self>, facts: &mut Facts) -> Result<()>;
}

enum Failure {
Expand Down Expand Up @@ -185,16 +161,21 @@ impl Failure {
}

impl Interpretation {
pub(super) fn new(cli: &'static str, decoder: Box<dyn Decoder>, retained: Retained) -> Self {
pub(super) fn new(cli: &'static str, decoder: Box<dyn Decoder>) -> Self {
Self {
cli,
decoder,
retained,
retained: None,
security: None,
models: models::Models::default(),
}
}

pub(super) fn with_retained(mut self, retained: Box<dyn Retained>) -> Self {
self.retained = Some(retained);
self
}

/// Apply refusal and Model reporting rules independently of the session's log label.
pub fn for_security(mut self, requested_model: Option<&str>) -> Self {
self.security = Some(Security::new(requested_model));
Expand Down Expand Up @@ -262,7 +243,9 @@ impl Interpretation {
if refusal.is_some() {
facts.outcome = TurnOutcome::Failed;
}
let recovered = self.retained.reconcile(&mut facts);
let recovered = self
.retained
.map_or(Ok(()), |retained| retained.reconcile(&mut facts));
if let Err(error) = interrupt::check() {
return (None, Err(Failure::Execution(error)));
}
Expand Down
Loading
Loading