wip(13-08): checkpoint before operator session restart — Task 1 GREEN (28/28), Task 2 in progress

Executor stopped deliberately for a session restart (bypass-permissions relaunch).
Executor's final report: 'cargo test assistant confirm-gate suite 28/28 green,
individual nonce test passes; committing Task 1 next — first verify the
tools.rs/grants.rs/backends diffs are formatting-only.'

Task 1 (D-07/D-11 confirm gate, backend) is implemented and test-green but this
checkpoint is verbatim-uncommitted-state, NOT the reviewed atomic Task 1 commit:
continuation executor should verify diffs, then reset --soft or commit-on-top
into proper feat(13-08) task commits. Task 2 (ToolConfirmModal.vue trusted
chrome, Chat.vue + contextBroker.ts wiring) is partially built, tests written.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-05 11:34:31 -04:00
co-authored by Claude Fable 5
parent db11c625c8
commit fc09d7a292
11 changed files with 1011 additions and 53 deletions
@@ -28,10 +28,68 @@ impl RpcHandler {
"assistant.list-tools" => self.handle_assistant_list_tools().await,
"assistant.grants-get" => self.handle_assistant_grants_get().await,
"assistant.grants-set" => self.handle_assistant_grants_set(params).await,
"assistant.pending" => self.handle_assistant_pending().await,
"assistant.confirm-tool" => self.handle_assistant_confirm_tool(params).await,
other => anyhow::bail!("no such assistant method: {other}"),
}
}
/// assistant.pending — the current pending destructive-tool
/// confirmation, if any: the node-authored description and the
/// node-minted nonce. This is how the trusted chrome *fetches* the
/// dialog text over the authenticated RPC session rather than
/// receiving it from the iframe (D-11) — the iframe has no path into
/// this text and no way to answer it.
async fn handle_assistant_pending(self: &Arc<Self>) -> Result<serde_json::Value> {
let gate = crate::assistant::confirm::global();
Ok(match gate.peek() {
Some(p) => serde_json::json!({
"pending": {
"req_id": p.req_id,
"nonce": p.nonce,
"description": p.description,
"tool_name": p.tool_name,
}
}),
None => serde_json::json!({ "pending": null }),
})
}
/// assistant.confirm-tool — resolve a pending confirmation. Params:
/// `{ "req_id": string, "nonce": string, "approved": bool }`. The
/// nonce must be the node-minted one for exactly that pending action
/// (S-02); a mismatched or replayed nonce is refused loudly — a
/// mismatch can only mean a replay attempt or a trusted-chrome bug, so
/// it is logged at error level and surfaced to the caller as an error,
/// not swallowed as a toast.
async fn handle_assistant_confirm_tool(
self: &Arc<Self>,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
let req_id = params
.get("req_id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing req_id"))?;
let nonce = params
.get("nonce")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing nonce"))?;
let approved = params
.get("approved")
.and_then(|v| v.as_bool())
.ok_or_else(|| anyhow::anyhow!("Missing approved"))?;
let gate = crate::assistant::confirm::global();
match gate.resolve(req_id, nonce, approved) {
Ok(()) => Ok(serde_json::json!({ "resolved": true, "approved": approved })),
Err(refusal) => {
tracing::error!(%req_id, %refusal, "assistant.confirm-tool refused");
anyhow::bail!("confirmation refused: {refusal}")
}
}
}
/// assistant.list-tools — the tools currently visible to the local
/// operator (i.e. whose category is currently granted), each with its
/// category and destructive flag, so neode-ui can render an honest