P0 blocker — carry tool lifecycle and explicit approvals through authenticated ZARA/1 #191

Closed
opened 2026-08-24 04:57:11 +00:00 by lost-rob0t · 2 comments
lost-rob0t commented 2026-08-24 04:57:11 +00:00 (Migrated from github.com)

Parent epic: #127
Blocks: #134
Depends on: #130, #131, #133, #209
Integrates with: #82/#83 runtime command/event boundary and existing agent/tool approval behavior.

Why this blocker exists

Auto-RAGE research for #134 found that the final Voice-replacement contract requires tools/search plus explicit approval events during a daemon voice session, including cross-principal isolation, but the current authenticated ZARA/1 / ZaraClient contract cannot express that interaction.

The runtime already has the right application-level vocabulary to reuse:

  • ApproveTool(tool_run_id=...)
  • RejectTool(tool_run_id=..., reason=...)
  • tool/user-input lifecycle events such as ToolStarted, ToolCompleted, UserInputRequired, and UserResponded.

The gap is the client/service protocol boundary:

  • zara/protocol.py has no tool/approval message family;
  • zara/protocol_runtime.py::command_from_message currently maps only turn.submit and turn.cancel;
  • runtime_event_to_message does not map tool/approval lifecycle events;
  • ZmqZaraClient.submit supports only SubmitTurn and CancelTurn.

Do not hide this missing contract inside #134 and do not create a second tool runtime.

Goal

Carry Zara's existing runtime tool lifecycle and explicit approval interaction through the authenticated ZaraClient service boundary and closed ZARA/1 protocol so a CLI/desktop/voice client can observe an approval request and answer it without direct access to RuntimeHost, AgentManager, tool objects, or provider internals.

Required architecture

  • keep ZaraClient as the application-facing seam;
  • keep ZeroMQ transport behind that seam;
  • gateway maps closed protocol commands/events to the existing runtime command/event boundary only;
  • no direct gateway calls into AgentManager, tools, Prolog, MemoryManager, or Qt;
  • authenticated server-side principal is authority; payload-supplied principal/user identity is never trusted;
  • approval is correlated to a stable tool_run_id plus the owning principal/session/conversation/turn as required by the existing runtime semantics;
  • unknown/stale/cancelled/mismatched approval requests fail closed;
  • approval/rejection is not a generic arbitrary-tool invocation API;
  • tool arguments/results remain typed/sanitized according to existing runtime policy and must not expose hidden provider protocol/reasoning.

Protocol direction

Research owns the exact names, but use a small closed message family such as:

  • server events for tool started/completed/failed and explicit user approval/input required;
  • client commands for approve/reject tied to the existing ApproveTool / RejectTool runtime commands;
  • acknowledgement/result events with ordinary request correlation.

Do not add arbitrary class names, callable names, shell snippets, Python imports, pickle, eval, or provider-native objects to the wire.

Security and isolation requirements

Prove at minimum:

  • principal A's pending approval is never delivered to principal B;
  • principal B cannot approve/reject A's tool_run_id, even if B guesses the identifier;
  • two sessions/devices for one principal follow an explicit ownership/visibility policy rather than whichever route happens to answer first;
  • approval after turn cancellation/replacement is rejected and cannot resurrect stale work;
  • approval from a pre-reconnect/stale session cannot authorize a newer/different request;
  • duplicate/replayed approval commands obey an explicit idempotency policy and never duplicate a side effect;
  • one principal waiting for approval does not block another principal's turns/tools;
  • disconnect while awaiting approval has bounded cleanup/timeout behavior;
  • authorization/capability policy is evaluated server-side after authentication;
  • audit/log output records IDs/action/result/timing only and does not leak transcript text, tool secrets/results, provider secrets, credentials, or CURVE private keys;
  • bounded queues/pending approval state prevent an abusive client from causing unbounded growth.

TDD requirements

Before production changes, add deterministic tests proving the expected red state for each behavior. Cover:

  • protocol encode/decode golden fixtures and closed-schema rejection;
  • runtime command mapping for approve/reject;
  • runtime tool/approval event mapping;
  • ZaraClient submit/receive behavior;
  • end-to-end authenticated local ZARA/1 approval-required fake/local tool flow;
  • cross-principal event and command isolation;
  • wrong/stale/unknown tool_run_id;
  • cancel-vs-approve race with deterministic barriers, not flaky sleeps;
  • duplicate/replay semantics;
  • reconnect while approval is pending;
  • slow/disconnected client while another principal continues;
  • sanitized errors/audit records;
  • lifecycle cleanup and bounded pending state.

