refactor(web): drop the permission RPC pair and turn-anchoring machinery

The session.permissions/setPermission unary pair, the PermissionOption wire
DTO, the client Session wrappers, and the fixture/fake mirrors all leave the
wire: the read side moves to the 'permissions' session projection and the
write side moves to the /permission command in follow-up commits, so the
web protocol gains no permission methods at all.

The pendingSwitches + prompt-submit flush + hasOpenTurn move also goes.
Knob events no longer need turn enclosure: the persistence scanner keeps
standalone events after the last turn/end as part of the preserved prefix
(remove-synthetic-log-only-turns), none of the three knob invariants demand
an open turn, and the setters append bare events. An idle switch commits
immediately; hasOpenTurn stays a user-approval private fold (its audit pair
is the one contract that still requires enclosure).

The old PermissionSelect chip and its mount-time fetch die with the RPCs
(the resident composer broke the mount-once assumption); the projection-fed
replacement lands with the Access seat swap.
This commit is contained in:
imccyu
2026-07-28 21:35:26 +08:00
parent a66d1e335f
commit c6b552e817
36 changed files with 37 additions and 561 deletions

View File

@@ -3,7 +3,6 @@ import { Context } from 'cordis'
import { createUserMessage, CallId, createMessage, createToolResultMessage, MessageId, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
import SessionStore, {
findLastMessageTurnEnd,
hasOpenTurn,
SESSION_FORMAT_VERSION,
Session,
SessionEvent,
@@ -108,17 +107,6 @@ describe('Session', () => {
expect(findLastMessageTurnEnd(session.events)).toBe(messageEnd)
})
it('reports an open turn only between turn/start and its turn/end', () => {
const session = new Session(SessionId('open-turn'))
expect(hasOpenTurn(session.events)).toBe(false)
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
expect(hasOpenTurn(session.events)).toBe(true)
session.append('user/message', { content: [{ type: 'text', text: 'x' }], source: { kind: 'user' } }, { surfaceOp: 'append' })
expect(hasOpenTurn(session.events)).toBe(true)
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
expect(hasOpenTurn(session.events)).toBe(false)
})
it('round-trips the coarse aborted turn outcome', () => {
const session = new Session(SessionId('aborted'))
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })