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
25 changes: 23 additions & 2 deletions crates/adapter-smith/src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,13 +377,24 @@ const TOOL_OUTPUT_BUDGET: usize = 8_000;
/// Build a fresh [`ToolCtx`] that shares the daemon `Client` with `src`
/// when one has already been opened. Used to fan a turn's Safe tool
/// calls into parallel tasks without each one re-connecting.
/// Forget what the `agentd_context` tool already served this model: called
/// whenever message history is compacted or pruned, since served-but-dropped
/// content must be sent again (spec 0095).
pub(crate) fn reset_context_serve(ctx: &ToolCtx) {
ctx.context_serve
.lock()
.unwrap_or_else(|p| p.into_inner())
.reset();
}

pub(crate) fn clone_tool_ctx(src: &ToolCtx) -> ToolCtx {
let new_ctx = ToolCtx {
cwd: src.cwd.clone(),
session_id: src.session_id.clone(),
client: tokio::sync::OnceCell::new(),
emit: src.emit.clone(),
procs: src.procs.clone(),
context_serve: src.context_serve.clone(),
sandbox: src.sandbox.clone(),
sandbox_policy: src.sandbox_policy.clone(),
};
Expand Down Expand Up @@ -627,6 +638,7 @@ pub async fn run(
client: tokio::sync::OnceCell::new(),
emit: Some(emit.clone()),
procs: Arc::new(crate::tools::proc::ProcRegistry::default()),
context_serve: Arc::new(std::sync::Mutex::new(Default::default())),
sandbox: announce_sandbox(&emit),
sandbox_policy: crate::sandbox::SandboxPolicy::workspace_default(&cwd),
};
Expand Down Expand Up @@ -835,6 +847,9 @@ pub async fn run(
tracing::warn!(error = ?e, "auto-compact: persist rewrite failed");
}
}
// The summarized history no longer holds what the
// context tool served; the next call must resend.
reset_context_serve(&tool_ctx);
emit.emit(SessionEvent::ContextCompacted {
kept_turns: outcome.kept_turn_pairs,
dropped_turns: outcome.dropped_turn_pairs,
Expand All @@ -849,7 +864,9 @@ pub async fn run(
}
}
}
let _pruned = context::prune_to_budget(&mut messages, budget);
if context::prune_to_budget(&mut messages, budget) > 0 {
reset_context_serve(&tool_ctx);
}

