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

@@ -145,21 +145,6 @@ export interface SessionSummary {
cwd?: string
}
/**
* One selectable permission preset (or the derived `custom` state) as the
* client renders it. Protocol-owned DTO (the ACP bridge precedent: each
* protocol owns its presentation shape); the host projects it from
* `ctx.permission` without exposing that service's types on the wire.
*/
export interface PermissionOption {
/** The machine value (`session.setPermission` vocabulary): a preset table key, or `custom`. */
value: string
/** The display label. */
name: string
/** One user-facing sentence on what the value means. */
description?: string
}
/** Session-domain unary methods (the map keys session.* of RpcMethodMap). */
export interface SessionsApi {
/** Lists persisted sessions (updatedAt descending). v1 returns everything; cursor is a reserved seat, unimplemented. */
@@ -216,23 +201,4 @@ export interface SessionsApi {
/** Stops: clears both FIFOs + aborts the current step (1:1 with agent.cancel). */
cancel(request: RpcRequest<{ sessionId: SessionId }>): Promise<RpcResponse<{ accepted: true }>>
/**
* Reads the session's permission select: every switchable preset plus the
* effective current value (`custom` when the knobs match no preset — shown,
* never a switch target). A host composed without the permission service
* returns empty options and `custom`; clients hide the control.
*/
permissions(request: RpcRequest<{ sessionId: SessionId }>):
Promise<RpcResponse<{ options: PermissionOption[]; currentValue: string }>>
/**
* Switches the session's permission preset. Mirrors the ACP bridge's
* turn-anchoring: inside an open turn the knob events append immediately;
* idle switches are held last-write-wins and flushed into the next prompted
* turn (approval-policy and sandbox-mode events must stay turn-enclosed for
* durable replay). A current-value echo is acknowledged without recording.
* Unknown values and a permission-less composition are bad-request.
*/
setPermission(request: RpcRequest<{ sessionId: SessionId; value: string }>):
Promise<RpcResponse<{ currentValue: string }>>
}