@workerdeck/protocol 2.7.1 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/build/index.mjs.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @workerdeck/protocol
|
|
2
2
|
|
|
3
3
|
The WorkerDeck wire protocol: typed session events, commands, and REST shapes shared by the
|
|
4
|
-
server and every client. Dependency-free, browser-safe. This protocol is the product boundary
|
|
4
|
+
server and every client. Dependency-free, browser-safe. This protocol is the product boundary -
|
|
5
5
|
versioned from day one.
|
|
6
6
|
|
|
7
7
|
Part of [WorkerDeck](https://github.com/workerdeck/workerdeck), the web-controlled
|
|
@@ -40,7 +40,7 @@ ws.onmessage = ({ data }) => {
|
|
|
40
40
|
throw new Error('protocol mismatch')
|
|
41
41
|
}
|
|
42
42
|
if (frame.type === 'event' && frame.event.type === 'assistant_message') {
|
|
43
|
-
render(frame.event.message) // ApiMessage
|
|
43
|
+
render(frame.event.message) // ApiMessage - plain Anthropic content blocks
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -53,7 +53,7 @@ server reports it in the `attached` (and `queue_attached`) frame so clients can
|
|
|
53
53
|
|
|
54
54
|
## At a glance
|
|
55
55
|
|
|
56
|
-
**Events (server → client)**
|
|
56
|
+
**Events (server → client)** - `system_init`, `status_changed`, `capabilities`, `model_changed`,
|
|
57
57
|
`permission_mode_changed`, `context_usage`, `rate_limit`, `assistant_message`, `user_message`,
|
|
58
58
|
`stream_delta`, `turn_result`, `tool_titles` (human-readable labels for tool calls the client
|
|
59
59
|
cannot name on its own), `permission_requested`, `permission_resolved`,
|
|
@@ -61,27 +61,27 @@ cannot name on its own), `permission_requested`, `permission_resolved`,
|
|
|
61
61
|
correlated by `executionId`), `file_delivered`, `sdk_event` (forward-compatible passthrough for
|
|
62
62
|
unmodeled SDK messages), `session_error`, `session_closed`.
|
|
63
63
|
|
|
64
|
-
**Commands (client → server)**
|
|
64
|
+
**Commands (client → server)** - `user_message`, `permission_decision`, `interrupt`,
|
|
65
65
|
`set_permission_mode`, `set_model`, `tool_call_result` (answering a bridged execution), `close`.
|
|
66
66
|
|
|
67
|
-
**Other server frames**
|
|
67
|
+
**Other server frames** - `attached`, `event`, `tool_call_request` / `tool_call_canceled` (the
|
|
68
68
|
browser tool bridge: the server asks an attached client to run a *sandboxed* tool call), and
|
|
69
69
|
`protocol_error`.
|
|
70
70
|
|
|
71
|
-
**REST shapes**
|
|
71
|
+
**REST shapes** - `CreateSessionRequest` / `SessionInfo` and their response wrappers,
|
|
72
72
|
`ResolvePermissionRequest` (the REST counterpart of `permission_decision`),
|
|
73
73
|
`SdkSessionSummary` for listing the Agent SDK's on-disk sessions to offer resume,
|
|
74
74
|
`ProfileInfo` for what a session may run as, `ListSessionFilesResponse` for a session's
|
|
75
75
|
deliverables, and `SubmitExecutionResultRequest` for delivering a deferred execution's result.
|
|
76
76
|
|
|
77
|
-
**Job queue**
|
|
77
|
+
**Job queue** - `CreateJobRequest` / `JobInfo` / `JobEvent` (including `job_parked` /
|
|
78
78
|
`job_resumed`) / `QueueStats` and the `QueueServerFrame` union for the one-way queue WebSocket,
|
|
79
79
|
used when the server mounts the
|
|
80
80
|
[`@workerdeck/queue`](https://www.npmjs.com/package/@workerdeck/queue) routes.
|
|
81
81
|
|
|
82
82
|
Two engines ride this one protocol: `SessionInfo.engine` says which (`claude` or `provider`), and
|
|
83
|
-
`supportsPermissionMode(engine, mode)`
|
|
84
|
-
restriction
|
|
83
|
+
`supportsPermissionMode(engine, mode)` - a real runtime export, the single source of truth for the
|
|
84
|
+
restriction - is what create forms filter with and the gateway rejects with.
|
|
85
85
|
|
|
86
86
|
Forward compatibility is deliberate: unknown content blocks fall back to `UnknownBlock`, unions
|
|
87
87
|
the SDK may grow (`apiKeySource`, rate-limit fields) stay `string`, and unmodeled SDK messages
|
|
@@ -94,23 +94,23 @@ ride through as `sdk_event` rather than breaking older clients.
|
|
|
94
94
|
- **This package owns rules, not just shapes.** `transcriptActivity` is the row-count both the
|
|
95
95
|
reducer renders by and the runners report as `activityCount`; `transcriptContent` is the
|
|
96
96
|
does-it-mutate-items rule behind `conversation_reset`'s replay (the runner skips content below
|
|
97
|
-
the latest reset, and it must skip exactly what the reducer would have cleared
|
|
97
|
+
the latest reset, and it must skip exactly what the reducer would have cleared - note it is
|
|
98
98
|
broader than `transcriptActivity() > 0`: deltas and tool results count zero rows and still
|
|
99
99
|
mutate items); `session-list.ts` is the sessions list view model; `watermarks.ts` is the unread
|
|
100
100
|
model. They live here because a client that filtered or counted differently would announce work
|
|
101
|
-
it is hiding. Change one, change every consumer
|
|
101
|
+
it is hiding. Change one, change every consumer - including the Swift mirror in
|
|
102
102
|
`apps/ios/WorkerDeckKit`.
|
|
103
103
|
- **`activityCount` is monotonic across a `conversation_reset`.** It is an unread *cursor*
|
|
104
|
-
diffed against stored monotonic watermarks, not an item count
|
|
104
|
+
diffed against stored monotonic watermarks, not an item count - resetting it to the fresh row
|
|
105
105
|
count would leave every stored mark above it and that session's badge silently dead. After a
|
|
106
106
|
`/clear` it deliberately exceeds the rendered row count.
|
|
107
107
|
- **`ENGINE_CAPABILITIES` is pinned by identity, and is a *fallback*.** A server that reports its
|
|
108
108
|
own record wins; this table is what a client uses when talking to one that doesn't. Editing a
|
|
109
109
|
value here is a cross-client change, not a local one.
|
|
110
110
|
- **Capability records describe the engine, not the deployment.** A `true` means the engine can do
|
|
111
|
-
it, never that this session is allowed to
|
|
111
|
+
it, never that this session is allowed to - grants and policy live on the profile and the request.
|
|
112
112
|
|
|
113
113
|
## License
|
|
114
114
|
|
|
115
|
-
MIT © Tobias Strebitzer
|
|
115
|
+
MIT © Tobias Strebitzer -
|
|
116
116
|
[LICENSE](https://github.com/workerdeck/workerdeck/blob/master/LICENSE)
|
package/build/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/session-list.ts","../src/checklist.ts","../src/tool-titles.ts","../src/usage.ts","../src/watermarks.ts","../src/index.ts"],"sourcesContent":["import type { SessionInfo, SubagentInfo } from './index.ts'\n\nexport type SessionState = 'attention' | 'working' | 'idle' | 'ended'\n\nexport const STATE_ORDER: readonly SessionState[] = ['attention', 'working', 'idle', 'ended']\n\nexport const STATE_LABELS: Record<SessionState, string> = {\n attention: 'Needs attention',\n working: 'Working',\n idle: 'Idle',\n ended: 'Ended',\n}\n\nexport function sessionState(info: SessionInfo): SessionState {\n if (info.pendingPermissionCount > 0 || info.status === 'awaiting_approval') {\n return 'attention'\n }\n if (info.status === 'failed' || info.status === 'closed') {\n return 'ended'\n }\n if (info.status === 'running' || info.status === 'starting') {\n return 'working'\n }\n if (runningSubagents(info).length > 0) {\n return 'working'\n }\n return 'idle'\n}\n\nexport function runningSubagents(info: SessionInfo): SubagentInfo[] {\n return (info.subagents ?? []).filter((sub) => sub.status === 'running')\n}\n\nexport function isAgentRecord(sub: SubagentInfo): boolean {\n return (sub.agentType?.trim() ?? '') !== ''\n}\n\nexport function subagentLabel(sub: SubagentInfo): string {\n const agent = sub.agentType?.trim()\n const description = sub.description?.trim()\n if (agent && description) {\n return `${agent} · ${description}`\n }\n return agent || description || 'Sub-agent'\n}\n\nexport type Facet = 'gateway' | 'adapter' | 'state' | 'project'\nexport type GroupBy = 'none' | Facet\nexport type SortBy = 'recent' | 'name' | Facet\n\nexport type ViewConfig = {\n search: string\n gateways: string[]\n adapters: string[]\n states: SessionState[]\n projects?: string[]\n scoped: boolean\n groupBy: GroupBy\n sortBy: SortBy\n}\n\nexport const DEFAULT_VIEW_CONFIG: ViewConfig = {\n search: '',\n gateways: [],\n adapters: [],\n states: [],\n projects: [],\n scoped: true,\n groupBy: 'state',\n sortBy: 'recent',\n}\n\nexport type ScopeRoot = { hostId?: string; path: string }\n\nexport type WorkspaceScope = { label: string; roots: ScopeRoot[] }\n\nexport type SessionRow = {\n hostId: string\n hostName: string\n local: boolean\n adapter: string\n state: SessionState\n info: SessionInfo\n // Messages this client has not read — `unseenCount` against its watermark, so the\n // unit is prose (`SessionInfo.proseCount`) wherever the gateway reports it. A session\n // that is only running tools contributes 0: the badge answers \"is there something to\n // read\", not \"is anything happening\", which is what `state` is for.\n unseen: number\n}\n\nexport type SessionGroup = { key: string; label?: string; rows: SessionRow[] }\n\nexport function adaptersOf(rows: readonly SessionRow[]): string[] {\n return [...new Set(rows.map((r) => r.adapter))].sort()\n}\n\nexport function projectsOf(rows: readonly SessionRow[]): { key: string; label: string }[] {\n const byKey = new Map<string, string>()\n for (const row of rows) {\n byKey.set(projectKey(row), projectLabel(row))\n }\n return [...byKey].map(([key, label]) => ({ key, label })).sort((a, b) => a.label.toLowerCase().localeCompare(b.label.toLowerCase()))\n}\n\nexport function sessionLabel(info: SessionInfo): string {\n return info.title ?? info.id.slice(0, 8)\n}\n\nexport function projectKey(row: SessionRow): string {\n return `${row.hostId}:${normalizePath(row.info.project?.root ?? row.info.cwd)}`\n}\n\nexport function projectLabel(row: Pick<SessionRow, 'info'>): string {\n const name = row.info.project?.name\n if (name) {\n return name\n }\n const dir = normalizePath(row.info.cwd)\n return dir.slice(dir.lastIndexOf('/') + 1) || 'No project'\n}\n\nexport function projectSubpath(row: Pick<SessionRow, 'info'>): string | undefined {\n const root = row.info.project?.root\n if (root === undefined || !row.info.cwd) {\n return undefined\n }\n const base = normalizePath(root)\n const dir = normalizePath(row.info.cwd)\n if (dir === base) {\n return undefined\n }\n if (!dir.startsWith(`${base}/`)) {\n return undefined\n }\n return dir.slice(base.length + 1) || undefined\n}\n\nexport function isJobRun(info: SessionInfo): boolean {\n return typeof info.meta?.jobId === 'string'\n}\n\nfunction matchesSearch(row: SessionRow, needle: string): boolean {\n if (!needle) {\n return true\n }\n return (\n sessionLabel(row.info).toLowerCase().includes(needle) ||\n row.info.cwd.toLowerCase().includes(needle) ||\n (row.info.project?.name.toLowerCase().includes(needle) ?? false) ||\n row.hostName.toLowerCase().includes(needle) ||\n row.adapter.toLowerCase().includes(needle) ||\n row.info.id.startsWith(needle)\n )\n}\n\nfunction normalizePath(path: string): string {\n return path.replace(/\\\\/g, '/').replace(/\\/+$/, '')\n}\n\nfunction isWithin(root: string, path: string): boolean {\n const base = normalizePath(root)\n const dir = normalizePath(path)\n // The separator matters: /a/project must not swallow /a/project-2.\n return dir === base || dir.startsWith(`${base}/`)\n}\n\nexport function inScope(row: SessionRow, scope: WorkspaceScope): boolean {\n return scope.roots.some(\n (root) => (root.hostId ? root.hostId.toLowerCase() === row.hostId.toLowerCase() : row.local) && isWithin(root.path, row.info.cwd),\n )\n}\n\nexport function scopeActive(config: ViewConfig, scope: WorkspaceScope | undefined): boolean {\n return config.scoped && scope !== undefined\n}\n\nexport function filterRows(rows: readonly SessionRow[], config: ViewConfig, scope?: WorkspaceScope): SessionRow[] {\n const needle = config.search.trim().toLowerCase()\n const scoping = scopeActive(config, scope) ? scope : undefined\n return rows.filter(\n (row) =>\n (config.gateways.length === 0 || config.gateways.includes(row.hostId)) &&\n (config.adapters.length === 0 || config.adapters.includes(row.adapter)) &&\n (config.states.length === 0 || config.states.includes(row.state)) &&\n (!config.projects?.length || config.projects.includes(projectKey(row))) &&\n (!scoping || inScope(row, scoping)) &&\n matchesSearch(row, needle),\n )\n}\n\nfunction facetKey(row: SessionRow, facet: Facet): string {\n return facet === 'gateway' ? row.hostId : facet === 'adapter' ? row.adapter : facet === 'project' ? projectKey(row) : row.state\n}\n\nfunction facetLabel(row: SessionRow, facet: Facet): string {\n return facet === 'gateway'\n ? row.hostName\n : facet === 'adapter'\n ? row.adapter\n : facet === 'project'\n ? projectLabel(row)\n : STATE_LABELS[row.state]\n}\n\nfunction facetRank(row: SessionRow, facet: Facet): string {\n if (facet === 'state') {\n return String(STATE_ORDER.indexOf(row.state))\n }\n return facetLabel(row, facet).toLowerCase()\n}\n\nfunction byRecency(a: SessionRow, b: SessionRow) {\n return (b.info.lastActivityAt ?? b.info.createdAt) - (a.info.lastActivityAt ?? a.info.createdAt)\n}\n\nfunction compare(a: SessionRow, b: SessionRow, sortBy: SortBy): number {\n if (sortBy === 'recent') {\n return byRecency(a, b)\n }\n if (sortBy === 'name') {\n return (\n sessionLabel(a.info).localeCompare(sessionLabel(b.info), undefined, {\n sensitivity: 'base',\n }) || byRecency(a, b)\n )\n }\n return facetRank(a, sortBy).localeCompare(facetRank(b, sortBy)) || byRecency(a, b)\n}\n\nexport function groupRows(rows: readonly SessionRow[], config: ViewConfig): SessionGroup[] {\n const sorted = [...rows].sort((a, b) => compare(a, b, config.sortBy))\n if (config.groupBy === 'none') {\n return sorted.length ? [{ key: 'all', rows: sorted }] : []\n }\n const facet = config.groupBy\n const groups = new Map<string, SessionGroup & { rank: string }>()\n for (const row of sorted) {\n const key = facetKey(row, facet)\n const group = groups.get(key)\n if (group) {\n group.rows.push(row)\n } else {\n groups.set(key, {\n key,\n label: facetLabel(row, facet),\n rank: facetRank(row, facet),\n rows: [row],\n })\n }\n }\n return [...groups.values()].sort((a, b) => a.rank.localeCompare(b.rank))\n}\n\nexport type SubsetSummary = { shown: number; total: number; causes: string[] }\n\nexport function subsetSummary(\n config: ViewConfig,\n scope: WorkspaceScope | undefined,\n shown: number,\n total: number,\n): SubsetSummary | undefined {\n if (shown >= total) {\n return undefined\n }\n const causes: string[] = []\n if (scope && scopeActive(config, scope)) {\n causes.push(scope.label)\n }\n const facets =\n (config.gateways.length ? 1 : 0) + (config.adapters.length ? 1 : 0) + (config.states.length ? 1 : 0) + (config.projects?.length ? 1 : 0)\n if (facets > 0) {\n causes.push(`${facets} filter${facets === 1 ? '' : 's'}`)\n }\n if (config.search.trim()) {\n causes.push('search')\n }\n return { shown, total, causes }\n}\n\nexport function hasFacetFilter(config: ViewConfig): boolean {\n return (\n config.search.trim().length > 0 ||\n config.gateways.length > 0 ||\n config.adapters.length > 0 ||\n config.states.length > 0 ||\n (config.projects?.length ?? 0) > 0\n )\n}\n\nexport function clearFilters(config: ViewConfig): ViewConfig {\n return {\n ...DEFAULT_VIEW_CONFIG,\n scoped: false,\n groupBy: config.groupBy,\n sortBy: config.sortBy,\n }\n}\n","import type { ChecklistItem, ChecklistStatus, SessionInfo } from './index.ts'\nimport { isAgentRecord } from './session-list.ts'\n\nexport type SessionTaskState = 'pending' | 'running' | 'done' | 'failed'\n\nexport type SessionTaskSource = 'checklist' | 'spawn'\n\nexport type SessionTask = {\n key: string\n label: string\n source: SessionTaskSource\n state: SessionTaskState\n detail?: string\n toolUseId?: string\n}\n\nexport type TaskSummary = { total: number; done: number; running: number; failed: number }\n\nfunction checklistEntry(value: unknown): ChecklistItem | undefined {\n if (typeof value !== 'object' || value === null) {\n return undefined\n }\n const raw = value as { content?: unknown; status?: unknown; activeForm?: unknown }\n const status = raw.status\n if (status !== 'pending' && status !== 'in_progress' && status !== 'completed') {\n return undefined\n }\n const content = typeof raw.content === 'string' ? raw.content.trim() : ''\n if (content === '') {\n return undefined\n }\n const active = typeof raw.activeForm === 'string' ? raw.activeForm.trim() : ''\n return { status, text: status === 'in_progress' && active !== '' ? active : content }\n}\n\n// Whole-or-nothing: a malformed entry (streaming, partial input) yields undefined rather than a half-read list.\n// A well-formed empty list is a clear, and answers `[]`.\nexport function parseTodoWriteInput(input: unknown): ChecklistItem[] | undefined {\n const todos = (input as { todos?: unknown } | null)?.todos\n if (!Array.isArray(todos)) {\n return undefined\n }\n const out: ChecklistItem[] = []\n for (const value of todos) {\n const entry = checklistEntry(value)\n if (entry === undefined) {\n return undefined\n }\n out.push(entry)\n }\n return out\n}\n\nfunction checklistState(status: ChecklistStatus): SessionTaskState {\n switch (status) {\n case 'in_progress': {\n return 'running'\n }\n case 'completed': {\n return 'done'\n }\n default: {\n return 'pending'\n }\n }\n}\n\nexport function sessionTasks(info: Pick<SessionInfo, 'checklist' | 'subagents'>): SessionTask[] {\n const checklist = (info.checklist ?? []).map((item, index) => ({\n key: `checklist:${index}`,\n label: item.text,\n source: 'checklist' as const,\n state: checklistState(item.status),\n }))\n const spawns = (info.subagents ?? [])\n .filter((sub) => !isAgentRecord(sub))\n .map((sub) => ({\n key: `spawn:${sub.toolUseId}`,\n label: sub.description?.trim() || 'Task',\n source: 'spawn' as const,\n state: sub.status,\n detail: sub.toolCount > 0 ? String(sub.toolCount) : undefined,\n toolUseId: sub.toolUseId,\n }))\n return [...checklist, ...spawns]\n}\n\nexport function taskSummary(tasks: readonly SessionTask[]): TaskSummary {\n let done = 0\n let running = 0\n let failed = 0\n for (const task of tasks) {\n if (task.state === 'done') {\n done += 1\n } else if (task.state === 'running') {\n running += 1\n } else if (task.state === 'failed') {\n failed += 1\n }\n }\n return { total: tasks.length, done, running, failed }\n}\n\nexport function taskCountLabel(summary: TaskSummary): string | undefined {\n return summary.total === 0 ? undefined : `${summary.done}/${summary.total}`\n}\n\nexport function visibleTasks(tasks: readonly SessionTask[], showCompleted: boolean): SessionTask[] {\n return showCompleted ? [...tasks] : tasks.filter((task) => task.state !== 'done')\n}\n","export const TOOL_TITLE_MAX_CHARS = 64\n\n// Capability tools the sandbox grants and the synthetic names the codex adapter invents: wire\n// names with no public vocabulary behind them. An engine's own tool names (`Bash`, `Read`,\n// `Task`) are deliberately absent — those are the CLI's published names and users read them.\nexport const BUILTIN_TOOL_TITLES: Record<string, string> = {\n fs_read: 'Reading a file',\n fs_write: 'Writing a file',\n fs_list: 'Listing files',\n eval_script: 'Running a script',\n web_fetch: 'Fetching a web page',\n web_search: 'Searching the web',\n deliver_file: 'Delivering a file',\n download: 'Downloading a file',\n CodexCommand: 'Running a command',\n CodexFileChange: 'Editing a file',\n CodexWebSearch: 'Searching the web',\n CodexImageGeneration: 'Generating an image',\n CodexImageView: 'Viewing an image',\n}\n\nexport function toolTitle(name: string, titles?: Record<string, string>): string | undefined {\n return sanitizeToolTitle(titles?.[name] ?? BUILTIN_TOOL_TITLES[name], name)\n}\n\n// Titles reach here from remote MCP servers, so they are untrusted display text: one line,\n// clamped, and never a restatement of the wire name the caller already has.\nexport function sanitizeToolTitle(title: string | undefined, name?: string): string | undefined {\n if (typeof title !== 'string') {\n return undefined\n }\n const flat = [...title]\n .map((char) => (char < ' ' || char === '\\u007f' ? ' ' : char))\n .join('')\n .replace(/\\s+/g, ' ')\n .trim()\n if (flat === '' || flat === name) {\n return undefined\n }\n // By code point, not code unit: a clamp that splits a surrogate pair renders a replacement\n // character, and the clients that mirror this rule do not count in UTF-16.\n const points = [...flat]\n return points.length > TOOL_TITLE_MAX_CHARS ? points.slice(0, TOOL_TITLE_MAX_CHARS - 1).join('') + '…' : flat\n}\n","import type { ProfileUsage, RateLimitInfo } from './index.ts'\n\nexport type SessionUsage = {\n rateLimits?: Record<string, RateLimitInfo>\n updatedAt?: number\n}\n\nexport function mergeUsage(session: SessionUsage, profile: ProfileUsage | undefined): ProfileUsage {\n const out: ProfileUsage = {}\n for (const [key, info] of Object.entries(session.rateLimits ?? {})) {\n out[key] = { info, updatedAt: session.updatedAt ?? 0 }\n }\n for (const [key, window] of Object.entries(profile ?? {})) {\n out[key] = window\n }\n return out\n}\n\nexport type UsageWindowRow = {\n key: string\n info: RateLimitInfo\n updatedAt?: number\n inferredReset?: boolean\n}\n\nexport function orderUsageWindows(usage: ProfileUsage | undefined): UsageWindowRow[] {\n const all = Object.entries(usage ?? {})\n .filter(([, w]) => w.info.utilization !== undefined)\n .map(([key, w]) => ({ key, info: w.info, updatedAt: w.updatedAt, inferredReset: w.inferredReset }))\n const named = ['five_hour', 'seven_day'].flatMap((key) => all.filter((w) => w.key === key))\n const perModel = all.filter((w) => w.key.startsWith('seven_day_')).sort((a, b) => a.key.localeCompare(b.key))\n return [...named, ...perModel]\n}\n\nexport function usageInfos(usage: ProfileUsage | undefined): Record<string, RateLimitInfo> | undefined {\n if (!usage) {\n return undefined\n }\n const out: Record<string, RateLimitInfo> = {}\n for (const [key, window] of Object.entries(usage)) {\n out[key] = window.info\n }\n return out\n}\n","export type Watermark = {\n itemCount: number\n activity: number\n // Prose rows read (`SessionInfo.proseCount`). Optional because a mark stored before\n // prose counting existed cannot say — see `unseenCount`, which reads that absence as\n // \"caught up\" rather than badging a whole history the operator has already seen.\n prose?: number\n turns: number\n seenAt: number\n}\n\nexport type WatermarkStore = {\n read(): Record<string, Watermark> | undefined\n write(marks: Record<string, Watermark>): void\n}\n\nconst MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000\n\nconst TOUCH_MS = 60_000\n\nexport function watermarkKey(hostId: string, sessionId: string) {\n return `${hostId}:${sessionId}`\n}\n\nexport class Watermarks {\n readonly #store: WatermarkStore\n #cache: Record<string, Watermark>\n\n constructor(store: WatermarkStore) {\n this.#store = store\n this.#cache = { ...store.read() }\n }\n\n get(hostId: string, sessionId: string): Watermark | undefined {\n return this.#cache[watermarkKey(hostId, sessionId)]\n }\n\n all(): Readonly<Record<string, Watermark>> {\n return this.#cache\n }\n\n mark(\n hostId: string,\n sessionId: string,\n seen: { itemCount?: number; activity?: number; prose?: number; turns?: number },\n now = Date.now(),\n ): boolean {\n const id = watermarkKey(hostId, sessionId)\n const previous = this.#cache[id]\n const next: Watermark = {\n itemCount: Math.max(previous?.itemCount ?? 0, seen.itemCount ?? 0),\n activity: Math.max(previous?.activity ?? 0, seen.activity ?? 0),\n // A caller with nothing to say about prose (an older gateway reports no `proseCount`)\n // must not overwrite a real mark with 0, which would re-badge everything already read.\n prose: seen.prose === undefined ? previous?.prose : Math.max(previous?.prose ?? 0, seen.prose),\n turns: Math.max(previous?.turns ?? 0, seen.turns ?? 0),\n seenAt: now,\n }\n if (\n previous &&\n previous.itemCount === next.itemCount &&\n previous.activity === next.activity &&\n previous.prose === next.prose &&\n previous.turns === next.turns &&\n next.seenAt - previous.seenAt < TOUCH_MS\n ) {\n return false\n }\n this.#cache[id] = next\n this.#store.write(this.#prune(now))\n return true\n }\n\n forget(hostId: string, sessionId: string): void {\n const id = watermarkKey(hostId, sessionId)\n if (!(id in this.#cache)) {\n return\n }\n delete this.#cache[id]\n this.#store.write(this.#cache)\n }\n\n #prune(now: number): Record<string, Watermark> {\n const cutoff = now - MAX_AGE_MS\n for (const [id, mark] of Object.entries(this.#cache)) {\n if (mark.seenAt < cutoff) {\n delete this.#cache[id]\n }\n }\n return this.#cache\n }\n}\n\n// The badge's number, in the best unit the pair can agree on: prose the human has not\n// read, else rows, else turns. The ladder is what keeps an older gateway (no `proseCount`\n// on the wire) badging exactly as it did before rather than going silent.\nexport function unseenCount(mark: Watermark | undefined, info: { proseCount?: number; activityCount?: number; turns?: number }): number {\n if (!mark) {\n return 0\n }\n if (info.proseCount !== undefined) {\n // `mark.prose` absent = a mark written before prose counting. Reading it as\n // \"caught up\" costs one missed badge on a session already visited; reading it as 0\n // would badge every such session with its entire history the first time it polls.\n return Math.max(0, info.proseCount - (mark.prose ?? info.proseCount))\n }\n if (info.activityCount !== undefined) {\n return Math.max(0, info.activityCount - mark.activity)\n }\n return Math.max(0, (info.turns ?? 0) - mark.turns)\n}\n","export const PROTOCOL_VERSION = 1\n\nexport type SessionStatus = 'starting' | 'running' | 'awaiting_approval' | 'idle' | 'parked' | 'failed' | 'closed'\n\nexport type PermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan' | 'dontAsk' | 'auto'\n\nexport type TextBlock = { type: 'text'; text: string }\nexport type ThinkingBlock = { type: 'thinking'; thinking: string }\nexport type ToolUseBlock = { type: 'tool_use'; id: string; name: string; input: unknown }\nexport type ToolResultBlock = {\n type: 'tool_result'\n tool_use_id: string\n content?: string | Array<{ type: string; text?: string; [key: string]: unknown }>\n is_error?: boolean\n truncated?: boolean\n total_chars?: number\n}\n\nexport const TOOL_RESULT_HEAD_CHARS = 8_000\n\nexport type ImageRefPart = {\n type: 'image_ref'\n media_type: string\n bytes: number\n part_index: number\n}\n\nfunction base64Bytes(data: string): number {\n const padding = data.endsWith('==') ? 2 : data.endsWith('=') ? 1 : 0\n return Math.max(0, Math.floor((data.length * 3) / 4) - padding)\n}\n\nexport function imagePartRef(part: { type?: string; [key: string]: unknown }, index: number): ImageRefPart | undefined {\n if (part.type !== 'image') {\n return undefined\n }\n const source = part.source as { type?: string; data?: unknown; media_type?: unknown } | undefined\n if (!source || source.type !== 'base64' || typeof source.data !== 'string') {\n return undefined\n }\n return {\n type: 'image_ref',\n media_type: typeof source.media_type === 'string' ? source.media_type : 'application/octet-stream',\n bytes: base64Bytes(source.data),\n part_index: index,\n }\n}\n\nexport type UnknownBlock = { type: string; [key: string]: unknown }\n\nexport type PatchHunk = {\n oldStart: number\n oldLines: number\n newStart: number\n newLines: number\n lines: string[]\n}\n\nexport type FilePatch = {\n path?: string\n kind?: 'create' | 'update'\n hunks: PatchHunk[]\n truncated?: boolean\n}\n\nexport type ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock | UnknownBlock\n\nexport type MessageAttachment = {\n id: string\n name: string\n mediaType: string\n bytes: number\n}\n\nexport type ApiMessage = {\n role: 'user' | 'assistant'\n content: string | ContentBlock[]\n model?: string\n stop_reason?: string | null\n usage?: {\n input_tokens?: number\n output_tokens?: number\n cache_creation_input_tokens?: number\n cache_read_input_tokens?: number\n }\n}\n\nexport type PermissionRequest = {\n id: string\n toolName: string\n input: Record<string, unknown>\n toolUseId: string\n title?: string\n displayName?: string\n description?: string\n decisionReason?: string\n agentId?: string\n expiresAt?: number\n}\n\nexport type PermissionDecisionSource = 'client' | 'timeout' | 'policy'\n\nexport type UserQuestionOption = {\n label: string\n description?: string\n preview?: string\n}\n\nexport type UserQuestion = {\n question: string\n header: string\n options: UserQuestionOption[]\n multiSelect?: boolean\n}\n\nexport type QuestionBehavior = 'ask' | 'auto' | 'deny'\n\nexport function parseUserQuestions(input: Record<string, unknown>): UserQuestion[] {\n const raw = Array.isArray(input.questions) ? input.questions : []\n return raw.flatMap((entry): UserQuestion[] => {\n const q = entry as Partial<UserQuestion>\n if (typeof q.question !== 'string' || !Array.isArray(q.options)) {\n return []\n }\n const options = q.options.filter((o): o is UserQuestionOption => typeof (o as UserQuestionOption | undefined)?.label === 'string')\n if (options.length === 0) {\n return []\n }\n return [\n {\n question: q.question,\n header: typeof q.header === 'string' ? q.header : '',\n options,\n multiSelect: q.multiSelect === true,\n },\n ]\n })\n}\n\nexport type ModelOption = {\n value: string\n resolvedModel?: string\n displayName: string\n description?: string\n primary?: boolean\n reasoningEfforts?: readonly string[]\n}\n\nexport type SkillInfo = {\n name: string\n description?: string\n shortDescription?: string\n displayName?: string\n defaultPrompt?: string\n scope?: string\n enabled: boolean\n}\n\nexport type SlashCommandInfo = {\n name: string\n description?: string\n argumentHint?: string\n aliases?: string[]\n}\n\nexport type ContextUsageCategory = {\n name: string\n tokens: number\n color: string\n}\n\nexport type ContextUsage = {\n categories: ContextUsageCategory[]\n totalTokens: number\n maxTokens: number\n percentage: number\n model?: string\n}\n\nexport type ContextReading = {\n totalTokens: number\n maxTokens: number\n percentage: number\n}\n\nexport type RateLimitInfo = {\n status: string\n rateLimitType?: string\n utilization?: number\n resetsAt?: number\n isUsingOverage?: boolean\n}\n\nexport type ToolExecutionStatus = 'pending' | 'deferred' | 'settled' | 'failed'\n\nexport type ToolExecutionBackend = 'server' | 'browser' | 'managed' | 'remote'\n\nexport type ToolExecutionOutput = { type: 'text'; value: string } | { type: 'json'; value: unknown }\n\nexport type SessionEventBody =\n | {\n type: 'system_init'\n sdkSessionId: string\n model: string\n cwd: string\n apiKeySource: string\n tools: string[]\n skills: string[]\n slashCommands: string[]\n permissionMode: PermissionMode\n claudeCodeVersion: string\n mcpServers: Array<{ name: string; status: string }>\n }\n | { type: 'status_changed'; status: SessionStatus; detail?: string }\n | {\n type: 'capabilities'\n models: ModelOption[]\n commands: SlashCommandInfo[]\n defaultModel?: string\n }\n | { type: 'skills'; skills: SkillInfo[] }\n | { type: 'tool_titles'; titles: Record<string, string> }\n | { type: 'checklist'; items: ChecklistItem[] }\n | {\n type: 'file_produced'\n fileId: string\n path: string\n mediaType?: string\n bytes?: number\n toolUseId?: string\n }\n | { type: 'model_changed'; model?: string }\n | { type: 'permission_mode_changed'; mode: PermissionMode }\n | { type: 'context_usage'; usage: ContextUsage }\n | { type: 'rate_limit'; info: RateLimitInfo }\n | { type: 'plan_info'; subscriptionType: string }\n | {\n type: 'conversation_reset'\n sdkSessionId?: string\n }\n | {\n type: 'context_compacted'\n uuid: string\n parentToolUseId?: string | null\n // The same `uuid` is emitted twice: once with `pending` when the engine starts summarising,\n // once without it when the boundary lands, so one row settles in place. A reader that\n // never sees the first (an older gateway, a replay coalesced to the last) draws the\n // boundary alone, which is what it drew before.\n pending?: boolean\n trigger?: 'manual' | 'auto'\n preTokens?: number\n postTokens?: number\n error?: string\n }\n | {\n type: 'assistant_message'\n message: ApiMessage\n parentToolUseId: string | null\n replay?: boolean\n uuid: string\n }\n | {\n type: 'user_message'\n message: ApiMessage\n parentToolUseId: string | null\n replay?: boolean\n synthetic?: boolean\n attachments?: MessageAttachment[]\n patch?: FilePatch\n uuid?: string\n }\n | {\n type: 'stream_delta'\n event: { type: string; [key: string]: unknown }\n parentToolUseId: string | null\n uuid: string\n }\n | {\n type: 'turn_result'\n subtype: 'success' | 'error_during_execution' | 'error_max_turns' | 'error_max_budget_usd' | 'error_max_structured_output_retries'\n isError: boolean\n durationMs: number\n numTurns: number\n totalCostUsd: number\n result?: string\n errors?: string[]\n usage?: unknown\n }\n | { type: 'permission_requested'; request: PermissionRequest }\n | {\n type: 'permission_resolved'\n requestId: string\n behavior: 'allow' | 'deny'\n resolvedBy: PermissionDecisionSource\n message?: string\n }\n | {\n type: 'execution_dispatched'\n executionId: string\n toolName: string\n backend: ToolExecutionBackend\n deferred?: boolean\n expiresAt?: number\n }\n | {\n type: 'execution_result'\n executionId: string\n output: ToolExecutionOutput\n logs?: string[]\n durationMs?: number\n }\n | {\n type: 'execution_failed'\n executionId: string\n reason: string\n error: string\n logs?: string[]\n durationMs?: number\n }\n | { type: 'file_delivered'; path: string; bytes: number; description?: string }\n | { type: 'sdk_event'; payload: { type: string; [key: string]: unknown } }\n | { type: 'session_error'; message: string }\n | { type: 'session_closed'; reason: 'client' | 'server' | 'error' }\n\nexport type SessionEvent = SessionEventBody & {\n seq: number\n ts: number\n}\n\nexport type SessionCommand =\n | {\n type: 'user_message'\n text: string\n attachmentIds?: string[]\n }\n | {\n type: 'permission_decision'\n requestId: string\n behavior: 'allow' | 'deny'\n updatedInput?: Record<string, unknown>\n message?: string\n interrupt?: boolean\n }\n | { type: 'interrupt' }\n | { type: 'clear_context' }\n | { type: 'set_permission_mode'; mode: PermissionMode }\n | { type: 'set_model'; model?: string }\n | {\n type: 'tool_call_result'\n executionId: string\n output: ToolExecutionOutput\n logs?: string[]\n }\n | {\n type: 'tool_call_error'\n executionId: string\n reason: string\n error: string\n logs?: string[]\n }\n | { type: 'shell_command'; command: string }\n | { type: 'close' }\n\nexport const SHELL_COMMAND_MAX = 4000\n\nexport type AttachedFrame = {\n type: 'attached'\n protocolVersion: number\n session: SessionInfo\n replayingFrom: number\n // Whether this principal may run `!` commands on this session: server config x operator x hostCwd.\n // Additive, so no PROTOCOL_VERSION bump; an older gateway omits it and the client never offers the mode.\n shell?: boolean\n}\n\nexport type ToolCallRequestFrame = {\n type: 'tool_call_request'\n executionId: string\n toolName: string\n input: unknown\n vfsSeed?: Record<string, string>\n limits?: { timeoutMs?: number; memoryLimitBytes?: number }\n expiresAt?: number\n}\n\nexport type ServerFrame =\n | AttachedFrame\n | { type: 'event'; event: SessionEvent }\n | ToolCallRequestFrame\n | { type: 'tool_call_canceled'; executionId: string; reason: string }\n | { type: 'protocol_error'; message: string }\n\nexport type ClientFrame = SessionCommand\n\nexport type ProfileDefaults = {\n model?: string\n permissionMode?: PermissionMode\n}\n\nexport type ProfileEngine = 'claude' | 'codex' | 'provider'\n\nexport type EngineCapabilities = {\n interactiveApprovals: boolean\n permissionModes: readonly PermissionMode[]\n defaultPermissionMode: PermissionMode\n resume: boolean\n resumeBackfill: boolean\n listSessions: boolean\n contextUsage: boolean\n rateLimits: boolean\n mcpStatus: boolean\n mcpServerActions: boolean\n sessionMcpServers: boolean\n slashCommands: boolean\n clearContext?: boolean\n skillsList: boolean\n settingSources: boolean\n budgets: boolean\n attachments: ReadonlyArray<'image' | 'pdf' | 'text'>\n reasoningEfforts?: readonly string[]\n vfs: boolean\n hostCwd?: boolean\n streaming: 'token' | 'item' | 'none'\n}\n\nexport const ENGINE_CAPABILITIES: Record<ProfileEngine, EngineCapabilities> = {\n claude: {\n interactiveApprovals: true,\n permissionModes: ['default', 'acceptEdits', 'bypassPermissions', 'plan', 'dontAsk', 'auto'],\n defaultPermissionMode: 'default',\n resume: true,\n resumeBackfill: true,\n listSessions: true,\n contextUsage: true,\n rateLimits: true,\n mcpStatus: true,\n mcpServerActions: true,\n sessionMcpServers: true,\n slashCommands: true,\n clearContext: true,\n skillsList: false,\n settingSources: true,\n budgets: true,\n attachments: ['image', 'pdf', 'text'],\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],\n vfs: false,\n hostCwd: true,\n streaming: 'token',\n },\n codex: {\n interactiveApprovals: true,\n permissionModes: ['default', 'acceptEdits', 'bypassPermissions', 'auto'],\n defaultPermissionMode: 'default',\n resume: true,\n resumeBackfill: true,\n listSessions: true,\n contextUsage: true,\n rateLimits: true,\n mcpStatus: true,\n mcpServerActions: false,\n sessionMcpServers: false,\n slashCommands: false,\n clearContext: true,\n skillsList: true,\n settingSources: false,\n budgets: false,\n attachments: ['image', 'text'],\n reasoningEfforts: ['minimal', 'low', 'medium', 'high', 'xhigh'],\n vfs: false,\n hostCwd: true,\n streaming: 'token',\n },\n provider: {\n interactiveApprovals: false,\n permissionModes: ['default', 'bypassPermissions', 'dontAsk'],\n defaultPermissionMode: 'default',\n resume: false,\n resumeBackfill: false,\n listSessions: false,\n contextUsage: false,\n rateLimits: false,\n mcpStatus: true,\n mcpServerActions: false,\n sessionMcpServers: false,\n slashCommands: false,\n clearContext: true,\n skillsList: false,\n settingSources: false,\n budgets: false,\n attachments: ['image', 'pdf', 'text'],\n vfs: true,\n hostCwd: false,\n streaming: 'token',\n },\n}\n\nexport function supportsPermissionMode(engine: ProfileEngine | undefined, mode: PermissionMode): boolean {\n return ENGINE_CAPABILITIES[engine ?? 'claude'].permissionModes.includes(mode)\n}\n\nexport type ProviderConfig = {\n id: string\n model?: string\n models?: string[]\n baseUrl?: string\n apiKeyEnv?: string\n}\n\nexport type SessionCapability = 'web_search' | 'download' | 'web_fetch' | 'deliver_file'\n\nexport type ProfileSessionDefaults = {\n capabilities?: SessionCapability[]\n mcpServers?: string[]\n instructions?: string\n}\n\nexport type ProfileUsageWindow = {\n info: RateLimitInfo\n updatedAt: number\n inferredReset?: boolean\n}\n\nexport type ProfileUsage = Record<string, ProfileUsageWindow>\n\nexport type ProfileInfo = {\n name: string\n engine?: ProfileEngine\n configDir?: string\n codexHome?: string\n provider?: ProviderConfig\n description?: string\n defaults?: ProfileDefaults\n session?: ProfileSessionDefaults\n models?: ModelOption[]\n defaultModel?: string\n capabilities?: EngineCapabilities\n available?: boolean\n unavailableReason?: string\n usage?: ProfileUsage\n managed?: boolean\n}\n\nexport type ProfileConfigSnapshot = {\n settings?: {\n model?: string\n defaultPermissionMode?: string\n permissionRules?: { allow: number; ask: number; deny: number }\n envKeys?: string[]\n hooks?: string[]\n }\n hasUserMemory: boolean\n skills: string[]\n agents: string[]\n commands: string[]\n}\n\nexport type McpServerConfigWire =\n | { type?: 'stdio'; command: string; args?: string[]; env?: Record<string, string> }\n | { type: 'http'; url: string; headers?: Record<string, string> }\n | { type: 'sse'; url: string; headers?: Record<string, string> }\n\nexport type McpServerToolInfo = {\n name: string\n title?: string\n description?: string\n annotations?: { readOnly?: boolean; destructive?: boolean; openWorld?: boolean }\n inputSchema?: unknown\n}\n\nexport type McpServerStatusInfo = {\n name: string\n status: string\n scope?: string\n error?: string\n serverInfo?: { name: string; version: string }\n transport?: 'stdio' | 'http' | 'sse' | 'sdk'\n command?: string\n args?: string[]\n url?: string\n tools?: McpServerToolInfo[]\n}\n\nexport type McpServersResponse = { servers: McpServerStatusInfo[] }\n\nexport type McpServerActionRequest = { action: 'reconnect' | 'enable' | 'disable' }\n\nexport type UploadAttachmentResponse = { attachment: MessageAttachment }\n\nexport type CreateSessionRequest = {\n cwd?: string\n profile?: string\n prompt?: string\n permissionMode?: PermissionMode\n allowDangerouslySkipPermissions?: boolean\n allowedTools?: string[]\n disallowedTools?: string[]\n mcpServers?: Record<string, McpServerConfigWire>\n settingSources?: Array<'user' | 'project' | 'local'>\n model?: string\n maxTurns?: number\n maxBudgetUsd?: number\n resume?: string\n forkSession?: boolean\n reasoningEffort?: string\n includePartialMessages?: boolean\n // Overrides the gateway's default. null (or 0) means a prompt never expires — it waits as long as the session lives.\n approvalTimeoutMs?: number | null\n questionBehavior?: QuestionBehavior\n capabilities?: SessionCapability[]\n meta?: Record<string, unknown>\n scope?: Record<string, string>\n}\n\nexport type SubagentInfo = {\n toolUseId: string\n agentType?: string\n description?: string\n status: 'running' | 'done' | 'failed'\n startedAt: number\n toolCount: number\n}\n\nexport const SUBAGENT_HISTORY = 8\n\n// Widening this union costs a PROTOCOL_VERSION bump: Swift decodes it strictly.\nexport type ChecklistStatus = 'pending' | 'in_progress' | 'completed'\n\nexport type ChecklistItem = { text: string; status: ChecklistStatus }\n\nexport const CHECKLIST_TEXT_MAX = 200\n\nexport type ProjectIcon = { type: 'glyph'; name: string } | { type: 'image'; mediaType: 'image/png' | 'image/svg+xml'; hash: string }\n\nexport type ProjectInfo = {\n name: string\n root: string\n icon?: ProjectIcon\n}\n\nexport type SessionInfo = {\n id: string\n sdkSessionId?: string\n status: SessionStatus\n cwd: string\n profile?: string\n engine?: ProfileEngine\n capabilities?: EngineCapabilities\n model?: string\n permissionMode?: PermissionMode\n canBypassPermissions?: boolean\n apiKeySource?: string\n createdAt: number\n lastSeq: number\n // Which log the seqs belong to. A dormant wake starts a fresh log and bumps it, so a `seq`\n // that outlived one — a notification sitting on a lock screen — can be told from a live one.\n // Absent from a gateway that predates it, and from a session that has never woken; additive,\n // so no `PROTOCOL_VERSION` bump, and a reader that finds it absent on either side must keep\n // trusting the seq.\n epoch?: number\n pendingPermissionCount: number\n subagents?: SubagentInfo[]\n checklist?: ChecklistItem[]\n meta?: Record<string, unknown>\n title?: string\n totalCostUsd?: number\n numTurns?: number\n activityCount?: number\n // Rows of the kind a person is actually waiting to read — see `transcriptProse`.\n // Absent from a gateway that predates it — additive, so no `PROTOCOL_VERSION` bump —\n // which is why every reader falls back to `activityCount`. This is the badge's number; `activityCount` stays the \"has\n // anything happened at all\" measure that sorting and dormancy read.\n proseCount?: number\n lastActivityAt?: number\n contextUsage?: ContextReading\n scope?: Record<string, string>\n project?: ProjectInfo\n}\n\nexport function contextReading(body: SessionEventBody): ContextReading | undefined {\n if (body.type !== 'context_usage') {\n return undefined\n }\n const { totalTokens, maxTokens, percentage } = body.usage\n return { totalTokens, maxTokens, percentage }\n}\n\nexport function transcriptActivity(body: SessionEventBody): number {\n if ('parentToolUseId' in body && body.parentToolUseId != null) {\n return 0\n }\n switch (body.type) {\n case 'assistant_message': {\n const content = body.message.content\n if (typeof content === 'string') {\n return content.trim() === '' ? 0 : 1\n }\n const rows = content.filter((block) => block.type === 'text' || block.type === 'thinking' || block.type === 'tool_use').length\n return rows\n }\n case 'user_message': {\n return body.synthetic ? 0 : 1\n }\n case 'turn_result':\n case 'file_delivered':\n case 'session_error': {\n return 1\n }\n default: {\n return 0\n }\n }\n}\n\n// The narrower door beside transcriptActivity: output addressed to the human, not evidence of work.\nexport function transcriptProse(body: SessionEventBody): number {\n if ('parentToolUseId' in body && body.parentToolUseId != null) {\n return 0\n }\n switch (body.type) {\n case 'assistant_message': {\n const content = body.message.content\n if (typeof content === 'string') {\n return content.trim() === '' ? 0 : 1\n }\n return content.filter((block) => block.type === 'text' && typeof block.text === 'string' && block.text.trim() !== '').length\n }\n case 'user_message': {\n return body.synthetic ? 0 : 1\n }\n case 'turn_result': {\n return body.isError ? 1 : 0\n }\n case 'file_delivered':\n case 'session_error': {\n return 1\n }\n default: {\n return 0\n }\n }\n}\n\nexport function transcriptContent(body: SessionEventBody): boolean {\n switch (body.type) {\n case 'user_message':\n case 'assistant_message':\n case 'stream_delta':\n case 'turn_result':\n case 'execution_dispatched':\n case 'execution_result':\n case 'execution_failed':\n case 'file_delivered':\n case 'session_error':\n case 'session_closed':\n case 'conversation_reset':\n case 'context_compacted': {\n return true\n }\n default: {\n return false\n }\n }\n}\n\nexport function replayCoalesceKey(body: SessionEventBody): string | undefined {\n switch (body.type) {\n case 'context_usage': {\n return 'context_usage'\n }\n case 'rate_limit': {\n return body.info.rateLimitType ? `rate_limit:${body.info.rateLimitType}` : undefined\n }\n case 'status_changed': {\n return 'status_changed'\n }\n case 'context_compacted': {\n return `context_compacted:${body.uuid}`\n }\n case 'checklist': {\n return 'checklist'\n }\n case 'sdk_event': {\n return body.payload.type === 'system' && body.payload.subtype === 'status' ? 'sdk_event:system:status' : undefined\n }\n default: {\n return undefined\n }\n }\n}\n\nexport function replayRetains(body: SessionEventBody): boolean {\n if (body.type !== 'stream_delta') {\n return true\n }\n const delta = body.event as { type?: string; delta?: { type?: string } }\n if (delta.type !== 'content_block_delta') {\n return false\n }\n return delta.delta?.type === 'text_delta' || delta.delta?.type === 'thinking_delta'\n}\n\nexport function snapshotRetains(body: SessionEventBody): boolean {\n return body.type !== 'stream_delta'\n}\n\nexport type SdkSessionSummary = {\n sessionId: string\n summary: string\n lastModified: number\n createdAt?: number\n customTitle?: string\n firstPrompt?: string\n gitBranch?: string\n cwd?: string\n}\n\nexport type SessionFileInfo = { path: string; bytes: number }\nexport type ListSessionFilesResponse = { files: SessionFileInfo[] }\nexport type ListSessionsResponse = { sessions: SessionInfo[] }\nexport type CreateSessionResponse = { session: SessionInfo }\nexport type GetSessionResponse = { session: SessionInfo }\n\nexport type UpdateSessionRequest = { title?: string | null }\nexport type UpdateSessionResponse = { session: SessionInfo }\n\nexport type ResolvePermissionRequest =\n | { behavior: 'allow'; updatedInput?: Record<string, unknown> }\n | { behavior: 'deny'; message?: string; interrupt?: boolean }\nexport type ResolvePermissionResponse = { resolved: true }\n\nexport type SubmitExecutionResultRequest =\n | { status: 'ok'; output: ToolExecutionOutput; logs?: string[] }\n | { status: 'failed'; reason: string; error: string; logs?: string[] }\nexport type SubmitExecutionResultResponse = {\n applied: boolean\n sessionId: string\n}\n\nexport type ListSdkSessionsResponse = { sdkSessions: SdkSessionSummary[] }\nexport type ListProfilesResponse = {\n profiles: ProfileInfo[]\n canManage?: boolean\n}\n\nexport type CreateProfileRequest = ProfileInfo\n\nexport type UpdateProfileRequest = Omit<Partial<ProfileInfo>, 'name'>\n\nexport type HostFileRoot = {\n path: string\n name: string\n}\n\nexport type ListHostRootsResponse = {\n roots: HostFileRoot[]\n canWrite: boolean\n}\n\nexport type HostDirEntry = {\n name: string\n path: string\n type: 'file' | 'dir' | 'symlink' | 'other'\n bytes?: number\n modifiedAt?: number\n}\n\nexport type ListHostDirResponse = {\n path: string\n entries: HostDirEntry[]\n truncated?: boolean\n}\n\nexport type HostFileMatch = {\n path: string\n relative: string\n}\n\nexport type FindHostFilesResponse = {\n base: string\n matches: HostFileMatch[]\n truncated: boolean\n}\n\nexport type ReadHostFileResponse = {\n path: string\n content: string\n encoding: 'utf8' | 'base64'\n bytes: number\n hash: string\n modifiedAt: number\n}\n\nexport type WriteHostFileRequest = {\n path: string\n content: string\n encoding?: 'utf8' | 'base64'\n expectedHash?: string\n}\n\nexport type WriteHostFileResponse = {\n path: string\n bytes: number\n hash: string\n modifiedAt: number\n}\n\nexport type SaveProfileResponse = { profile: ProfileInfo }\nexport type GetProfileResponse = { profile: ProfileInfo; config: ProfileConfigSnapshot }\nexport type ErrorResponse = { error: string }\n\nexport type SessionNotificationType = 'permission_requested' | 'turn_completed' | 'session_error' | 'session_closed'\n\nexport type SessionNotification = {\n type: SessionNotificationType\n sessionId: string\n session: SessionInfo\n seq: number\n ts: number\n preview?: string\n request?: PermissionRequest\n result?: { isError: boolean; durationMs: number; numTurns: number; totalCostUsd: number }\n reason?: 'client' | 'server' | 'error'\n}\n\nexport type SessionWebhookConfig = {\n url: string\n headers?: Record<string, string>\n events?: SessionNotificationType[]\n}\n\nexport type JobStatus = 'queued' | 'running' | 'parked' | 'succeeded' | 'failed' | 'canceled'\n\nexport type WebhookConfig = {\n url: string\n headers?: Record<string, string>\n progress?: 'messages' | 'completion'\n}\n\nexport type CreateJobRequest = {\n session: CreateSessionRequest & { prompt: string }\n webhook?: WebhookConfig\n maxTokens?: number\n maxDurationMs?: number\n attempts?: number\n retryDelayMs?: number\n meta?: Record<string, unknown>\n}\n\nexport type JobUsage = {\n tokens: number\n totalCostUsd: number\n numTurns: number\n}\n\nexport type JobResult = {\n subtype: string\n isError: boolean\n result?: string\n errors?: string[]\n durationMs: number\n}\n\nexport type JobInfo = {\n id: string\n status: JobStatus\n cwd: string\n profile?: string\n prompt: string\n sessionId?: string\n sdkSessionId?: string\n createdAt: number\n startedAt?: number\n finishedAt?: number\n attempt?: number\n maxAttempts?: number\n nextRunAt?: number\n parkedAt?: number\n parkedExecutionId?: string\n usage: JobUsage\n result?: JobResult\n error?: string\n meta?: Record<string, unknown>\n scope?: Record<string, string>\n}\n\nexport type JobProgress = {\n kind: 'assistant_text' | 'tool_use' | 'permission_requested' | 'permission_resolved'\n preview?: string\n request?: PermissionRequest\n}\n\nexport type JobEvent =\n | { type: 'job_submitted'; job: JobInfo; ts: number }\n | { type: 'job_started'; job: JobInfo; ts: number }\n | { type: 'job_progress'; job: JobInfo; progress: JobProgress; ts: number }\n | { type: 'job_parked'; job: JobInfo; executionId: string; ts: number }\n | { type: 'job_resumed'; job: JobInfo; executionId: string; ts: number }\n | { type: 'job_retrying'; job: JobInfo; ts: number }\n | { type: 'job_completed'; job: JobInfo; ts: number }\n\nexport type QueueStats = {\n maxConcurrency: number\n running: number\n queued: number\n parked: number\n sessionTokenLimit?: number\n dailyTokenLimit?: number\n dailyTokensUsed: number\n paused: boolean\n}\n\nexport type QueueServerFrame =\n | { type: 'queue_attached'; protocolVersion: number; stats: QueueStats }\n | { type: 'job_event'; event: JobEvent }\n | { type: 'queue_stats'; stats: QueueStats }\n\nexport type CreateJobResponse = { job: JobInfo }\nexport type GetJobResponse = { job: JobInfo }\nexport type ListJobsResponse = { jobs: JobInfo[] }\nexport type QueueStatsResponse = { stats: QueueStats }\n\nexport * from './checklist.ts'\nexport * from './session-list.ts'\nexport * from './tool-titles.ts'\nexport * from './usage.ts'\nexport * from './watermarks.ts'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/session-list.ts","../src/checklist.ts","../src/tool-titles.ts","../src/usage.ts","../src/watermarks.ts","../src/index.ts"],"sourcesContent":["import type { SessionInfo, SubagentInfo } from './index.ts'\n\nexport type SessionState = 'attention' | 'working' | 'idle' | 'ended'\n\nexport const STATE_ORDER: readonly SessionState[] = ['attention', 'working', 'idle', 'ended']\n\nexport const STATE_LABELS: Record<SessionState, string> = {\n attention: 'Needs attention',\n working: 'Working',\n idle: 'Idle',\n ended: 'Ended',\n}\n\nexport function sessionState(info: SessionInfo): SessionState {\n if (info.pendingPermissionCount > 0 || info.status === 'awaiting_approval') {\n return 'attention'\n }\n if (info.status === 'failed' || info.status === 'closed') {\n return 'ended'\n }\n if (info.status === 'running' || info.status === 'starting') {\n return 'working'\n }\n if (runningSubagents(info).length > 0) {\n return 'working'\n }\n return 'idle'\n}\n\nexport function runningSubagents(info: SessionInfo): SubagentInfo[] {\n return (info.subagents ?? []).filter((sub) => sub.status === 'running')\n}\n\nexport function isAgentRecord(sub: SubagentInfo): boolean {\n return (sub.agentType?.trim() ?? '') !== ''\n}\n\nexport function subagentLabel(sub: SubagentInfo): string {\n const agent = sub.agentType?.trim()\n const description = sub.description?.trim()\n if (agent && description) {\n return `${agent} · ${description}`\n }\n return agent || description || 'Sub-agent'\n}\n\nexport type Facet = 'gateway' | 'adapter' | 'state' | 'project'\nexport type GroupBy = 'none' | Facet\nexport type SortBy = 'recent' | 'name' | Facet\n\nexport type ViewConfig = {\n search: string\n gateways: string[]\n adapters: string[]\n states: SessionState[]\n projects?: string[]\n scoped: boolean\n groupBy: GroupBy\n sortBy: SortBy\n}\n\nexport const DEFAULT_VIEW_CONFIG: ViewConfig = {\n search: '',\n gateways: [],\n adapters: [],\n states: [],\n projects: [],\n scoped: true,\n groupBy: 'state',\n sortBy: 'recent',\n}\n\nexport type ScopeRoot = { hostId?: string; path: string }\n\nexport type WorkspaceScope = { label: string; roots: ScopeRoot[] }\n\nexport type SessionRow = {\n hostId: string\n hostName: string\n local: boolean\n adapter: string\n state: SessionState\n info: SessionInfo\n // Messages this client has not read - `unseenCount` against its watermark, so the\n // unit is prose (`SessionInfo.proseCount`) wherever the gateway reports it. A session\n // that is only running tools contributes 0: the badge answers \"is there something to\n // read\", not \"is anything happening\", which is what `state` is for.\n unseen: number\n}\n\nexport type SessionGroup = { key: string; label?: string; rows: SessionRow[] }\n\nexport function adaptersOf(rows: readonly SessionRow[]): string[] {\n return [...new Set(rows.map((r) => r.adapter))].sort()\n}\n\nexport function projectsOf(rows: readonly SessionRow[]): { key: string; label: string }[] {\n const byKey = new Map<string, string>()\n for (const row of rows) {\n byKey.set(projectKey(row), projectLabel(row))\n }\n return [...byKey].map(([key, label]) => ({ key, label })).sort((a, b) => a.label.toLowerCase().localeCompare(b.label.toLowerCase()))\n}\n\nexport function sessionLabel(info: SessionInfo): string {\n return info.title ?? info.id.slice(0, 8)\n}\n\nexport function projectKey(row: SessionRow): string {\n return `${row.hostId}:${normalizePath(row.info.project?.root ?? row.info.cwd)}`\n}\n\nexport function projectLabel(row: Pick<SessionRow, 'info'>): string {\n const name = row.info.project?.name\n if (name) {\n return name\n }\n const dir = normalizePath(row.info.cwd)\n return dir.slice(dir.lastIndexOf('/') + 1) || 'No project'\n}\n\nexport function projectSubpath(row: Pick<SessionRow, 'info'>): string | undefined {\n const root = row.info.project?.root\n if (root === undefined || !row.info.cwd) {\n return undefined\n }\n const base = normalizePath(root)\n const dir = normalizePath(row.info.cwd)\n if (dir === base) {\n return undefined\n }\n if (!dir.startsWith(`${base}/`)) {\n return undefined\n }\n return dir.slice(base.length + 1) || undefined\n}\n\nexport function isJobRun(info: SessionInfo): boolean {\n return typeof info.meta?.jobId === 'string'\n}\n\nfunction matchesSearch(row: SessionRow, needle: string): boolean {\n if (!needle) {\n return true\n }\n return (\n sessionLabel(row.info).toLowerCase().includes(needle) ||\n row.info.cwd.toLowerCase().includes(needle) ||\n (row.info.project?.name.toLowerCase().includes(needle) ?? false) ||\n row.hostName.toLowerCase().includes(needle) ||\n row.adapter.toLowerCase().includes(needle) ||\n row.info.id.startsWith(needle)\n )\n}\n\nfunction normalizePath(path: string): string {\n return path.replace(/\\\\/g, '/').replace(/\\/+$/, '')\n}\n\nfunction isWithin(root: string, path: string): boolean {\n const base = normalizePath(root)\n const dir = normalizePath(path)\n // The separator matters: /a/project must not swallow /a/project-2.\n return dir === base || dir.startsWith(`${base}/`)\n}\n\nexport function inScope(row: SessionRow, scope: WorkspaceScope): boolean {\n return scope.roots.some(\n (root) => (root.hostId ? root.hostId.toLowerCase() === row.hostId.toLowerCase() : row.local) && isWithin(root.path, row.info.cwd),\n )\n}\n\nexport function scopeActive(config: ViewConfig, scope: WorkspaceScope | undefined): boolean {\n return config.scoped && scope !== undefined\n}\n\nexport function filterRows(rows: readonly SessionRow[], config: ViewConfig, scope?: WorkspaceScope): SessionRow[] {\n const needle = config.search.trim().toLowerCase()\n const scoping = scopeActive(config, scope) ? scope : undefined\n return rows.filter(\n (row) =>\n (config.gateways.length === 0 || config.gateways.includes(row.hostId)) &&\n (config.adapters.length === 0 || config.adapters.includes(row.adapter)) &&\n (config.states.length === 0 || config.states.includes(row.state)) &&\n (!config.projects?.length || config.projects.includes(projectKey(row))) &&\n (!scoping || inScope(row, scoping)) &&\n matchesSearch(row, needle),\n )\n}\n\nfunction facetKey(row: SessionRow, facet: Facet): string {\n return facet === 'gateway' ? row.hostId : facet === 'adapter' ? row.adapter : facet === 'project' ? projectKey(row) : row.state\n}\n\nfunction facetLabel(row: SessionRow, facet: Facet): string {\n return facet === 'gateway'\n ? row.hostName\n : facet === 'adapter'\n ? row.adapter\n : facet === 'project'\n ? projectLabel(row)\n : STATE_LABELS[row.state]\n}\n\nfunction facetRank(row: SessionRow, facet: Facet): string {\n if (facet === 'state') {\n return String(STATE_ORDER.indexOf(row.state))\n }\n return facetLabel(row, facet).toLowerCase()\n}\n\nfunction byRecency(a: SessionRow, b: SessionRow) {\n return (b.info.lastActivityAt ?? b.info.createdAt) - (a.info.lastActivityAt ?? a.info.createdAt)\n}\n\nfunction compare(a: SessionRow, b: SessionRow, sortBy: SortBy): number {\n if (sortBy === 'recent') {\n return byRecency(a, b)\n }\n if (sortBy === 'name') {\n return (\n sessionLabel(a.info).localeCompare(sessionLabel(b.info), undefined, {\n sensitivity: 'base',\n }) || byRecency(a, b)\n )\n }\n return facetRank(a, sortBy).localeCompare(facetRank(b, sortBy)) || byRecency(a, b)\n}\n\nexport function groupRows(rows: readonly SessionRow[], config: ViewConfig): SessionGroup[] {\n const sorted = [...rows].sort((a, b) => compare(a, b, config.sortBy))\n if (config.groupBy === 'none') {\n return sorted.length ? [{ key: 'all', rows: sorted }] : []\n }\n const facet = config.groupBy\n const groups = new Map<string, SessionGroup & { rank: string }>()\n for (const row of sorted) {\n const key = facetKey(row, facet)\n const group = groups.get(key)\n if (group) {\n group.rows.push(row)\n } else {\n groups.set(key, {\n key,\n label: facetLabel(row, facet),\n rank: facetRank(row, facet),\n rows: [row],\n })\n }\n }\n return [...groups.values()].sort((a, b) => a.rank.localeCompare(b.rank))\n}\n\nexport type SubsetSummary = { shown: number; total: number; causes: string[] }\n\nexport function subsetSummary(\n config: ViewConfig,\n scope: WorkspaceScope | undefined,\n shown: number,\n total: number,\n): SubsetSummary | undefined {\n if (shown >= total) {\n return undefined\n }\n const causes: string[] = []\n if (scope && scopeActive(config, scope)) {\n causes.push(scope.label)\n }\n const facets =\n (config.gateways.length ? 1 : 0) + (config.adapters.length ? 1 : 0) + (config.states.length ? 1 : 0) + (config.projects?.length ? 1 : 0)\n if (facets > 0) {\n causes.push(`${facets} filter${facets === 1 ? '' : 's'}`)\n }\n if (config.search.trim()) {\n causes.push('search')\n }\n return { shown, total, causes }\n}\n\nexport function hasFacetFilter(config: ViewConfig): boolean {\n return (\n config.search.trim().length > 0 ||\n config.gateways.length > 0 ||\n config.adapters.length > 0 ||\n config.states.length > 0 ||\n (config.projects?.length ?? 0) > 0\n )\n}\n\nexport function clearFilters(config: ViewConfig): ViewConfig {\n return {\n ...DEFAULT_VIEW_CONFIG,\n scoped: false,\n groupBy: config.groupBy,\n sortBy: config.sortBy,\n }\n}\n","import type { ChecklistItem, ChecklistStatus, SessionInfo } from './index.ts'\nimport { isAgentRecord } from './session-list.ts'\n\nexport type SessionTaskState = 'pending' | 'running' | 'done' | 'failed'\n\nexport type SessionTaskSource = 'checklist' | 'spawn'\n\nexport type SessionTask = {\n key: string\n label: string\n source: SessionTaskSource\n state: SessionTaskState\n detail?: string\n toolUseId?: string\n}\n\nexport type TaskSummary = { total: number; done: number; running: number; failed: number }\n\nfunction checklistEntry(value: unknown): ChecklistItem | undefined {\n if (typeof value !== 'object' || value === null) {\n return undefined\n }\n const raw = value as { content?: unknown; status?: unknown; activeForm?: unknown }\n const status = raw.status\n if (status !== 'pending' && status !== 'in_progress' && status !== 'completed') {\n return undefined\n }\n const content = typeof raw.content === 'string' ? raw.content.trim() : ''\n if (content === '') {\n return undefined\n }\n const active = typeof raw.activeForm === 'string' ? raw.activeForm.trim() : ''\n return { status, text: status === 'in_progress' && active !== '' ? active : content }\n}\n\n// Whole-or-nothing: a malformed entry (streaming, partial input) yields undefined rather than a half-read list.\n// A well-formed empty list is a clear, and answers `[]`.\nexport function parseTodoWriteInput(input: unknown): ChecklistItem[] | undefined {\n const todos = (input as { todos?: unknown } | null)?.todos\n if (!Array.isArray(todos)) {\n return undefined\n }\n const out: ChecklistItem[] = []\n for (const value of todos) {\n const entry = checklistEntry(value)\n if (entry === undefined) {\n return undefined\n }\n out.push(entry)\n }\n return out\n}\n\nfunction checklistState(status: ChecklistStatus): SessionTaskState {\n switch (status) {\n case 'in_progress': {\n return 'running'\n }\n case 'completed': {\n return 'done'\n }\n default: {\n return 'pending'\n }\n }\n}\n\nexport function sessionTasks(info: Pick<SessionInfo, 'checklist' | 'subagents'>): SessionTask[] {\n const checklist = (info.checklist ?? []).map((item, index) => ({\n key: `checklist:${index}`,\n label: item.text,\n source: 'checklist' as const,\n state: checklistState(item.status),\n }))\n const spawns = (info.subagents ?? [])\n .filter((sub) => !isAgentRecord(sub))\n .map((sub) => ({\n key: `spawn:${sub.toolUseId}`,\n label: sub.description?.trim() || 'Task',\n source: 'spawn' as const,\n state: sub.status,\n detail: sub.toolCount > 0 ? String(sub.toolCount) : undefined,\n toolUseId: sub.toolUseId,\n }))\n return [...checklist, ...spawns]\n}\n\nexport function taskSummary(tasks: readonly SessionTask[]): TaskSummary {\n let done = 0\n let running = 0\n let failed = 0\n for (const task of tasks) {\n if (task.state === 'done') {\n done += 1\n } else if (task.state === 'running') {\n running += 1\n } else if (task.state === 'failed') {\n failed += 1\n }\n }\n return { total: tasks.length, done, running, failed }\n}\n\nexport function taskCountLabel(summary: TaskSummary): string | undefined {\n return summary.total === 0 ? undefined : `${summary.done}/${summary.total}`\n}\n\nexport function visibleTasks(tasks: readonly SessionTask[], showCompleted: boolean): SessionTask[] {\n return showCompleted ? [...tasks] : tasks.filter((task) => task.state !== 'done')\n}\n","export const TOOL_TITLE_MAX_CHARS = 64\n\n// Capability tools the sandbox grants and the synthetic names the codex adapter invents: wire\n// names with no public vocabulary behind them. An engine's own tool names (`Bash`, `Read`,\n// `Task`) are deliberately absent - those are the CLI's published names and users read them.\nexport const BUILTIN_TOOL_TITLES: Record<string, string> = {\n fs_read: 'Reading a file',\n fs_write: 'Writing a file',\n fs_list: 'Listing files',\n eval_script: 'Running a script',\n web_fetch: 'Fetching a web page',\n web_search: 'Searching the web',\n deliver_file: 'Delivering a file',\n download: 'Downloading a file',\n CodexCommand: 'Running a command',\n CodexFileChange: 'Editing a file',\n CodexWebSearch: 'Searching the web',\n CodexImageGeneration: 'Generating an image',\n CodexImageView: 'Viewing an image',\n}\n\nexport function toolTitle(name: string, titles?: Record<string, string>): string | undefined {\n return sanitizeToolTitle(titles?.[name] ?? BUILTIN_TOOL_TITLES[name], name)\n}\n\n// Titles reach here from remote MCP servers, so they are untrusted display text: one line,\n// clamped, and never a restatement of the wire name the caller already has.\nexport function sanitizeToolTitle(title: string | undefined, name?: string): string | undefined {\n if (typeof title !== 'string') {\n return undefined\n }\n const flat = [...title]\n .map((char) => (char < ' ' || char === '\\u007f' ? ' ' : char))\n .join('')\n .replace(/\\s+/g, ' ')\n .trim()\n if (flat === '' || flat === name) {\n return undefined\n }\n // By code point, not code unit: a clamp that splits a surrogate pair renders a replacement\n // character, and the clients that mirror this rule do not count in UTF-16.\n const points = [...flat]\n return points.length > TOOL_TITLE_MAX_CHARS ? points.slice(0, TOOL_TITLE_MAX_CHARS - 1).join('') + '…' : flat\n}\n","import type { ProfileUsage, RateLimitInfo } from './index.ts'\n\nexport type SessionUsage = {\n rateLimits?: Record<string, RateLimitInfo>\n updatedAt?: number\n}\n\nexport function mergeUsage(session: SessionUsage, profile: ProfileUsage | undefined): ProfileUsage {\n const out: ProfileUsage = {}\n for (const [key, info] of Object.entries(session.rateLimits ?? {})) {\n out[key] = { info, updatedAt: session.updatedAt ?? 0 }\n }\n for (const [key, window] of Object.entries(profile ?? {})) {\n out[key] = window\n }\n return out\n}\n\nexport type UsageWindowRow = {\n key: string\n info: RateLimitInfo\n updatedAt?: number\n inferredReset?: boolean\n}\n\nexport function orderUsageWindows(usage: ProfileUsage | undefined): UsageWindowRow[] {\n const all = Object.entries(usage ?? {})\n .filter(([, w]) => w.info.utilization !== undefined)\n .map(([key, w]) => ({ key, info: w.info, updatedAt: w.updatedAt, inferredReset: w.inferredReset }))\n const named = ['five_hour', 'seven_day'].flatMap((key) => all.filter((w) => w.key === key))\n const perModel = all.filter((w) => w.key.startsWith('seven_day_')).sort((a, b) => a.key.localeCompare(b.key))\n return [...named, ...perModel]\n}\n\nexport function usageInfos(usage: ProfileUsage | undefined): Record<string, RateLimitInfo> | undefined {\n if (!usage) {\n return undefined\n }\n const out: Record<string, RateLimitInfo> = {}\n for (const [key, window] of Object.entries(usage)) {\n out[key] = window.info\n }\n return out\n}\n","export type Watermark = {\n itemCount: number\n activity: number\n // Prose rows read (`SessionInfo.proseCount`). Optional because a mark stored before\n // prose counting existed cannot say - see `unseenCount`, which reads that absence as\n // \"caught up\" rather than badging a whole history the operator has already seen.\n prose?: number\n turns: number\n seenAt: number\n}\n\nexport type WatermarkStore = {\n read(): Record<string, Watermark> | undefined\n write(marks: Record<string, Watermark>): void\n}\n\nconst MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000\n\nconst TOUCH_MS = 60_000\n\nexport function watermarkKey(hostId: string, sessionId: string) {\n return `${hostId}:${sessionId}`\n}\n\nexport class Watermarks {\n readonly #store: WatermarkStore\n #cache: Record<string, Watermark>\n\n constructor(store: WatermarkStore) {\n this.#store = store\n this.#cache = { ...store.read() }\n }\n\n get(hostId: string, sessionId: string): Watermark | undefined {\n return this.#cache[watermarkKey(hostId, sessionId)]\n }\n\n all(): Readonly<Record<string, Watermark>> {\n return this.#cache\n }\n\n mark(\n hostId: string,\n sessionId: string,\n seen: { itemCount?: number; activity?: number; prose?: number; turns?: number },\n now = Date.now(),\n ): boolean {\n const id = watermarkKey(hostId, sessionId)\n const previous = this.#cache[id]\n const next: Watermark = {\n itemCount: Math.max(previous?.itemCount ?? 0, seen.itemCount ?? 0),\n activity: Math.max(previous?.activity ?? 0, seen.activity ?? 0),\n // A caller with nothing to say about prose (an older gateway reports no `proseCount`)\n // must not overwrite a real mark with 0, which would re-badge everything already read.\n prose: seen.prose === undefined ? previous?.prose : Math.max(previous?.prose ?? 0, seen.prose),\n turns: Math.max(previous?.turns ?? 0, seen.turns ?? 0),\n seenAt: now,\n }\n if (\n previous &&\n previous.itemCount === next.itemCount &&\n previous.activity === next.activity &&\n previous.prose === next.prose &&\n previous.turns === next.turns &&\n next.seenAt - previous.seenAt < TOUCH_MS\n ) {\n return false\n }\n this.#cache[id] = next\n this.#store.write(this.#prune(now))\n return true\n }\n\n forget(hostId: string, sessionId: string): void {\n const id = watermarkKey(hostId, sessionId)\n if (!(id in this.#cache)) {\n return\n }\n delete this.#cache[id]\n this.#store.write(this.#cache)\n }\n\n #prune(now: number): Record<string, Watermark> {\n const cutoff = now - MAX_AGE_MS\n for (const [id, mark] of Object.entries(this.#cache)) {\n if (mark.seenAt < cutoff) {\n delete this.#cache[id]\n }\n }\n return this.#cache\n }\n}\n\n// The badge's number, in the best unit the pair can agree on: prose the human has not\n// read, else rows, else turns. The ladder is what keeps an older gateway (no `proseCount`\n// on the wire) badging exactly as it did before rather than going silent.\nexport function unseenCount(mark: Watermark | undefined, info: { proseCount?: number; activityCount?: number; turns?: number }): number {\n if (!mark) {\n return 0\n }\n if (info.proseCount !== undefined) {\n // `mark.prose` absent = a mark written before prose counting. Reading it as\n // \"caught up\" costs one missed badge on a session already visited; reading it as 0\n // would badge every such session with its entire history the first time it polls.\n return Math.max(0, info.proseCount - (mark.prose ?? info.proseCount))\n }\n if (info.activityCount !== undefined) {\n return Math.max(0, info.activityCount - mark.activity)\n }\n return Math.max(0, (info.turns ?? 0) - mark.turns)\n}\n","export const PROTOCOL_VERSION = 1\n\nexport type SessionStatus = 'starting' | 'running' | 'awaiting_approval' | 'idle' | 'parked' | 'failed' | 'closed'\n\nexport type PermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan' | 'dontAsk' | 'auto'\n\nexport type TextBlock = { type: 'text'; text: string }\nexport type ThinkingBlock = { type: 'thinking'; thinking: string }\nexport type ToolUseBlock = { type: 'tool_use'; id: string; name: string; input: unknown }\nexport type ToolResultBlock = {\n type: 'tool_result'\n tool_use_id: string\n content?: string | Array<{ type: string; text?: string; [key: string]: unknown }>\n is_error?: boolean\n truncated?: boolean\n total_chars?: number\n}\n\nexport const TOOL_RESULT_HEAD_CHARS = 8_000\n\nexport type ImageRefPart = {\n type: 'image_ref'\n media_type: string\n bytes: number\n part_index: number\n}\n\nfunction base64Bytes(data: string): number {\n const padding = data.endsWith('==') ? 2 : data.endsWith('=') ? 1 : 0\n return Math.max(0, Math.floor((data.length * 3) / 4) - padding)\n}\n\nexport function imagePartRef(part: { type?: string; [key: string]: unknown }, index: number): ImageRefPart | undefined {\n if (part.type !== 'image') {\n return undefined\n }\n const source = part.source as { type?: string; data?: unknown; media_type?: unknown } | undefined\n if (!source || source.type !== 'base64' || typeof source.data !== 'string') {\n return undefined\n }\n return {\n type: 'image_ref',\n media_type: typeof source.media_type === 'string' ? source.media_type : 'application/octet-stream',\n bytes: base64Bytes(source.data),\n part_index: index,\n }\n}\n\nexport type UnknownBlock = { type: string; [key: string]: unknown }\n\nexport type PatchHunk = {\n oldStart: number\n oldLines: number\n newStart: number\n newLines: number\n lines: string[]\n}\n\nexport type FilePatch = {\n path?: string\n kind?: 'create' | 'update'\n hunks: PatchHunk[]\n truncated?: boolean\n}\n\nexport type ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock | UnknownBlock\n\nexport type MessageAttachment = {\n id: string\n name: string\n mediaType: string\n bytes: number\n}\n\nexport type ApiMessage = {\n role: 'user' | 'assistant'\n content: string | ContentBlock[]\n model?: string\n stop_reason?: string | null\n usage?: {\n input_tokens?: number\n output_tokens?: number\n cache_creation_input_tokens?: number\n cache_read_input_tokens?: number\n }\n}\n\nexport type PermissionRequest = {\n id: string\n toolName: string\n input: Record<string, unknown>\n toolUseId: string\n title?: string\n displayName?: string\n description?: string\n decisionReason?: string\n agentId?: string\n expiresAt?: number\n}\n\nexport type PermissionDecisionSource = 'client' | 'timeout' | 'policy'\n\nexport type UserQuestionOption = {\n label: string\n description?: string\n preview?: string\n}\n\nexport type UserQuestion = {\n question: string\n header: string\n options: UserQuestionOption[]\n multiSelect?: boolean\n}\n\nexport type QuestionBehavior = 'ask' | 'auto' | 'deny'\n\nexport function parseUserQuestions(input: Record<string, unknown>): UserQuestion[] {\n const raw = Array.isArray(input.questions) ? input.questions : []\n return raw.flatMap((entry): UserQuestion[] => {\n const q = entry as Partial<UserQuestion>\n if (typeof q.question !== 'string' || !Array.isArray(q.options)) {\n return []\n }\n const options = q.options.filter((o): o is UserQuestionOption => typeof (o as UserQuestionOption | undefined)?.label === 'string')\n if (options.length === 0) {\n return []\n }\n return [\n {\n question: q.question,\n header: typeof q.header === 'string' ? q.header : '',\n options,\n multiSelect: q.multiSelect === true,\n },\n ]\n })\n}\n\nexport type ModelOption = {\n value: string\n resolvedModel?: string\n displayName: string\n description?: string\n primary?: boolean\n reasoningEfforts?: readonly string[]\n}\n\nexport type SkillInfo = {\n name: string\n description?: string\n shortDescription?: string\n displayName?: string\n defaultPrompt?: string\n scope?: string\n enabled: boolean\n}\n\nexport type SlashCommandInfo = {\n name: string\n description?: string\n argumentHint?: string\n aliases?: string[]\n}\n\nexport type ContextUsageCategory = {\n name: string\n tokens: number\n color: string\n}\n\nexport type ContextUsage = {\n categories: ContextUsageCategory[]\n totalTokens: number\n maxTokens: number\n percentage: number\n model?: string\n}\n\nexport type ContextReading = {\n totalTokens: number\n maxTokens: number\n percentage: number\n}\n\nexport type RateLimitInfo = {\n status: string\n rateLimitType?: string\n utilization?: number\n resetsAt?: number\n isUsingOverage?: boolean\n}\n\nexport type ToolExecutionStatus = 'pending' | 'deferred' | 'settled' | 'failed'\n\nexport type ToolExecutionBackend = 'server' | 'browser' | 'managed' | 'remote'\n\nexport type ToolExecutionOutput = { type: 'text'; value: string } | { type: 'json'; value: unknown }\n\nexport type SessionEventBody =\n | {\n type: 'system_init'\n sdkSessionId: string\n model: string\n cwd: string\n apiKeySource: string\n tools: string[]\n skills: string[]\n slashCommands: string[]\n permissionMode: PermissionMode\n claudeCodeVersion: string\n mcpServers: Array<{ name: string; status: string }>\n }\n | { type: 'status_changed'; status: SessionStatus; detail?: string }\n | {\n type: 'capabilities'\n models: ModelOption[]\n commands: SlashCommandInfo[]\n defaultModel?: string\n }\n | { type: 'skills'; skills: SkillInfo[] }\n | { type: 'tool_titles'; titles: Record<string, string> }\n | { type: 'checklist'; items: ChecklistItem[] }\n | {\n type: 'file_produced'\n fileId: string\n path: string\n mediaType?: string\n bytes?: number\n toolUseId?: string\n }\n | { type: 'model_changed'; model?: string }\n | { type: 'permission_mode_changed'; mode: PermissionMode }\n | { type: 'context_usage'; usage: ContextUsage }\n | { type: 'rate_limit'; info: RateLimitInfo }\n | { type: 'plan_info'; subscriptionType: string }\n | {\n type: 'conversation_reset'\n sdkSessionId?: string\n }\n | {\n type: 'context_compacted'\n uuid: string\n parentToolUseId?: string | null\n // The same `uuid` is emitted twice: once with `pending` when the engine starts summarising,\n // once without it when the boundary lands, so one row settles in place. A reader that\n // never sees the first (an older gateway, a replay coalesced to the last) draws the\n // boundary alone, which is what it drew before.\n pending?: boolean\n trigger?: 'manual' | 'auto'\n preTokens?: number\n postTokens?: number\n error?: string\n }\n | {\n type: 'assistant_message'\n message: ApiMessage\n parentToolUseId: string | null\n replay?: boolean\n uuid: string\n }\n | {\n type: 'user_message'\n message: ApiMessage\n parentToolUseId: string | null\n replay?: boolean\n synthetic?: boolean\n attachments?: MessageAttachment[]\n patch?: FilePatch\n uuid?: string\n }\n | {\n type: 'stream_delta'\n event: { type: string; [key: string]: unknown }\n parentToolUseId: string | null\n uuid: string\n }\n | {\n type: 'turn_result'\n subtype: 'success' | 'error_during_execution' | 'error_max_turns' | 'error_max_budget_usd' | 'error_max_structured_output_retries'\n isError: boolean\n durationMs: number\n numTurns: number\n totalCostUsd: number\n result?: string\n errors?: string[]\n usage?: unknown\n }\n | { type: 'permission_requested'; request: PermissionRequest }\n | {\n type: 'permission_resolved'\n requestId: string\n behavior: 'allow' | 'deny'\n resolvedBy: PermissionDecisionSource\n message?: string\n }\n | {\n type: 'execution_dispatched'\n executionId: string\n toolName: string\n backend: ToolExecutionBackend\n deferred?: boolean\n expiresAt?: number\n }\n | {\n type: 'execution_result'\n executionId: string\n output: ToolExecutionOutput\n logs?: string[]\n durationMs?: number\n }\n | {\n type: 'execution_failed'\n executionId: string\n reason: string\n error: string\n logs?: string[]\n durationMs?: number\n }\n | { type: 'file_delivered'; path: string; bytes: number; description?: string }\n | { type: 'sdk_event'; payload: { type: string; [key: string]: unknown } }\n | { type: 'session_error'; message: string }\n | { type: 'session_closed'; reason: 'client' | 'server' | 'error' }\n\nexport type SessionEvent = SessionEventBody & {\n seq: number\n ts: number\n}\n\nexport type SessionCommand =\n | {\n type: 'user_message'\n text: string\n attachmentIds?: string[]\n }\n | {\n type: 'permission_decision'\n requestId: string\n behavior: 'allow' | 'deny'\n updatedInput?: Record<string, unknown>\n message?: string\n interrupt?: boolean\n }\n | { type: 'interrupt' }\n | { type: 'clear_context' }\n | { type: 'set_permission_mode'; mode: PermissionMode }\n | { type: 'set_model'; model?: string }\n | {\n type: 'tool_call_result'\n executionId: string\n output: ToolExecutionOutput\n logs?: string[]\n }\n | {\n type: 'tool_call_error'\n executionId: string\n reason: string\n error: string\n logs?: string[]\n }\n | { type: 'shell_command'; command: string }\n | { type: 'close' }\n\nexport const SHELL_COMMAND_MAX = 4000\n\nexport type AttachedFrame = {\n type: 'attached'\n protocolVersion: number\n session: SessionInfo\n replayingFrom: number\n // Whether this principal may run `!` commands on this session: server config x operator x hostCwd.\n // Additive, so no PROTOCOL_VERSION bump; an older gateway omits it and the client never offers the mode.\n shell?: boolean\n}\n\nexport type ToolCallRequestFrame = {\n type: 'tool_call_request'\n executionId: string\n toolName: string\n input: unknown\n vfsSeed?: Record<string, string>\n limits?: { timeoutMs?: number; memoryLimitBytes?: number }\n expiresAt?: number\n}\n\nexport type ServerFrame =\n | AttachedFrame\n | { type: 'event'; event: SessionEvent }\n | ToolCallRequestFrame\n | { type: 'tool_call_canceled'; executionId: string; reason: string }\n | { type: 'protocol_error'; message: string }\n\nexport type ClientFrame = SessionCommand\n\nexport type ProfileDefaults = {\n model?: string\n permissionMode?: PermissionMode\n}\n\nexport type ProfileEngine = 'claude' | 'codex' | 'provider'\n\nexport type EngineCapabilities = {\n interactiveApprovals: boolean\n permissionModes: readonly PermissionMode[]\n defaultPermissionMode: PermissionMode\n resume: boolean\n resumeBackfill: boolean\n listSessions: boolean\n contextUsage: boolean\n rateLimits: boolean\n mcpStatus: boolean\n mcpServerActions: boolean\n sessionMcpServers: boolean\n slashCommands: boolean\n clearContext?: boolean\n skillsList: boolean\n settingSources: boolean\n budgets: boolean\n attachments: ReadonlyArray<'image' | 'pdf' | 'text'>\n reasoningEfforts?: readonly string[]\n vfs: boolean\n hostCwd?: boolean\n streaming: 'token' | 'item' | 'none'\n}\n\nexport const ENGINE_CAPABILITIES: Record<ProfileEngine, EngineCapabilities> = {\n claude: {\n interactiveApprovals: true,\n permissionModes: ['default', 'acceptEdits', 'bypassPermissions', 'plan', 'dontAsk', 'auto'],\n defaultPermissionMode: 'default',\n resume: true,\n resumeBackfill: true,\n listSessions: true,\n contextUsage: true,\n rateLimits: true,\n mcpStatus: true,\n mcpServerActions: true,\n sessionMcpServers: true,\n slashCommands: true,\n clearContext: true,\n skillsList: false,\n settingSources: true,\n budgets: true,\n attachments: ['image', 'pdf', 'text'],\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],\n vfs: false,\n hostCwd: true,\n streaming: 'token',\n },\n codex: {\n interactiveApprovals: true,\n permissionModes: ['default', 'acceptEdits', 'bypassPermissions', 'auto'],\n defaultPermissionMode: 'default',\n resume: true,\n resumeBackfill: true,\n listSessions: true,\n contextUsage: true,\n rateLimits: true,\n mcpStatus: true,\n mcpServerActions: false,\n sessionMcpServers: false,\n slashCommands: false,\n clearContext: true,\n skillsList: true,\n settingSources: false,\n budgets: false,\n attachments: ['image', 'text'],\n reasoningEfforts: ['minimal', 'low', 'medium', 'high', 'xhigh'],\n vfs: false,\n hostCwd: true,\n streaming: 'token',\n },\n provider: {\n interactiveApprovals: false,\n permissionModes: ['default', 'bypassPermissions', 'dontAsk'],\n defaultPermissionMode: 'default',\n resume: false,\n resumeBackfill: false,\n listSessions: false,\n contextUsage: false,\n rateLimits: false,\n mcpStatus: true,\n mcpServerActions: false,\n sessionMcpServers: false,\n slashCommands: false,\n clearContext: true,\n skillsList: false,\n settingSources: false,\n budgets: false,\n attachments: ['image', 'pdf', 'text'],\n vfs: true,\n hostCwd: false,\n streaming: 'token',\n },\n}\n\nexport function supportsPermissionMode(engine: ProfileEngine | undefined, mode: PermissionMode): boolean {\n return ENGINE_CAPABILITIES[engine ?? 'claude'].permissionModes.includes(mode)\n}\n\nexport type ProviderConfig = {\n id: string\n model?: string\n models?: string[]\n baseUrl?: string\n apiKeyEnv?: string\n}\n\nexport type SessionCapability = 'web_search' | 'download' | 'web_fetch' | 'deliver_file'\n\nexport type ProfileSessionDefaults = {\n capabilities?: SessionCapability[]\n mcpServers?: string[]\n instructions?: string\n}\n\nexport type ProfileUsageWindow = {\n info: RateLimitInfo\n updatedAt: number\n inferredReset?: boolean\n}\n\nexport type ProfileUsage = Record<string, ProfileUsageWindow>\n\nexport type ProfileInfo = {\n name: string\n engine?: ProfileEngine\n configDir?: string\n codexHome?: string\n provider?: ProviderConfig\n description?: string\n defaults?: ProfileDefaults\n session?: ProfileSessionDefaults\n models?: ModelOption[]\n defaultModel?: string\n capabilities?: EngineCapabilities\n available?: boolean\n unavailableReason?: string\n usage?: ProfileUsage\n managed?: boolean\n}\n\nexport type ProfileConfigSnapshot = {\n settings?: {\n model?: string\n defaultPermissionMode?: string\n permissionRules?: { allow: number; ask: number; deny: number }\n envKeys?: string[]\n hooks?: string[]\n }\n hasUserMemory: boolean\n skills: string[]\n agents: string[]\n commands: string[]\n}\n\nexport type McpServerConfigWire =\n | { type?: 'stdio'; command: string; args?: string[]; env?: Record<string, string> }\n | { type: 'http'; url: string; headers?: Record<string, string> }\n | { type: 'sse'; url: string; headers?: Record<string, string> }\n\nexport type McpServerToolInfo = {\n name: string\n title?: string\n description?: string\n annotations?: { readOnly?: boolean; destructive?: boolean; openWorld?: boolean }\n inputSchema?: unknown\n}\n\nexport type McpServerStatusInfo = {\n name: string\n status: string\n scope?: string\n error?: string\n serverInfo?: { name: string; version: string }\n transport?: 'stdio' | 'http' | 'sse' | 'sdk'\n command?: string\n args?: string[]\n url?: string\n tools?: McpServerToolInfo[]\n}\n\nexport type McpServersResponse = { servers: McpServerStatusInfo[] }\n\nexport type McpServerActionRequest = { action: 'reconnect' | 'enable' | 'disable' }\n\nexport type UploadAttachmentResponse = { attachment: MessageAttachment }\n\nexport type CreateSessionRequest = {\n cwd?: string\n profile?: string\n prompt?: string\n permissionMode?: PermissionMode\n allowDangerouslySkipPermissions?: boolean\n allowedTools?: string[]\n disallowedTools?: string[]\n mcpServers?: Record<string, McpServerConfigWire>\n settingSources?: Array<'user' | 'project' | 'local'>\n model?: string\n maxTurns?: number\n maxBudgetUsd?: number\n resume?: string\n forkSession?: boolean\n reasoningEffort?: string\n includePartialMessages?: boolean\n // Overrides the gateway's default. null (or 0) means a prompt never expires - it waits as long as the session lives.\n approvalTimeoutMs?: number | null\n questionBehavior?: QuestionBehavior\n capabilities?: SessionCapability[]\n meta?: Record<string, unknown>\n scope?: Record<string, string>\n}\n\nexport type SubagentInfo = {\n toolUseId: string\n agentType?: string\n description?: string\n status: 'running' | 'done' | 'failed'\n startedAt: number\n toolCount: number\n}\n\nexport const SUBAGENT_HISTORY = 8\n\n// Widening this union costs a PROTOCOL_VERSION bump: Swift decodes it strictly.\nexport type ChecklistStatus = 'pending' | 'in_progress' | 'completed'\n\nexport type ChecklistItem = { text: string; status: ChecklistStatus }\n\nexport const CHECKLIST_TEXT_MAX = 200\n\nexport type ProjectIcon = { type: 'glyph'; name: string } | { type: 'image'; mediaType: 'image/png' | 'image/svg+xml'; hash: string }\n\nexport type ProjectInfo = {\n name: string\n root: string\n icon?: ProjectIcon\n}\n\nexport type SessionInfo = {\n id: string\n sdkSessionId?: string\n status: SessionStatus\n cwd: string\n profile?: string\n engine?: ProfileEngine\n capabilities?: EngineCapabilities\n model?: string\n permissionMode?: PermissionMode\n canBypassPermissions?: boolean\n apiKeySource?: string\n createdAt: number\n lastSeq: number\n // Which log the seqs belong to. A dormant wake starts a fresh log and bumps it, so a `seq`\n // that outlived one - a notification sitting on a lock screen - can be told from a live one.\n // Absent from a gateway that predates it, and from a session that has never woken; additive,\n // so no `PROTOCOL_VERSION` bump, and a reader that finds it absent on either side must keep\n // trusting the seq.\n epoch?: number\n pendingPermissionCount: number\n subagents?: SubagentInfo[]\n checklist?: ChecklistItem[]\n meta?: Record<string, unknown>\n title?: string\n totalCostUsd?: number\n numTurns?: number\n activityCount?: number\n // Rows of the kind a person is actually waiting to read - see `transcriptProse`.\n // Absent from a gateway that predates it - additive, so no `PROTOCOL_VERSION` bump -\n // which is why every reader falls back to `activityCount`. This is the badge's number; `activityCount` stays the \"has\n // anything happened at all\" measure that sorting and dormancy read.\n proseCount?: number\n lastActivityAt?: number\n contextUsage?: ContextReading\n scope?: Record<string, string>\n project?: ProjectInfo\n}\n\nexport function contextReading(body: SessionEventBody): ContextReading | undefined {\n if (body.type !== 'context_usage') {\n return undefined\n }\n const { totalTokens, maxTokens, percentage } = body.usage\n return { totalTokens, maxTokens, percentage }\n}\n\nexport function transcriptActivity(body: SessionEventBody): number {\n if ('parentToolUseId' in body && body.parentToolUseId != null) {\n return 0\n }\n switch (body.type) {\n case 'assistant_message': {\n const content = body.message.content\n if (typeof content === 'string') {\n return content.trim() === '' ? 0 : 1\n }\n const rows = content.filter((block) => block.type === 'text' || block.type === 'thinking' || block.type === 'tool_use').length\n return rows\n }\n case 'user_message': {\n return body.synthetic ? 0 : 1\n }\n case 'turn_result':\n case 'file_delivered':\n case 'session_error': {\n return 1\n }\n default: {\n return 0\n }\n }\n}\n\n// The narrower door beside transcriptActivity: output addressed to the human, not evidence of work.\nexport function transcriptProse(body: SessionEventBody): number {\n if ('parentToolUseId' in body && body.parentToolUseId != null) {\n return 0\n }\n switch (body.type) {\n case 'assistant_message': {\n const content = body.message.content\n if (typeof content === 'string') {\n return content.trim() === '' ? 0 : 1\n }\n return content.filter((block) => block.type === 'text' && typeof block.text === 'string' && block.text.trim() !== '').length\n }\n case 'user_message': {\n return body.synthetic ? 0 : 1\n }\n case 'turn_result': {\n return body.isError ? 1 : 0\n }\n case 'file_delivered':\n case 'session_error': {\n return 1\n }\n default: {\n return 0\n }\n }\n}\n\nexport function transcriptContent(body: SessionEventBody): boolean {\n switch (body.type) {\n case 'user_message':\n case 'assistant_message':\n case 'stream_delta':\n case 'turn_result':\n case 'execution_dispatched':\n case 'execution_result':\n case 'execution_failed':\n case 'file_delivered':\n case 'session_error':\n case 'session_closed':\n case 'conversation_reset':\n case 'context_compacted': {\n return true\n }\n default: {\n return false\n }\n }\n}\n\nexport function replayCoalesceKey(body: SessionEventBody): string | undefined {\n switch (body.type) {\n case 'context_usage': {\n return 'context_usage'\n }\n case 'rate_limit': {\n return body.info.rateLimitType ? `rate_limit:${body.info.rateLimitType}` : undefined\n }\n case 'status_changed': {\n return 'status_changed'\n }\n case 'context_compacted': {\n return `context_compacted:${body.uuid}`\n }\n case 'checklist': {\n return 'checklist'\n }\n case 'sdk_event': {\n return body.payload.type === 'system' && body.payload.subtype === 'status' ? 'sdk_event:system:status' : undefined\n }\n default: {\n return undefined\n }\n }\n}\n\nexport function replayRetains(body: SessionEventBody): boolean {\n if (body.type !== 'stream_delta') {\n return true\n }\n const delta = body.event as { type?: string; delta?: { type?: string } }\n if (delta.type !== 'content_block_delta') {\n return false\n }\n return delta.delta?.type === 'text_delta' || delta.delta?.type === 'thinking_delta'\n}\n\nexport function snapshotRetains(body: SessionEventBody): boolean {\n return body.type !== 'stream_delta'\n}\n\nexport type SdkSessionSummary = {\n sessionId: string\n summary: string\n lastModified: number\n createdAt?: number\n customTitle?: string\n firstPrompt?: string\n gitBranch?: string\n cwd?: string\n}\n\nexport type SessionFileInfo = { path: string; bytes: number }\nexport type ListSessionFilesResponse = { files: SessionFileInfo[] }\nexport type ListSessionsResponse = { sessions: SessionInfo[] }\nexport type CreateSessionResponse = { session: SessionInfo }\nexport type GetSessionResponse = { session: SessionInfo }\n\nexport type UpdateSessionRequest = { title?: string | null }\nexport type UpdateSessionResponse = { session: SessionInfo }\n\nexport type ResolvePermissionRequest =\n | { behavior: 'allow'; updatedInput?: Record<string, unknown> }\n | { behavior: 'deny'; message?: string; interrupt?: boolean }\nexport type ResolvePermissionResponse = { resolved: true }\n\nexport type SubmitExecutionResultRequest =\n | { status: 'ok'; output: ToolExecutionOutput; logs?: string[] }\n | { status: 'failed'; reason: string; error: string; logs?: string[] }\nexport type SubmitExecutionResultResponse = {\n applied: boolean\n sessionId: string\n}\n\nexport type ListSdkSessionsResponse = { sdkSessions: SdkSessionSummary[] }\nexport type ListProfilesResponse = {\n profiles: ProfileInfo[]\n canManage?: boolean\n}\n\nexport type CreateProfileRequest = ProfileInfo\n\nexport type UpdateProfileRequest = Omit<Partial<ProfileInfo>, 'name'>\n\nexport type HostFileRoot = {\n path: string\n name: string\n}\n\nexport type ListHostRootsResponse = {\n roots: HostFileRoot[]\n canWrite: boolean\n}\n\nexport type HostDirEntry = {\n name: string\n path: string\n type: 'file' | 'dir' | 'symlink' | 'other'\n bytes?: number\n modifiedAt?: number\n}\n\nexport type ListHostDirResponse = {\n path: string\n entries: HostDirEntry[]\n truncated?: boolean\n}\n\nexport type HostFileMatch = {\n path: string\n relative: string\n}\n\nexport type FindHostFilesResponse = {\n base: string\n matches: HostFileMatch[]\n truncated: boolean\n}\n\nexport type ReadHostFileResponse = {\n path: string\n content: string\n encoding: 'utf8' | 'base64'\n bytes: number\n hash: string\n modifiedAt: number\n}\n\nexport type WriteHostFileRequest = {\n path: string\n content: string\n encoding?: 'utf8' | 'base64'\n expectedHash?: string\n}\n\nexport type WriteHostFileResponse = {\n path: string\n bytes: number\n hash: string\n modifiedAt: number\n}\n\nexport type SaveProfileResponse = { profile: ProfileInfo }\nexport type GetProfileResponse = { profile: ProfileInfo; config: ProfileConfigSnapshot }\nexport type ErrorResponse = { error: string }\n\nexport type SessionNotificationType = 'permission_requested' | 'turn_completed' | 'session_error' | 'session_closed'\n\nexport type SessionNotification = {\n type: SessionNotificationType\n sessionId: string\n session: SessionInfo\n seq: number\n ts: number\n preview?: string\n request?: PermissionRequest\n result?: { isError: boolean; durationMs: number; numTurns: number; totalCostUsd: number }\n reason?: 'client' | 'server' | 'error'\n}\n\nexport type SessionWebhookConfig = {\n url: string\n headers?: Record<string, string>\n events?: SessionNotificationType[]\n}\n\nexport type JobStatus = 'queued' | 'running' | 'parked' | 'succeeded' | 'failed' | 'canceled'\n\nexport type WebhookConfig = {\n url: string\n headers?: Record<string, string>\n progress?: 'messages' | 'completion'\n}\n\nexport type CreateJobRequest = {\n session: CreateSessionRequest & { prompt: string }\n webhook?: WebhookConfig\n maxTokens?: number\n maxDurationMs?: number\n attempts?: number\n retryDelayMs?: number\n meta?: Record<string, unknown>\n}\n\nexport type JobUsage = {\n tokens: number\n totalCostUsd: number\n numTurns: number\n}\n\nexport type JobResult = {\n subtype: string\n isError: boolean\n result?: string\n errors?: string[]\n durationMs: number\n}\n\nexport type JobInfo = {\n id: string\n status: JobStatus\n cwd: string\n profile?: string\n prompt: string\n sessionId?: string\n sdkSessionId?: string\n createdAt: number\n startedAt?: number\n finishedAt?: number\n attempt?: number\n maxAttempts?: number\n nextRunAt?: number\n parkedAt?: number\n parkedExecutionId?: string\n usage: JobUsage\n result?: JobResult\n error?: string\n meta?: Record<string, unknown>\n scope?: Record<string, string>\n}\n\nexport type JobProgress = {\n kind: 'assistant_text' | 'tool_use' | 'permission_requested' | 'permission_resolved'\n preview?: string\n request?: PermissionRequest\n}\n\nexport type JobEvent =\n | { type: 'job_submitted'; job: JobInfo; ts: number }\n | { type: 'job_started'; job: JobInfo; ts: number }\n | { type: 'job_progress'; job: JobInfo; progress: JobProgress; ts: number }\n | { type: 'job_parked'; job: JobInfo; executionId: string; ts: number }\n | { type: 'job_resumed'; job: JobInfo; executionId: string; ts: number }\n | { type: 'job_retrying'; job: JobInfo; ts: number }\n | { type: 'job_completed'; job: JobInfo; ts: number }\n\nexport type QueueStats = {\n maxConcurrency: number\n running: number\n queued: number\n parked: number\n sessionTokenLimit?: number\n dailyTokenLimit?: number\n dailyTokensUsed: number\n paused: boolean\n}\n\nexport type QueueServerFrame =\n | { type: 'queue_attached'; protocolVersion: number; stats: QueueStats }\n | { type: 'job_event'; event: JobEvent }\n | { type: 'queue_stats'; stats: QueueStats }\n\nexport type CreateJobResponse = { job: JobInfo }\nexport type GetJobResponse = { job: JobInfo }\nexport type ListJobsResponse = { jobs: JobInfo[] }\nexport type QueueStatsResponse = { stats: QueueStats }\n\nexport * from './checklist.ts'\nexport * from './session-list.ts'\nexport * from './tool-titles.ts'\nexport * from './usage.ts'\nexport * from './watermarks.ts'\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workerdeck/protocol",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "The WorkerDeck wire protocol: typed session events, commands, and REST shapes shared by server and clients. Dependency-free, browser-safe. This protocol is the product boundary
|
|
5
|
+
"description": "The WorkerDeck wire protocol: typed session events, commands, and REST shapes shared by server and clients. Dependency-free, browser-safe. This protocol is the product boundary - versioned from day one.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./build/index.mjs",
|
|
8
8
|
"types": "./build/index.d.mts",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@types/node": "^26.
|
|
20
|
+
"@types/node": "^26.6.1",
|
|
21
21
|
"rimraf": "^6.1.3",
|
|
22
22
|
"tsdown": "^0.22.14"
|
|
23
23
|
},
|
|
@@ -45,6 +45,6 @@
|
|
|
45
45
|
"clean": "rimraf build",
|
|
46
46
|
"build": "tsdown",
|
|
47
47
|
"typecheck": "tsgo -p tsconfig.json",
|
|
48
|
-
"test": "echo \"@workerdeck/protocol has no tests of its own
|
|
48
|
+
"test": "echo \"@workerdeck/protocol has no tests of its own - its rules (transcriptActivity, replayCoalesceKey, snapshotRetains/replayRetains, result truncation, image refs) are proven against real consumers in packages/react/test and packages/core/test.\""
|
|
49
49
|
}
|
|
50
50
|
}
|