Skip to content

fix: deliver steers that ask to clear pending permissions - #73

Merged
supermomonga merged 1 commit into
mainfrom
fix/zcode-steer-session-error
Oct 23, 2026
Merged

supermomonga merged 1 commit into
mainfrom
fix/zcode-steer-session-error

Conversation

@supermomonga

Copy link
Copy Markdown
Owner

Problem

Sending a message while ZCode was running failed with [System Error] Duplicate ZCode clientMessageId, and the message was not delivered. Every steer from the Paseo app was affected.

Cause

  • Paseo marks every message typed or spoken in the app during a run with clearPendingPermissions: true (Paseo session.ts). The provider rejected such steers with INTERACTION_UNSUPPORTED.
  • PluginAgentSession.steerActiveTurn treats a failed result as unavailable. steerOrReplaceActiveTurn then interrupts the turn and resends the prompt with the same clientMessageId. The provider kept every used ID, so send() threw Duplicate ZCode clientMessageId.
  • test:upstream and test:steering-runtime steered without the flag, so neither covered this path.

Change

  • Steers that clear permissions. They are sent as native guidance like any other steer. Once ZCode accepts the input, the session declines pending permissions, questions and Plan approvals with their deny action until the input's userInput row appears. Requests opened in the meantime are declined too, because a later tool of the same model step can ask again. Requests after the message is read are left to the user. This follows Paseo's provider contract (docs/providers.md), as the Codex and Claude providers do.
  • Reusable IDs. A prompt that was not admitted no longer reserves its clientMessageId, so Paseo's interrupt-and-replace fallback can resend it.
  • Decline answers. PresentedInteraction.decline is the first deny option for permissions (ZCode's deny, listed before workflowRefine) and decline for questions and Plan approvals. cancel is not used.
  • ADR 22 records the decision and amends ADR 9. README and docs/verification.md are updated.

ZCode behavior at 3c5b227: a guide sendText is accepted while an interaction is pending and is read only after every tool call of the model step settles. Declining a permission or question keeps the turn running, so the message is read in the same turn. Declining a Plan approval in Plan mode ends ZCode's turn (stopTurnAfterResult); the message falls back to the queue and starts the next native turn in the same Paseo run. resolveInteraction acknowledges even when the interaction is already gone. ZCode Desktop hides its composer while an interaction is pending, so it never takes this path.

Verification (Linux x64, Node.js 26.8.1)

  • Regression tests. The new provider tests failed before the fix: the flagged steer returned failed, and resending its ID threw Duplicate ZCode clientMessageId. With the decline call removed, pending permissions were not declined.
  • Static checks. npm run typecheck, npm test (22 files, 209 tests), npm run build, npm run format:check and git diff --check passed.
  • Upstream. npm run test:upstream against Paseo 0.12.0 (cafe4b01c8522ff39560241c5deb01427dc8dc5b) passed. It now steers through Paseo's real adapter with clearPendingPermissions: true. Locally, NODE_PATH resolves a global zod and breaks the Git preparation check, so it ran without NODE_PATH.
  • adrs doctor reports no new diagnostics.

Not verified

  • Steering a turn blocked on a permission with a real model and runtime.
  • The fix in the Paseo app and daemon.

Paseo marks every message typed in the app during a run with
clearPendingPermissions. The provider failed such steers, so Paseo
interrupted the turn and resent the prompt with the same clientMessageId,
which the provider then rejected as a duplicate.

Send these steers as native guidance and, once ZCode accepts them,
decline pending permissions, questions and Plan approvals with their deny
action until ZCode reads the message. Allow the clientMessageId of an
input that was not admitted to be reused. Record the decision in ADR 22.
@supermomonga
supermomonga merged commit 9d25f8e into main Oct 23, 2026
7 checks passed
@supermomonga
supermomonga deleted the fix/zcode-steer-session-error branch October 23, 2026 23:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant