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:
@@ -51,25 +51,6 @@ export function findLastMessageTurnEnd(
|
||||
return latest
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the log currently sits inside an open turn (a `turn/start` not yet
|
||||
* closed by a `turn/end`). The turn is the durable log's commit/replay
|
||||
* boundary: a bare event appended between turns is indistinguishable from a
|
||||
* crash tail and silently dropped on reload, so writers of turn-enclosed
|
||||
* events (approval audit pairs, permission/sandbox knob switches) gate on
|
||||
* this fold and hold idle writes until the next turn opens.
|
||||
* @param events - session events, or an owned suffix, to inspect.
|
||||
* @returns true when the last turn boundary event is a `turn/start`.
|
||||
*/
|
||||
export function hasOpenTurn(events: readonly SessionEvent[]): boolean {
|
||||
for (let index = events.length - 1; index >= 0; index -= 1) {
|
||||
const type = (events[index] as SessionEvent).type
|
||||
if (type === 'turn/start') return true
|
||||
if (type === 'turn/end') return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
declare module 'cordis' {
|
||||
interface Context {
|
||||
sessions: SessionStore
|
||||
|
||||
@@ -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' } } })
|
||||
|
||||
Reference in New Issue
Block a user