Repository navigation
secret_request: masked secret card, value only in the run env (#1531) - #1542
Conversation
…store (#1531) Agents ask for a named secret; the user types it into a masked card. The value lives in main-process memory (electron/threadSecrets.js) and rides the env of the thread's next provider spawns. The store, transcript, MCP result and the next turn only ever see the name. Cleared on archive/settle/delete and on app restart. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ard (#1531) runAgent (generic provider) now forwards opts.env so the secret reaches it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e38e297eca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // #1531: a secret request value lives until archive / settle / delete. | ||
| threadSecrets.clear(threadId); |
There was a problem hiding this comment.
Clear secrets from the MCP retirement paths
This cleanup only runs through ipc-shared.retireAgent; the thread_archive and thread_settle handlers in electron/orchServer.js call their own file-local retireAgent, which never clears threadSecrets. Consequently, when an agent archives or settles a thread through the MCP tools, its previously provided secrets remain in memory and are injected again if the thread is reactivated, despite the UI promising that they are gone after archive. Route those handlers through the shared cleanup or explicitly clear the secret map there.
Useful? React with 👍 / 👎.
| pendingSecret: { | ||
| id: randomUUID(), | ||
| name, | ||
| prompt: truncate(String((input && input.prompt) || ""), 500), | ||
| askedAt: Date.now(), | ||
| }, | ||
| awaitingInput: true, |
There was a problem hiding this comment.
Suppress plan approval while a secret is pending
When a thread in plan mode calls secret_request and has emitted assistant text, notifyRunTerminal subsequently calls maybePersistPlanApproval, which skips persisted questions but not this new pendingSecret. It therefore creates a pendingPlan beside the secret card; after the user provides the secret, answerSecret queues the $NAME notification, but maybeDrainQueued refuses to run it while pendingPlan exists. In this context, Provide does not start the promised next turn until the user also resolves an unrelated plan approval, so maybePersistPlanApproval should treat pendingSecret like pendingQuestion.
Useful? React with 👍 / 👎.
…ope table (#1553) #1545 added a coverage test that every IPC handler is classified exactly once; #1542 (answerSecret) and #1551 (setFast) landed alongside it unclassified, so main's electron suite fails. setFast is steer like setReasoningEffort; answerSecret puts a value into the agent's run env, so it stays full-only. Co-authored-by: Willem <willem@willems-mbp.home> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ope table #1545 added a coverage test that every IPC handler is classified exactly once; #1542 (answerSecret) and #1551 (setFast) landed alongside it unclassified, so main's electron suite fails. setFast is steer like setReasoningEffort; answerSecret puts a value into the agent's run env, so it stays full-only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1545 added the scope table while #1551 (setFast) and #1542 (answerSecret) added channels in parallel, so main's scope-table test fails. setFast is steer, like setReasoningEffort. answerSecret is full: a device has no need to plant a secret in a run's env. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t/answerSecret web scopes #1435: bill a warm Claude process's cost growth, not its running total. #1436: worker-finished notice tells the orchestrator not to poll thread_status; saved notices with the old footer still fold. #1439: build devCoder on first use so importers don't stay alive ~4s. webScopes: threads:setFast (steer) and threads:answerSecret (full) were unclassified after #1545 landed alongside #1551 and #1542, failing main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part of #1531 (overlaps #289).
New coder-threads MCP tool,
secret_request({threadId, projectId, name, prompt?}), wherenamemust match^[A-Z_][A-Z0-9_]*$. Likeask_user, it returns immediately. The thread then shows a masked Secret requested: NAME card with Provide and Decline.Where the value goes
electron/threadSecrets.js), per thread. It never goes intocoder-store.json, the transcript, the MCP tool result, logs, or a detail broadcast. OnlypendingSecret {id, name, prompt}is stored.$NAMEin the env of the thread's next provider spawns: Claude, Codex app-server, Cursor, Kimi, Muse, OpenCode and generic. For Claude it is part of the warm-process reuse key, so a new secret restarts the warm CLI. That key is only compared in memory and is never logged.Secret NAME provided, and the next turn starts with "Secret NAME is now available as $NAME".retireAgent).Known limit: if the agent prints
$NAME(for exampleecho $NAME), the value lands in the tool output and the transcript. This PR does not scrub values out of tool output.Verification
electron/test/secret-request.test.jschecks the value is absent from the store file, the transcript and the tool result, and present in the spawned run env. There is alsotest/secretPrompt.test.tsx.mermaidEngine.tserror that is already on main.🤖 Generated with Claude Code