Add one focused non-interactive script and wire the deterministic subset into the existing full repository/Nix gate.

Acceptance

An authenticated client can receive the existing runtime's explicit tool-approval request, approve or reject it through ZaraClient, observe the resulting tool lifecycle, and continue the same conversation while another authenticated principal remains fully isolated and unblocked. Cancelled/stale/replayed approvals cannot authorize work.

Scope boundary

This issue is intentionally narrow. It carries current Zara runtime tool/approval semantics across the daemon client boundary so #134 can prove Voice parity.

It does not own #150's future semantic IntentFrame, provider graph, typed device-capability advertisement/action plane, arbitrary remote execution, or a replacement tool system. Do not pull those future architecture slices forward merely to close this blocker.

No Prolog-RLM dependency or runtime path.

Parent epic: #127 Blocks: #134 Depends on: #130, #131, #133, #209 Integrates with: #82/#83 runtime command/event boundary and existing agent/tool approval behavior. ## Why this blocker exists Auto-RAGE research for #134 found that the final Voice-replacement contract requires tools/search plus **explicit approval events during a daemon voice session**, including cross-principal isolation, but the current authenticated `ZARA/1` / `ZaraClient` contract cannot express that interaction. The runtime already has the right application-level vocabulary to reuse: - `ApproveTool(tool_run_id=...)` - `RejectTool(tool_run_id=..., reason=...)` - tool/user-input lifecycle events such as `ToolStarted`, `ToolCompleted`, `UserInputRequired`, and `UserResponded`. The gap is the client/service protocol boundary: - `zara/protocol.py` has no tool/approval message family; - `zara/protocol_runtime.py::command_from_message` currently maps only `turn.submit` and `turn.cancel`; - `runtime_event_to_message` does not map tool/approval lifecycle events; - `ZmqZaraClient.submit` supports only `SubmitTurn` and `CancelTurn`. Do not hide this missing contract inside #134 and do not create a second tool runtime. ## Goal Carry Zara's **existing** runtime tool lifecycle and explicit approval interaction through the authenticated `ZaraClient` service boundary and closed `ZARA/1` protocol so a CLI/desktop/voice client can observe an approval request and answer it without direct access to `RuntimeHost`, AgentManager, tool objects, or provider internals. ## Required architecture - keep `ZaraClient` as the application-facing seam; - keep ZeroMQ transport behind that seam; - gateway maps closed protocol commands/events to the existing runtime command/event boundary only; - no direct gateway calls into AgentManager, tools, Prolog, MemoryManager, or Qt; - authenticated server-side principal is authority; payload-supplied principal/user identity is never trusted; - approval is correlated to a stable `tool_run_id` plus the owning principal/session/conversation/turn as required by the existing runtime semantics; - unknown/stale/cancelled/mismatched approval requests fail closed; - approval/rejection is not a generic arbitrary-tool invocation API; - tool arguments/results remain typed/sanitized according to existing runtime policy and must not expose hidden provider protocol/reasoning. ## Protocol direction Research owns the exact names, but use a small closed message family such as: - server events for tool started/completed/failed and explicit user approval/input required; - client commands for approve/reject tied to the existing `ApproveTool` / `RejectTool` runtime commands; - acknowledgement/result events with ordinary request correlation. Do not add arbitrary class names, callable names, shell snippets, Python imports, pickle, eval, or provider-native objects to the wire. ## Security and isolation requirements Prove at minimum: - principal A's pending approval is never delivered to principal B; - principal B cannot approve/reject A's `tool_run_id`, even if B guesses the identifier; - two sessions/devices for one principal follow an explicit ownership/visibility policy rather than whichever route happens to answer first; - approval after turn cancellation/replacement is rejected and cannot resurrect stale work; - approval from a pre-reconnect/stale session cannot authorize a newer/different request; - duplicate/replayed approval commands obey an explicit idempotency policy and never duplicate a side effect; - one principal waiting for approval does not block another principal's turns/tools; - disconnect while awaiting approval has bounded cleanup/timeout behavior; - authorization/capability policy is evaluated server-side after authentication; - audit/log output records IDs/action/result/timing only and does not leak transcript text, tool secrets/results, provider secrets, credentials, or CURVE private keys; - bounded queues/pending approval state prevent an abusive client from causing unbounded growth. ## TDD requirements Before production changes, add deterministic tests proving the expected red state for each behavior. Cover: - protocol encode/decode golden fixtures and closed-schema rejection; - runtime command mapping for approve/reject; - runtime tool/approval event mapping; - `ZaraClient` submit/receive behavior; - end-to-end authenticated local ZARA/1 approval-required fake/local tool flow; - cross-principal event and command isolation; - wrong/stale/unknown `tool_run_id`; - cancel-vs-approve race with deterministic barriers, not flaky sleeps; - duplicate/replay semantics; - reconnect while approval is pending; - slow/disconnected client while another principal continues; - sanitized errors/audit records; - lifecycle cleanup and bounded pending state. Add one focused non-interactive script and wire the deterministic subset into the existing full repository/Nix gate. ## Acceptance An authenticated client can receive the existing runtime's explicit tool-approval request, approve or reject it through `ZaraClient`, observe the resulting tool lifecycle, and continue the same conversation while another authenticated principal remains fully isolated and unblocked. Cancelled/stale/replayed approvals cannot authorize work. ## Scope boundary This issue is intentionally narrow. It carries **current Zara runtime tool/approval semantics** across the daemon client boundary so #134 can prove Voice parity. It does **not** own #150's future semantic `IntentFrame`, provider graph, typed device-capability advertisement/action plane, arbitrary remote execution, or a replacement tool system. Do not pull those future architecture slices forward merely to close this blocker. No Prolog-RLM dependency or runtime path.
lost-rob0t commented 2026-08-24 07:47:30 +00:00 (Migrated from github.com)

