Skip to content

Carry an approval's persistence choice through elicitation - #1976

Open
SunkenInTime wants to merge 9 commits into
UsefulSoftwareCo:mainfrom
SunkenInTime:elicitation-persist-choice
Open

Carry an approval's persistence choice through elicitation#1976
SunkenInTime wants to merge 9 commits into
UsefulSoftwareCo:mainfrom
SunkenInTime:elicitation-persist-choice

Conversation

@SunkenInTime

Copy link
Copy Markdown

Fixes #1962.

What was happening

Using Computer Use through Executor, every read, click, or keystroke asked "Allow Computer Use to use App?" again — including repeated operations inside one execution. Accepting worked, but nothing was remembered.

Codex leaves the lifetime of that approval to the answer. The prompt's _meta carries persist: ["session", "always"], and the runtime remembers the app only when the reply's _meta.persist names one of those scopes. A reply with action and content alone is a one-time approval, so the next call prompts again.

I verified this against the installed codex app-server (0.153.4) over a direct JSON-RPC session, outside Executor: four mcpServer/tool/calls reading the same app on one thread. Accepting with {"action":"accept","content":{}} prompted on every call. Accepting once with _meta: {"persist": "always"} prompted once, wrote the app to ComputerUseAppApprovals.json, and every later call ran silently — including through Executor afterwards.

Where the choice was lost

  • approvalTerms kept only string-valued terms, so the offered ["session", "always"] never reached the host (packages/plugins/mcp/src/sdk/invoke.ts).
  • ElicitationResponse had no field an answer could carry terms in (packages/core/sdk/src/elicitation.ts).
  • The MCP plugin's elicitation/create handler, the app-server bridge's #completeElicitation, and the MCP host's native-mode handler all rebuilt the reply from action and content (invoke.ts, appserver-connector.ts, packages/hosts/mcp/src/tool-server.ts).
  • The model-mode resume tool and the HTTP resume API had no way to say "always".

What this changes

@executor-js/sdkElicitationResponse gains meta: ElicitationResponseMeta, a closed struct whose only field is persist. That mirrors the request-side projection: an answer can state exactly the terms the contract names, so no host can grant something a prompt never offered. offeredPersistence(request.meta) reads the scopes a prompt offers (a string list), and is the one helper the engine and the browser page share.

@executor-js/plugin-mcpapprovalTerms keeps string lists, so the offered scopes reach the host alongside the stated ones (Chrome's per-site persist: "always" still passes as before; objects and mixed lists still don't). The elicitation/create reply carries _meta: { persist } when an accept chose one, and only then. The app-server bridge forwards the reply's _meta down to Codex.

@executor-js/executionResumeResponse carries meta. The paused-execution output tells the model what a bare accept means and how to say otherwise, in the same instructions it already reads:

To have an accepted approval remembered, also pass persist as one of "session", "always"; without it the approval is for this call only.

@executor-js/host-mcp — native mode sends the request's terms to the client as _meta and reads persist back from the client's _meta. The model-mode resume tool takes an optional persist. (execute-action-resume, the shell modal's channel, is unchanged; the shell UI offers no such choice yet.)

@executor-js/api / @executor-js/reactPOST /executions/:id/resume accepts persist. The browser approval page shows a "Remember this approval" select when a pause offers scopes, defaulting to "Just this once".

Nothing is chosen automatically. Without an explicit choice from the model or the user, an accept is one-time, exactly as today.

Tests

  • approvalTerms keeps string lists and still drops objects and mixed lists.
  • App-server bridge: the fixture gains a Computer-Use-shaped app prompt; the client sees the offered scopes and the chosen one reaches the (fake) app-server's reply.
  • MCP plugin end-to-end: a real MCP server tool (remembered_echo) that offers scopes; the host's meta.persist reaches the server, and a bare accept stays one-time.
  • Engine: the pause output names the offered scopes in interaction.instructions and in the text.
  • Host: native mode round-trips _meta both ways and invents nothing when the client states nothing; model mode passes the resume tool's persist to the engine, and omits meta when none was given.

bun run lint, format:check, and typecheck pass for the touched packages, as do the plugin-mcp, execution, and host-mcp suites.

The browser page change is small and has no e2e scenario yet; happy to add one, or split that part out, whichever you prefer.

mikemikimike and others added 2 commits September 6, 2026 16:49
Codex Computer Use offers `persist: ["session", "always"]` in the terms of
its "Allow Computer Use to use X?" prompt and remembers the app only when
the answer names one. Executor lost the offer on the way in — the terms
projection kept strings only — and the choice on the way out, because every
adapter rebuilt the reply from `action` and `content`. So each accept was a
one-time approval and the same app prompted on every call.

- `ElicitationResponse.meta.persist` carries the choice; the vocabulary is
  closed so no host can grant more than the prompt offered.
- `approvalTerms` keeps string lists, so the offered scopes reach the host.
- The MCP plugin, the app-server bridge, and the MCP host (native mode)
  pass `_meta` through in both directions.
- The model-mode `resume` tool takes `persist`; the pause output names the
  offered scopes and says a bare accept is one-time.
- The HTTP resume API takes `persist`, and the browser approval page offers
  the scopes in a select. Nothing is chosen automatically.

Fixes UsefulSoftwareCo#1962
@SunkenInTime
SunkenInTime force-pushed the elicitation-persist-choice branch from bcf29c8 to 43edcee Compare September 11, 2026 19:24
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.

Computer Use approval bridge drops persistence choices and response metadata, causing repeated prompts

3 participants