@workerdeck/protocol 1.1.0 → 1.2.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/build/index.d.mts CHANGED
@@ -338,6 +338,27 @@ type SessionEventBody = {
338
338
  } | {
339
339
  type: 'conversation_reset';
340
340
  sdkSessionId?: string;
341
+ } |
342
+ /**
343
+ * The engine summarised the conversation to fit its context window, in place, mid-session.
344
+ *
345
+ * **Not a `conversation_reset`.** A reset empties the history and its replay rule skips
346
+ * everything below it; a compaction *preserves* the history it summarised, so applying the
347
+ * reset's rule here would throw away exactly what compaction kept. It is a marker at a point
348
+ * in the transcript, and nothing before it is retracted.
349
+ *
350
+ * It is also the missing half of the context ring: occupancy drops sharply for a reason the
351
+ * user did not cause, and a ring that falls from 90% to 15% with no marker reads as a bug.
352
+ *
353
+ * Engine-neutral by design — codex auto-compacts today and the Claude engine may emit this
354
+ * later; a second event type would make every client draw the same row twice.
355
+ */
356
+ {
357
+ type: 'context_compacted';
358
+ /** Stable transcript id — the row is addressable by bookmarks and the scrubber like any other. */
359
+ uuid: string;
360
+ /** Set when a sub-agent's own context was compacted, so the row nests where its work does. */
361
+ parentToolUseId?: string | null;
341
362
  } | {
342
363
  type: 'assistant_message';
343
364
  message: ApiMessage;
package/build/index.mjs CHANGED
@@ -447,7 +447,8 @@ function transcriptContent(body) {
447
447
  case "file_delivered":
448
448
  case "session_error":
449
449
  case "session_closed":
450
- case "conversation_reset": return true;
450
+ case "conversation_reset":
451
+ case "context_compacted": return true;
451
452
  default: return false;
452
453
  }
453
454
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/session-list.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 /**\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 */\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 { 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 /**\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 */\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/**\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.\n */\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 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 | {\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: '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: 'close' }\n\nexport type AttachedFrame = {\n type: 'attached'\n protocolVersion: number\n session: SessionInfo\n replayingFrom: number\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 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 approvalTimeoutMs?: number\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\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 pendingPermissionCount: number\n subagents?: SubagentInfo[]\n meta?: Record<string, unknown>\n title?: string\n totalCostUsd?: number\n numTurns?: number\n activityCount?: number\n /**\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 */\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/**\n * The unread badge's unit: output **addressed to the human**, not evidence of work.\n *\n * `transcriptActivity` counts a tool call and a paragraph alike, which is honest as\n * \"how much has happened\" and wrong as \"how much is there to read\" — a session that\n * tool-loops for a minute ticks 6, 7, 8 with nothing said yet. This scores the same\n * events through a narrower door:\n *\n * - assistant `text` blocks only — `thinking` is not addressed to anyone and `tool_use`\n * is the noise being filtered out;\n * - the **sub-agent carve-out is inherited** (`parentToolUseId != null` scores 0): prose a\n * sub-agent wrote to its parent is not addressed to the human either;\n * - a `turn_result` counts only when it **failed**, an interrupt or an error being a thing\n * the human is owed; a successful turn already carried its own prose and would otherwise\n * double-count every answer;\n * - `session_error` and `file_delivered` count — both are output, not work;\n * - `stream_delta` scores 0, exactly as in `transcriptActivity`. The badge is therefore\n * correct within one poll of a message *completing*, never mid-stream, which is the\n * deliberate price of leaving the streaming path alone.\n */\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 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 '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 './session-list.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/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 /**\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 */\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 { 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 /**\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 */\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/**\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.\n */\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 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 | {\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 * The engine summarised the conversation to fit its context window, in place, mid-session.\n *\n * **Not a `conversation_reset`.** A reset empties the history and its replay rule skips\n * everything below it; a compaction *preserves* the history it summarised, so applying the\n * reset's rule here would throw away exactly what compaction kept. It is a marker at a point\n * in the transcript, and nothing before it is retracted.\n *\n * It is also the missing half of the context ring: occupancy drops sharply for a reason the\n * user did not cause, and a ring that falls from 90% to 15% with no marker reads as a bug.\n *\n * Engine-neutral by design — codex auto-compacts today and the Claude engine may emit this\n * later; a second event type would make every client draw the same row twice.\n */\n | {\n type: 'context_compacted'\n /** Stable transcript id — the row is addressable by bookmarks and the scrubber like any other. */\n uuid: string\n /** Set when a sub-agent's own context was compacted, so the row nests where its work does. */\n parentToolUseId?: string | null\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: 'close' }\n\nexport type AttachedFrame = {\n type: 'attached'\n protocolVersion: number\n session: SessionInfo\n replayingFrom: number\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 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 approvalTimeoutMs?: number\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\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 pendingPermissionCount: number\n subagents?: SubagentInfo[]\n meta?: Record<string, unknown>\n title?: string\n totalCostUsd?: number\n numTurns?: number\n activityCount?: number\n /**\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 */\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/**\n * The unread badge's unit: output **addressed to the human**, not evidence of work.\n *\n * `transcriptActivity` counts a tool call and a paragraph alike, which is honest as\n * \"how much has happened\" and wrong as \"how much is there to read\" — a session that\n * tool-loops for a minute ticks 6, 7, 8 with nothing said yet. This scores the same\n * events through a narrower door:\n *\n * - assistant `text` blocks only — `thinking` is not addressed to anyone and `tool_use`\n * is the noise being filtered out;\n * - the **sub-agent carve-out is inherited** (`parentToolUseId != null` scores 0): prose a\n * sub-agent wrote to its parent is not addressed to the human either;\n * - a `turn_result` counts only when it **failed**, an interrupt or an error being a thing\n * the human is owed; a successful turn already carried its own prose and would otherwise\n * double-count every answer;\n * - `session_error` and `file_delivered` count — both are output, not work;\n * - `stream_delta` scores 0, exactly as in `transcriptActivity`. The badge is therefore\n * correct within one poll of a message *completing*, never mid-stream, which is the\n * deliberate price of leaving the streaming path alone.\n */\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 '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 './session-list.ts'\nexport * from './usage.ts'\nexport * from './watermarks.ts'\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workerdeck/protocol",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
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",