/** * The control vocabulary, shared by the core and every host. * * Moved here from the Pi host during structural normalization (A1): the core's * driver and store bind a pending action to this action union, and shared layers * must not import a host. `hosts/pi/control.ts` re-exports everything here, so the * Pi-facing module surface is unchanged. */ export const CONTROL_REGISTRY = [ { action: "handoff", zh: "交接", en: "handoff", payload: "optional", risk: "none", requiresConfirmation: false }, { action: "execute", zh: "执行", en: "exec", payload: "forbidden", risk: "write", requiresConfirmation: true }, { action: "round", zh: "一轮", en: "round", payload: "optional", risk: "write", requiresConfirmation: true }, { action: "status", zh: "状态", en: "status", payload: "forbidden", risk: "read", requiresConfirmation: false }, { action: "new", zh: "新会话", en: "new", // Deliberately forbidden even though a reason would be nice to record: only // `handoff` and `round` take free text, and that is a documented, tested // property of the vocabulary. A third exception widens the surface where a // sentence like "brb 新会话 是什么意思" stops being a question and becomes a // command, and the audit trail can carry the timestamp and thread instead. payload: "forbidden", risk: "write", requiresConfirmation: false, }, { action: "conversations", zh: "会话", en: "conversations", payload: "forbidden", risk: "read", requiresConfirmation: false, }, { action: "report", zh: "回执", en: "report", payload: "forbidden", risk: "none", requiresConfirmation: false }, { action: "stop", zh: "停轮", en: "stop", payload: "forbidden", risk: "read", requiresConfirmation: false }, { action: "confirm", zh: "确认", en: "confirm", payload: "forbidden", risk: "write", requiresConfirmation: false }, { action: "cancel", zh: "取消", en: "cancel", payload: "forbidden", risk: "read", requiresConfirmation: false }, ] as const; /** * The actions the registry actually defines. * * `ControlSpec.action` is widened to `string` above so the registry can exist before * the type; this is what re-narrows it, so every consumer still sees the union. */ export type ControlAction = (typeof CONTROL_REGISTRY)[number]["action"]; /** One reserved command, narrowed to the actions the registry actually defines. */ export interface ControlSpec { action: ControlAction; zh: string; en: string; payload: "optional" | "forbidden"; risk: "none" | "read" | "write"; requiresConfirmation: boolean; } /** * A pending high-risk action. * * Bound to the plan, the round, the repository and -- since payloads exist -- the * request itself. A bare "confirmed = true" would let a `brb 确认` typed after * the repository moved, or after the user changed the request, execute something * other than what was on screen. */ export interface PendingAction { action: ControlAction; sid: string; round: number; planKey: string; /** qid of the request this confirmation applies to. Empty when there is none. */ qid: string; /** git HEAD at the moment the pending action was created. */ head: string; createdAt: number; }