Auto-RAGE Analyze for #191 rejected a wire-only realization because the production runtime cannot currently pause or resume approval-required tools. The schema exists, but LangGraphRuntimeBackend.approve_tool/reject_tool is unsupported, the graph has no checkpointer/thread identity, and no production path emits a pending approval before a side effect.

New blocker #209 owns the transport-neutral canonical runtime approval lifecycle. #191 now depends on #209 and will resume only after #209 proves real pause/approve/reject/cancel/replay/isolation behavior. This prevents #134 from obtaining a synthetic PASS through fake wire events.

Auto-RAGE Analyze for #191 rejected a wire-only realization because the production runtime cannot currently pause or resume approval-required tools. The schema exists, but `LangGraphRuntimeBackend.approve_tool/reject_tool` is unsupported, the graph has no checkpointer/thread identity, and no production path emits a pending approval before a side effect. New blocker #209 owns the transport-neutral canonical runtime approval lifecycle. #191 now depends on #209 and will resume only after #209 proves real pause/approve/reject/cancel/replay/isolation behavior. This prevents #134 from obtaining a synthetic PASS through fake wire events.
lost-rob0t commented 2026-08-24 13:33:10 +00:00 (Migrated from github.com)

RAGE prerequisite update: #209 is complete and closed by PR #211.

  • final implementation head: 394a0ede032b0528b9eb727512666db9a822e09f
  • exact-head CI run: 32730453589 — test, Ubuntu shared mic, and Arch shared mic all passed
  • implementation merge: ddc30c737ee7eff030dd424949a490f5c6cf52dd
  • immutable closeout evidence: PR #212, merge 55f4f76b2906a05f289f3720e339702d5139aa6d

The transport-neutral production runtime can now pause before an approval-required side effect, accept ApproveTool / RejectTool, resume or reject once, and fail closed under stale/replayed ids, cancellation, timeout, restart, shutdown, capacity pressure, and principal separation. #191 is now dependency-satisfied and should re-enter Analyze in a fresh RAGE iteration; its authenticated ZARA/1 schema, route/session visibility, reconnect, and same-principal multi-device policy remain owned here.

RAGE prerequisite update: #209 is complete and closed by PR #211. - final implementation head: `394a0ede032b0528b9eb727512666db9a822e09f` - exact-head CI run: `32730453589` — `test`, Ubuntu shared mic, and Arch shared mic all passed - implementation merge: `ddc30c737ee7eff030dd424949a490f5c6cf52dd` - immutable closeout evidence: PR #212, merge `55f4f76b2906a05f289f3720e339702d5139aa6d` The transport-neutral production runtime can now pause before an approval-required side effect, accept `ApproveTool` / `RejectTool`, resume or reject once, and fail closed under stale/replayed ids, cancellation, timeout, restart, shutdown, capacity pressure, and principal separation. #191 is now dependency-satisfied and should re-enter Analyze in a fresh RAGE iteration; its authenticated ZARA/1 schema, route/session visibility, reconnect, and same-principal multi-device policy remain owned here.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/zara#191
No description provided.