fix: restore forceStrictSideEffects for non-guardian/unverified channel actors#9174
fix: restore forceStrictSideEffects for non-guardian/unverified channel actors#9174awlevin wants to merge 1 commit into
Conversation
…el actors The migration from runs-store to pending-interactions dropped the forceStrictSideEffects=true override for non-guardian and unverified_channel actors. Without this, these actors could execute side-effect tools without guaranteed guardian approval prompts. This fix adds the override in prepareSessionForMessage, mirroring the pattern already used in voice-session-bridge.ts — when the guardian context indicates a non-guardian or unverified_channel actor, the session's memoryPolicy.strictSideEffects is set to true so all side-effect tools require approval. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| if (actorRole === 'non-guardian' || actorRole === 'unverified_channel') { | ||
| session.memoryPolicy = { | ||
| ...session.memoryPolicy, | ||
| strictSideEffects: true, | ||
| }; | ||
| } |
There was a problem hiding this comment.
🔴 strictSideEffects is never reset for guardian actors on a reused session
When a non-guardian or unverified_channel actor sends a message, strictSideEffects is set to true on the session's memoryPolicy. However, when a subsequent message arrives from a guardian actor (or with no actorRole), the code does not reset strictSideEffects back to its derived default. Since sessions are reused across messages for the same conversationId, the true value sticks.
Root cause and comparison with voice-session-bridge
The voice-session-bridge at assistant/src/calls/voice-session-bridge.ts:293-298 always computes and sets strictSideEffects for every turn, regardless of actor role:
const strictSideEffects = forceStrictSideEffects
?? deps.deriveDefaultStrictSideEffects(opts.conversationId);
session.memoryPolicy = {
...session.memoryPolicy,
strictSideEffects,
};When isGuardian is true, forceStrictSideEffects is undefined, so it falls back to the derived default (typically false for standard threads). This ensures strictSideEffects is properly reset for guardian actors.
In contrast, the new code in server.ts:774-779 only sets strictSideEffects conditionally:
if (actorRole === 'non-guardian' || actorRole === 'unverified_channel') {
session.memoryPolicy = {
...session.memoryPolicy,
strictSideEffects: true,
};
}There is no else branch to reset strictSideEffects to false (or its derived default). So if a non-guardian message is followed by a guardian message on the same session, the guardian will still have strictSideEffects: true, causing unnecessary approval prompts for side-effect tools.
Impact: Guardian actors on a session that previously received a non-guardian message will be incorrectly forced through approval prompts for every side-effect tool invocation.
| if (actorRole === 'non-guardian' || actorRole === 'unverified_channel') { | |
| session.memoryPolicy = { | |
| ...session.memoryPolicy, | |
| strictSideEffects: true, | |
| }; | |
| } | |
| if (actorRole === 'non-guardian' || actorRole === 'unverified_channel') { | |
| session.memoryPolicy = { | |
| ...session.memoryPolicy, | |
| strictSideEffects: true, | |
| }; | |
| } else { | |
| session.memoryPolicy = { | |
| ...session.memoryPolicy, | |
| strictSideEffects: this.deriveMemoryPolicy(conversationId).strictSideEffects, | |
| }; | |
| } |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1ae126a52f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (actorRole === 'non-guardian' || actorRole === 'unverified_channel') { | ||
| session.memoryPolicy = { | ||
| ...session.memoryPolicy, | ||
| strictSideEffects: true, | ||
| }; |
There was a problem hiding this comment.
Recompute strict side-effects for every actor role change
This branch only ever flips strictSideEffects to true, but never restores it when later turns come from a trusted actor. Because getOrCreateSession reuses the same Session object per conversation, once a non-guardian/unverified turn hits this path, the strict flag remains sticky for subsequent guardian turns in that conversation, forcing side-effect approvals unexpectedly and potentially blocking normal guardian actions.
Useful? React with 👍 / 👎.
|
Shelving project — closing all open PRs |
Summary
forceStrictSideEffects=trueoverride fornon-guardianandunverified_channelactors inprepareSessionForMessage(daemon server)voice-session-bridge.tswhere strict side-effects are forced for non-guardian actorsTest plan
daemon-server-session-inittests passsession-tool-setup-side-effect-flagtests passvoice-session-bridgetests pass (16/16)🤖 Generated with Claude Code