let mut sink = MessageSink { emit: &emit };
let turn = match crate::provider_watchdog::complete(
Expand Down Expand Up @@ -878,7 +895,9 @@ pub async fn run(
now_ms,
);
let retry_budget = ((new_limit as f64) * context::UTILIZATION) as usize;
let _pruned = context::prune_to_budget(&mut messages, retry_budget);
if context::prune_to_budget(&mut messages, retry_budget) > 0 {
reset_context_serve(&tool_ctx);
}
emit.emit(SessionEvent::Status {
state: SessionState::Running,
detail: Some(format!(
Expand Down Expand Up @@ -1444,6 +1463,7 @@ async fn run_with_interrupt(
let session_id = ctx.session_id.clone();
let emit = ctx.emit.clone();
let procs = ctx.procs.clone();
let context_serve = ctx.context_serve.clone();
let sandbox = ctx.sandbox.clone();
let sandbox_policy = ctx.sandbox_policy.clone();
let client_cell = std::sync::Mutex::new(None::<Arc<construct_client::Client>>);
Expand All @@ -1457,6 +1477,7 @@ async fn run_with_interrupt(
client: tokio::sync::OnceCell::new(),
emit,
procs,
context_serve,
sandbox,
sandbox_policy,
};
Expand Down
13 changes: 11 additions & 2 deletions crates/adapter-smith/src/interactive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2184,6 +2184,7 @@ pub async fn run(
client: tokio::sync::OnceCell::new(),
emit: Some(emit.clone()),
procs: std::sync::Arc::new(crate::tools::proc::ProcRegistry::default()),
context_serve: std::sync::Arc::new(std::sync::Mutex::new(Default::default())),
sandbox: crate::agent::announce_sandbox(&emit),
sandbox_policy: crate::sandbox::SandboxPolicy::workspace_default(&cwd),
};
Expand Down Expand Up @@ -2582,6 +2583,7 @@ pub async fn run(
tracing::warn!(error = ?e, "compact: persist rewrite failed");
}
}
crate::agent::reset_context_serve(&tool_ctx);
emit.emit(SessionEvent::ContextCompacted {
kept_turns: outcome.kept_turn_pairs,
dropped_turns: outcome.dropped_turn_pairs,
Expand Down Expand Up @@ -2815,6 +2817,7 @@ pub async fn run(
tracing::warn!(error = ?e, "auto-compact: persist rewrite failed");
}
}
crate::agent::reset_context_serve(&tool_ctx);
emit.emit(SessionEvent::ContextCompacted {
kept_turns: outcome.kept_turn_pairs,
dropped_turns: outcome.dropped_turn_pairs,
Expand All @@ -2833,7 +2836,9 @@ pub async fn run(
}
}
}
let _pruned = context::prune_to_budget(&mut messages, budget);
if context::prune_to_budget(&mut messages, budget) > 0 {
crate::agent::reset_context_serve(&tool_ctx);
}
let mut sink = PtySink::new(&emit, pty_width, turn_started_at_ms);
// Wrap the provider call so user typing during the
// stream is fed to the editor and pressed-Enter lines
Expand Down Expand Up @@ -2882,7 +2887,9 @@ pub async fn run(
now_ms,
);
let retry_budget = ((new_limit as f64) * context::UTILIZATION) as usize;
let _pruned = context::prune_to_budget(&mut messages, retry_budget);
if context::prune_to_budget(&mut messages, retry_budget) > 0 {
crate::agent::reset_context_serve(&tool_ctx);
}
term.note(&format!(
"(context overflow — relearned cap as {} tokens, retrying)",
new_limit
Expand Down Expand Up @@ -4428,6 +4435,7 @@ async fn run_with_supervisor(
let session_id = ctx.session_id.clone();
let emit = ctx.emit.clone();
let procs = ctx.procs.clone();
let context_serve = ctx.context_serve.clone();
let sandbox = ctx.sandbox.clone();
let sandbox_policy = ctx.sandbox_policy.clone();
let client_seed = ctx.client.get().cloned();
Expand All @@ -4439,6 +4447,7 @@ async fn run_with_supervisor(
client: tokio::sync::OnceCell::new(),
emit,
procs,
context_serve,
sandbox,
sandbox_policy,
};
Expand Down
17 changes: 14 additions & 3 deletions crates/adapter-smith/src/tools/construct_daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,26 @@ impl Tool for Context {
agent_context::TOOL_DESCRIPTION
}
fn schema(&self) -> Value {
json!({ "type": "object", "properties": {} })
json!({
"type": "object",
"properties": {
"refresh": { "type": "boolean", "description": "Resend static fields and unchanged content. Pass true when earlier agentd_context results may have been compacted out of your context." },
"skip_memory": { "type": "boolean", "description": "Omit memory file contents you already hold verbatim (e.g. you just wrote them); paths and etags are still returned." },
"include_reference": { "type": "boolean", "description": "Include the full memory/widget policy and Markdown extension reference." }
}
})
}
fn risk(&self) -> ToolRisk {
ToolRisk::Safe
}
async fn run(&self, _input: Value, _ctx: &ToolCtx) -> Result<ToolOutcome> {
async fn run(&self, input: Value, ctx: &ToolCtx) -> Result<ToolOutcome> {
let request = agent_context::ContextRequest::from_args(&input);
let mut state = ctx.context_serve.lock().unwrap_or_else(|p| p.into_inner());
let response =
agent_context::compact_response(agent_context::build_from_env(), &request, &mut state);
Ok(ToolOutcome {
ok: true,
output: serde_json::to_string_pretty(&agent_context::build_from_env())?,
output: serde_json::to_string(&response)?,
})
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/adapter-smith/src/tools/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ mod tests {
client: tokio::sync::OnceCell::new(),
emit: None,
procs: std::sync::Arc::new(crate::tools::proc::ProcRegistry::default()),
context_serve: std::sync::Arc::new(std::sync::Mutex::new(Default::default())),
sandbox: std::sync::Arc::new(crate::sandbox::Noop),
sandbox_policy,
}
Expand Down Expand Up @@ -601,6 +602,7 @@ mod tests {
client: tokio::sync::OnceCell::new(),
emit: None,
procs: std::sync::Arc::new(crate::tools::proc::ProcRegistry::default()),
context_serve: std::sync::Arc::new(std::sync::Mutex::new(Default::default())),
sandbox: std::sync::Arc::new(sb),
sandbox_policy: policy,
};
Expand Down Expand Up @@ -669,6 +671,7 @@ mod tests {
client: tokio::sync::OnceCell::new(),
emit: None,
procs: std::sync::Arc::new(crate::tools::proc::ProcRegistry::default()),
context_serve: std::sync::Arc::new(std::sync::Mutex::new(Default::default())),
sandbox: std::sync::Arc::new(sb),
sandbox_policy: policy,
};
Expand Down
5 changes: 5 additions & 0 deletions crates/adapter-smith/src/tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ pub struct ToolCtx {
/// earlier `shell` call started. Cloned (not re-created) by
/// [`crate::agent::clone_tool_ctx`] and the parallel-call paths.
pub procs: Arc<proc::ProcRegistry>,
/// What the `agentd_context` tool has already served this session's
/// model, so repeat calls omit it (spec 0095). Shared across every clone
/// of the context; reset when the message history is compacted or pruned
/// (the model no longer holds what was served).
pub context_serve: Arc<std::sync::Mutex<construct_protocol::agent_context::ContextServeState>>,
/// OS sandbox backend for the session (`Noop` when disabled). Shared
/// across every clone of the context — selected once at session start.
pub sandbox: Arc<dyn crate::sandbox::Sandbox>,
Expand Down
3 changes: 3 additions & 0 deletions crates/adapter-smith/src/tools/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ mod tests {
client: tokio::sync::OnceCell::new(),
emit: None,
procs: std::sync::Arc::new(crate::tools::proc::ProcRegistry::default()),
context_serve: std::sync::Arc::new(std::sync::Mutex::new(Default::default())),
sandbox: std::sync::Arc::new(crate::sandbox::Noop),
sandbox_policy,
}
Expand Down Expand Up @@ -334,6 +335,7 @@ mod tests {
client: tokio::sync::OnceCell::new(),
emit: None,
procs: std::sync::Arc::new(crate::tools::proc::ProcRegistry::default()),
context_serve: std::sync::Arc::new(std::sync::Mutex::new(Default::default())),
sandbox: std::sync::Arc::new(sb),
sandbox_policy: policy,
};
Expand Down Expand Up @@ -400,6 +402,7 @@ mod tests {
client: tokio::sync::OnceCell::new(),
emit: None,
procs: std::sync::Arc::new(crate::tools::proc::ProcRegistry::default()),
context_serve: std::sync::Arc::new(std::sync::Mutex::new(Default::default())),
sandbox: std::sync::Arc::new(sb),
sandbox_policy: policy,
};
Expand Down
15 changes: 12 additions & 3 deletions crates/mcp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ async fn run_inner(
) -> Result<()> {
let mut stdin = BufReader::new(tokio::io::stdin());
let mut stdout = tokio::io::stdout();
// One MCP server process serves one agent, so what the context tool has
// already sent that agent is process state (spec 0095).
let context_state = tools::ContextServeState::default();

loop {
let raw = match transport::read_message(&mut stdin).await {
Expand All @@ -74,7 +77,8 @@ async fn run_inner(
client = new_client;
}
}
let resp = handle_request(&client, session_id.as_deref(), req).await;
let resp =
handle_request(&client, session_id.as_deref(), &context_state, req).await;
let v = match serde_json::to_value(&resp) {
Ok(v) => v,
Err(_) => continue,
Expand All @@ -91,7 +95,12 @@ async fn run_inner(
}
}

async fn handle_request(client: &Arc<Client>, session_id: Option<&str>, req: Request) -> Response {
async fn handle_request(
client: &Arc<Client>,
session_id: Option<&str>,
context_state: &tools::ContextServeState,
req: Request,
) -> Response {
let id = req.id.clone();
let params = req.params.clone().unwrap_or(serde_json::Value::Null);

Expand All @@ -110,7 +119,7 @@ async fn handle_request(client: &Arc<Client>, session_id: Option<&str>, req: Req
}),
),
"tools/list" => Response::ok(id, serde_json::json!({ "tools": tools::catalog() })),
"tools/call" => match tools::call(client, session_id, params).await {
"tools/call" => match tools::call(client, session_id, context_state, params).await {
Ok(content) => Response::ok(id, content),
Err(e) => Response::ok(
id,
Expand Down
Loading
Loading