Skip to content

Commit f527431

Browse files
committed
fix(session): also persist approve-tool grants synchronously
The Persist-on-resume switch symmetry: approve-safe / approve-safer / approve-session mutate session state and are now persisted here. approve-tool mutates session.Permissions.Allow with the same lifetime characteristics, and needs the same treatment or its in-turn grant is silently dropped by the next OnRunStart cycle (or a mid-turn process restart). Skip the persist when toolName is empty — the dispatcher's fallback to the pending tool call's name isn't reachable from here, so the mutation happens in-runtime as before but doesn't get the durable write.
1 parent 458b780 commit f527431

1 file changed

Lines changed: 11 additions & 8 deletions

File tree

pkg/server/session_manager.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -919,11 +919,8 @@ func (sm *SessionManager) ResumeSession(ctx context.Context, sessionID, confirma
919919
return errors.New("session not found")
920920
}
921921

922-
// approve-safe / approve-safer / approve-session mutate the session
923-
// mid-turn from the dispatcher, but PersistenceObserver only persists
924-
// on OnRunStart — the mutation would otherwise not survive to the
925-
// next turn. Apply and persist synchronously here so the state is
926-
// durable regardless of when the dispatcher processes the resume.
922+
// Mirror + persist mid-turn session mutations synchronously —
923+
// PersistenceObserver only persists on OnRunStart.
927924
if rt.session != nil {
928925
mutated := false
929926
switch runtime.ResumeType(confirmation) {
@@ -936,6 +933,13 @@ func (sm *SessionManager) ResumeSession(ctx context.Context, sessionID, confirma
936933
case runtime.ResumeTypeApproveSession:
937934
rt.session.SetToolsApproved(true)
938935
mutated = true
936+
case runtime.ResumeTypeApproveTool:
937+
// Skip when toolName is empty — the dispatcher's own
938+
// fallback (pending tool call name) isn't reachable here.
939+
if toolName != "" {
940+
rt.session.AppendPermissionAllow(toolName)
941+
mutated = true
942+
}
939943
}
940944
if mutated {
941945
if err := sm.sessionStore.UpdateSession(ctx, rt.session); err != nil {
@@ -1156,9 +1160,8 @@ func (sm *SessionManager) SetSessionSafetyPolicy(ctx context.Context, sessionID
11561160
sm.mux.Lock()
11571161
defer sm.mux.Unlock()
11581162

1159-
// Mirror onto the live runtime session (if any) so the running
1160-
// dispatcher picks up the new policy on the very next tool call,
1161-
// then persist so it survives to the next turn.
1163+
// Mirror onto the live runtime session so the dispatcher picks up
1164+
// the new policy on the next tool call, not just the next turn.
11621165
if rt, ok := sm.runtimeSessions.Load(sessionID); ok && rt.session != nil {
11631166
rt.session.SetSafetyPolicy(policy)
11641167
return sm.sessionStore.UpdateSession(ctx, rt.session)

0 commit comments

Comments
 (0)