@pat-lewczuk/cezar 0.1.3 → 0.1.4
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/dist/core/claude-ui-mapper.d.ts +57 -0
- package/dist/core/claude-ui-mapper.js +370 -0
- package/dist/core/claude-ui-mapper.js.map +1 -0
- package/dist/core/codex-ui-mapper.d.ts +53 -0
- package/dist/core/codex-ui-mapper.js +411 -0
- package/dist/core/codex-ui-mapper.js.map +1 -0
- package/dist/core/opencode-ui-mapper.d.ts +105 -0
- package/dist/core/opencode-ui-mapper.js +578 -0
- package/dist/core/opencode-ui-mapper.js.map +1 -0
- package/dist/core/process-usage.d.ts +63 -0
- package/dist/core/process-usage.js +181 -0
- package/dist/core/process-usage.js.map +1 -0
- package/dist/core/tool-display.d.ts +27 -0
- package/dist/core/tool-display.js +150 -0
- package/dist/core/tool-display.js.map +1 -0
- package/dist/core/ui-events.d.ts +294 -0
- package/dist/core/ui-events.js +25 -0
- package/dist/core/ui-events.js.map +1 -0
- package/dist/index.js +0 -0
- package/dist/pack-check.d.ts +17 -0
- package/dist/pack-check.js +27 -0
- package/dist/pack-check.js.map +1 -0
- package/dist/runs/title-summary.d.ts +15 -0
- package/dist/runs/title-summary.js +84 -0
- package/dist/runs/title-summary.js.map +1 -0
- package/dist/runs/ui-event-sink.d.ts +81 -0
- package/dist/runs/ui-event-sink.js +210 -0
- package/dist/runs/ui-event-sink.js.map +1 -0
- package/dist/server/capabilities.d.ts +18 -0
- package/dist/server/capabilities.js +23 -0
- package/dist/server/capabilities.js.map +1 -0
- package/dist/server/forge/github.d.ts +39 -0
- package/dist/server/forge/github.js +369 -0
- package/dist/server/forge/github.js.map +1 -0
- package/dist/server/forge/index.d.ts +24 -0
- package/dist/server/forge/index.js +45 -0
- package/dist/server/forge/index.js.map +1 -0
- package/dist/server/forge/types.d.ts +80 -0
- package/dist/server/forge/types.js +2 -0
- package/dist/server/forge/types.js.map +1 -0
- package/dist/server/git-changes.d.ts +182 -0
- package/dist/server/git-changes.js +424 -0
- package/dist/server/git-changes.js.map +1 -0
- package/dist/server/open-in-app.d.ts +19 -0
- package/dist/server/open-in-app.js +134 -0
- package/dist/server/open-in-app.js.map +1 -0
- package/dist/server/static-ui.d.ts +49 -0
- package/dist/server/static-ui.js +92 -0
- package/dist/server/static-ui.js.map +1 -0
- package/dist/update-check.d.ts +12 -0
- package/dist/update-check.js +41 -0
- package/dist/update-check.js.map +1 -0
- package/package.json +1 -1
- package/web/app/node_modules/.vite/vitest/ca84d1343b96baa8137c943ed1860e522cacb238/results.json +1 -0
- package/web/app/src/components/app-shell-container.tsx +76 -0
- package/web/dist/assets/arrow-left-BE957rJs.js +1 -0
- package/web/dist/assets/bundle-mjs-DLPpRFyl.js +1 -0
- package/web/dist/assets/chunk-BO2N2NFS-f5igTtpB.js +131 -0
- package/web/dist/assets/compare-variants-D6fPL9cS.js +1 -0
- package/web/dist/assets/core-BuMqAF_1.js +12 -0
- package/web/dist/assets/css-BsVw1vtW.js +1 -0
- package/web/dist/assets/dialog-BEgEWFDS.js +1 -0
- package/web/dist/assets/diff-view-CctnJHWe.js +4 -0
- package/web/dist/assets/diff-woXpYk--.js +1 -0
- package/web/dist/assets/dist-CW7M_Jhs.js +1 -0
- package/web/dist/assets/ellipsis-vertical-DJPAfccS.js +1 -0
- package/web/dist/assets/engine-javascript-FQvzRocB.js +141 -0
- package/web/dist/assets/folder-RbRLfIX-.js +1 -0
- package/web/dist/assets/git-pull-request-DJkAMT0f.js +1 -0
- package/web/dist/assets/git-toolbar-D8OPPqBP.js +1 -0
- package/web/dist/assets/github-BYIHwIJB.js +1 -0
- package/web/dist/assets/go-rLFTqkRN.js +1 -0
- package/web/dist/assets/highlighted-body-OFNGDK62-DGHHie_q.js +1 -0
- package/web/dist/assets/highlighter-BKVEsACy.js +3 -0
- package/web/dist/assets/html-BY76lXLH.js +1 -0
- package/web/dist/assets/index-9qG98Orw.js +11 -0
- package/web/dist/assets/index-Bz_r8Jt3.css +2 -0
- package/web/dist/assets/input-BRsSo4xH.js +1 -0
- package/web/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/web/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/web/dist/assets/javascript-BgS3c2Ky.js +1 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/web/dist/assets/json-qhed-kSA.js +1 -0
- package/web/dist/assets/jsonc-CYpm1nAK.js +1 -0
- package/web/dist/assets/jsx-Bz0zcwM4.js +1 -0
- package/web/dist/assets/lib-CNGa3Dig.js +1 -0
- package/web/dist/assets/loader-circle-BV3DO9mE.js +1 -0
- package/web/dist/assets/markdown-BYOwaDjH.js +1 -0
- package/web/dist/assets/markdown-CR_HLGTs.js +1 -0
- package/web/dist/assets/mermaid-GHXKKRXX-DM2VsGBG.js +1 -0
- package/web/dist/assets/open-mercato-toBr6SOa.svg +11 -0
- package/web/dist/assets/python-gzcpVVnB.js +1 -0
- package/web/dist/assets/react-dom-Ddtik4qM.js +1 -0
- package/web/dist/assets/refresh-cw-DCa-Rlwz.js +1 -0
- package/web/dist/assets/repo-git-XTqlVOl2.js +1 -0
- package/web/dist/assets/run-diff-C7mfxj7f.js +3 -0
- package/web/dist/assets/run-header-CfVeY3uM.js +1 -0
- package/web/dist/assets/rust-Cfkwpbl8.js +1 -0
- package/web/dist/assets/search-x-CoCyl7zT.js +1 -0
- package/web/dist/assets/shellscript-CLZ0U2zV.js +1 -0
- package/web/dist/assets/skill-detail-C9cniyId.js +1 -0
- package/web/dist/assets/skills-DXgY1uXJ.js +1 -0
- package/web/dist/assets/skills-ynuO5oCr.js +1 -0
- package/web/dist/assets/sql-BsFa4tDR.js +1 -0
- package/web/dist/assets/square-terminal-B6PZX4Qp.js +1 -0
- package/web/dist/assets/tab-link-phWEqSBy.js +1 -0
- package/web/dist/assets/task-changes-DYwnPnTN.js +1 -0
- package/web/dist/assets/task-commits-DZzxEzbn.js +1 -0
- package/web/dist/assets/task-files-FOAAz2FB.js +2 -0
- package/web/dist/assets/task-thread-Bncxe8Os.js +5 -0
- package/web/dist/assets/toaster-BS2E1bhz.js +43 -0
- package/web/dist/assets/trash-2-CDtxmmc8.js +1 -0
- package/web/dist/assets/triangle-alert-6su4Js5O.js +1 -0
- package/web/dist/assets/tsx-udAQXfEw.js +1 -0
- package/web/dist/assets/typescript-bsJCZSQ-.js +1 -0
- package/web/dist/assets/upload-Bp7hQxll.js +1 -0
- package/web/dist/assets/use-desktop-eHm2iTXX.js +3 -0
- package/web/dist/assets/utils-rQGbOrwc.js +1 -0
- package/web/dist/assets/workflows-Dg2wQg6i.js +11 -0
- package/web/dist/assets/x-Br_jIDBw.js +1 -0
- package/web/dist/assets/yaml-rwi0_p6S.js +1 -0
- package/web/dist/index.html +67 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized agent-event protocol v2 — the shared vocabulary every runner
|
|
3
|
+
* emits ALONGSIDE the v1 `AgentEvent` stream (never replacing it; mixed
|
|
4
|
+
* NDJSON files stay valid — old recordings must keep replaying).
|
|
5
|
+
*
|
|
6
|
+
* Contract sources (authoritative, in this order):
|
|
7
|
+
* - `.ai/analysis/cockpit-ui-redesign/agent-event-protocols.md` §7 (schema)
|
|
8
|
+
* and §7.1 (per-backend mapping tables);
|
|
9
|
+
* - the spec `.ai/specs/2026-07-14-cockpit-ui-redesign.md`
|
|
10
|
+
* §"Normalized agent-event protocol v2".
|
|
11
|
+
*
|
|
12
|
+
* Design rules baked in:
|
|
13
|
+
* 1. Item-lifecycle model (Codex/ACP style): one stable `id` per item with
|
|
14
|
+
* started/delta/updated/completed phases — two of the three backends are
|
|
15
|
+
* natively item-shaped and claude maps trivially.
|
|
16
|
+
* 2. ACP vocabulary wherever a choice is arbitrary (tool status/kind, plan
|
|
17
|
+
* entries, diff shape, stop reasons) for ecosystem alignment.
|
|
18
|
+
* 3. Every v1 `AgentEvent` stays derivable from this stream, so consumers
|
|
19
|
+
* can migrate one panel at a time.
|
|
20
|
+
*
|
|
21
|
+
* This module is pure vocabulary: no runtime imports, no runner coupling.
|
|
22
|
+
* Step 2.4 mirrors it into `web/app/src/protocol/`.
|
|
23
|
+
*/
|
|
24
|
+
/** The backend that produced a session — mirrors `RunnerId` in
|
|
25
|
+
* `agent-runner.ts` (kept structurally identical; a type-level test
|
|
26
|
+
* guards against drift so this module stays dependency-free). */
|
|
27
|
+
export type UiBackend = 'claude' | 'codex' | 'opencode';
|
|
28
|
+
/**
|
|
29
|
+
* Tool lifecycle status (ACP: pending/in_progress/completed/failed).
|
|
30
|
+
* `running` ≡ ACP `in_progress` (opencode's word — reads better in code);
|
|
31
|
+
* `declined` covers codex approval declines and claude `permission_denials`.
|
|
32
|
+
*
|
|
33
|
+
* Per backend: claude has no pending phase (`tool_use` arrives already
|
|
34
|
+
* running); codex maps `inProgress→running`, `completed/failed/declined`
|
|
35
|
+
* verbatim; opencode maps `pending/running` verbatim and `error→failed`.
|
|
36
|
+
*/
|
|
37
|
+
export type ToolStatus = 'pending' | 'running' | 'completed' | 'failed' | 'declined';
|
|
38
|
+
/**
|
|
39
|
+
* Icon/verb hint for a tool item — a superset of ACP's ToolKind.
|
|
40
|
+
* `task` = subagent spawn (claude Task / opencode subtask / codex review).
|
|
41
|
+
*/
|
|
42
|
+
export type ToolKind = 'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'think' | 'fetch' | 'task' | 'plan' | 'other';
|
|
43
|
+
/**
|
|
44
|
+
* Why a turn (or the session) stopped.
|
|
45
|
+
*
|
|
46
|
+
* Per backend: claude `result` subtype success→`end_turn`,
|
|
47
|
+
* error_max_turns→`max_tokens`, error_during_execution→`error`; codex
|
|
48
|
+
* turn/completed→`end_turn`, turn/failed→`error`, interrupt→`cancelled`;
|
|
49
|
+
* opencode `session.idle`→`end_turn` (or `error` when a session.error
|
|
50
|
+
* preceded it).
|
|
51
|
+
*/
|
|
52
|
+
export type StopReason = 'end_turn' | 'max_tokens' | 'refusal' | 'cancelled' | 'timeout' | 'error';
|
|
53
|
+
/** Status of one plan/todo entry (claude TodoWrite uses these words verbatim;
|
|
54
|
+
* codex todoList and opencode todowrite map 1:1). */
|
|
55
|
+
export type PlanStatus = 'pending' | 'in_progress' | 'completed';
|
|
56
|
+
/** One entry of the session plan — full-replacement semantics (ACP style). */
|
|
57
|
+
export interface PlanEntry {
|
|
58
|
+
content: string;
|
|
59
|
+
status: PlanStatus;
|
|
60
|
+
priority?: 'high' | 'medium' | 'low';
|
|
61
|
+
/** Present-continuous label shown while the entry is in progress
|
|
62
|
+
* (claude TodoWrite's `activeForm`). */
|
|
63
|
+
activeForm?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Raw token counts — never pre-weighted (cost weighting is a presentation
|
|
67
|
+
* concern, not a wire concern).
|
|
68
|
+
*
|
|
69
|
+
* Per backend: claude `result.usage` (+ `total_cost_usd` as `costUsd` on the
|
|
70
|
+
* carrying event); codex `thread/tokenUsage/updated` (no USD); opencode
|
|
71
|
+
* `message.updated` info.tokens/cost and `step-finish` parts.
|
|
72
|
+
*/
|
|
73
|
+
export interface TokenUsage {
|
|
74
|
+
input: number;
|
|
75
|
+
output: number;
|
|
76
|
+
cacheRead?: number;
|
|
77
|
+
cacheWrite?: number;
|
|
78
|
+
reasoning?: number;
|
|
79
|
+
/** The backend's own total when given, else the sum of the parts. */
|
|
80
|
+
total: number;
|
|
81
|
+
/** Model context-window size when known — feeds the fill gauge. */
|
|
82
|
+
contextWindow?: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* One file change carried by an edit-tool item.
|
|
86
|
+
* claude `Edit`/`Write` input → `oldText`/`newText`; codex
|
|
87
|
+
* `fileChange.changes[]` → `unified`; opencode `patch` parts → `unified`.
|
|
88
|
+
* `oldText: null` means the file is newly created.
|
|
89
|
+
*/
|
|
90
|
+
export interface FileDiff {
|
|
91
|
+
path: string;
|
|
92
|
+
oldText: string | null;
|
|
93
|
+
newText?: string;
|
|
94
|
+
unified?: string;
|
|
95
|
+
}
|
|
96
|
+
/** A file location a tool touched (feeds "jump to file" affordances). */
|
|
97
|
+
export interface ToolLocation {
|
|
98
|
+
path: string;
|
|
99
|
+
line?: number;
|
|
100
|
+
}
|
|
101
|
+
/** A choice offered by a permission request (ACP option kinds). */
|
|
102
|
+
export type PermissionOptionKind = 'allow_once' | 'allow_always' | 'reject_once' | 'reject_always';
|
|
103
|
+
export interface PermissionOption {
|
|
104
|
+
id: string;
|
|
105
|
+
label: string;
|
|
106
|
+
kind: PermissionOptionKind;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A chat message.
|
|
110
|
+
* claude: `assistant` `text` blocks (whole-block, or true deltas via
|
|
111
|
+
* `--include-partial-messages`); codex: `agentMessage` items (`phase` from
|
|
112
|
+
* the item's own phase field); opencode: text parts of a message.
|
|
113
|
+
*/
|
|
114
|
+
export interface UiMessageItem {
|
|
115
|
+
kind: 'message';
|
|
116
|
+
id: string;
|
|
117
|
+
role: 'assistant' | 'user';
|
|
118
|
+
text: string;
|
|
119
|
+
phase?: 'commentary' | 'final';
|
|
120
|
+
parentItemId?: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Extended thinking / reasoning summary.
|
|
124
|
+
* claude: `thinking` blocks; codex: `reasoning` items (+ textDelta);
|
|
125
|
+
* opencode: `reasoning` parts.
|
|
126
|
+
*/
|
|
127
|
+
export interface UiReasoningItem {
|
|
128
|
+
kind: 'reasoning';
|
|
129
|
+
id: string;
|
|
130
|
+
text: string;
|
|
131
|
+
parentItemId?: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* One tool invocation with its full lifecycle.
|
|
135
|
+
* claude: `tool_use` (started, status `running`) + `tool_result`
|
|
136
|
+
* (completed/failed; `permission_denials` → `declined`); codex: typed items
|
|
137
|
+
* (`commandExecution`→execute with `exitCode`, `fileChange`→edit with
|
|
138
|
+
* `diffs`, `mcpToolCall`→other, `webSearch`→fetch); opencode: tool parts
|
|
139
|
+
* (state pending/running/completed/error, `patch` parts → `diffs`).
|
|
140
|
+
*
|
|
141
|
+
* `parentItemId` nests subagent work under its spawning task item —
|
|
142
|
+
* claude `parent_tool_use_id`, opencode `subtask` parts, codex review items.
|
|
143
|
+
*/
|
|
144
|
+
export interface UiToolItem {
|
|
145
|
+
kind: 'tool';
|
|
146
|
+
id: string;
|
|
147
|
+
/** The backend's tool name / item type (e.g. `Bash`, `commandExecution`). */
|
|
148
|
+
name: string;
|
|
149
|
+
/** ACP-style icon/verb hint — see `toolDisplay()` in `tool-display.ts`. */
|
|
150
|
+
toolKind: ToolKind;
|
|
151
|
+
/** Human line, computed once in the protocol layer: "Ran npm test". */
|
|
152
|
+
title: string;
|
|
153
|
+
status: ToolStatus;
|
|
154
|
+
/** Raw input — may arrive incrementally. */
|
|
155
|
+
input?: unknown;
|
|
156
|
+
output?: string;
|
|
157
|
+
error?: string;
|
|
158
|
+
diffs?: FileDiff[];
|
|
159
|
+
locations?: ToolLocation[];
|
|
160
|
+
/** commandExecution / bash exit code, when the backend reports one. */
|
|
161
|
+
exitCode?: number;
|
|
162
|
+
parentItemId?: string;
|
|
163
|
+
}
|
|
164
|
+
export type UiItem = UiMessageItem | UiReasoningItem | UiToolItem;
|
|
165
|
+
/**
|
|
166
|
+
* Session opened.
|
|
167
|
+
* claude: `system/init` (model, tools, cwd); codex: `thread/started` or the
|
|
168
|
+
* `thread/start` result; opencode: `POST /session` response.
|
|
169
|
+
*/
|
|
170
|
+
export interface UiSessionStartedEvent {
|
|
171
|
+
type: 'session.started';
|
|
172
|
+
sessionId: string;
|
|
173
|
+
backend: UiBackend;
|
|
174
|
+
model?: string;
|
|
175
|
+
cwd?: string;
|
|
176
|
+
tools?: string[];
|
|
177
|
+
}
|
|
178
|
+
/** Session over (replaces v1 `done` / fatal `error`). */
|
|
179
|
+
export interface UiSessionEndedEvent {
|
|
180
|
+
type: 'session.ended';
|
|
181
|
+
reason: StopReason;
|
|
182
|
+
message?: string;
|
|
183
|
+
}
|
|
184
|
+
/** v1 `note` + `error` unified — severity explicit instead of implied. */
|
|
185
|
+
export interface UiSessionErrorEvent {
|
|
186
|
+
type: 'session.error';
|
|
187
|
+
message: string;
|
|
188
|
+
fatal: boolean;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* A turn began.
|
|
192
|
+
* claude: on each stdin user message; codex: `turn/started`;
|
|
193
|
+
* opencode: on each prompt POST.
|
|
194
|
+
*/
|
|
195
|
+
export interface UiTurnStartedEvent {
|
|
196
|
+
type: 'turn.started';
|
|
197
|
+
turnId: string;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* A turn finished.
|
|
201
|
+
* claude: `result` (usage + `total_cost_usd`); codex: `turn/completed` /
|
|
202
|
+
* `turn/failed`; opencode: `session.idle`.
|
|
203
|
+
*/
|
|
204
|
+
export interface UiTurnCompletedEvent {
|
|
205
|
+
type: 'turn.completed';
|
|
206
|
+
turnId: string;
|
|
207
|
+
stopReason: StopReason;
|
|
208
|
+
usage?: TokenUsage;
|
|
209
|
+
costUsd?: number;
|
|
210
|
+
}
|
|
211
|
+
/** An item entered the stream (tools usually with status pending/running). */
|
|
212
|
+
export interface UiItemStartedEvent {
|
|
213
|
+
type: 'item.started';
|
|
214
|
+
item: UiItem;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Streaming append to one field of a live item.
|
|
218
|
+
* claude: partial-message text deltas; codex: `item/agentMessage/delta`
|
|
219
|
+
* (`text`), `item/reasoning/textDelta` (`reasoning`),
|
|
220
|
+
* `item/commandExecution/outputDelta` (`output` — live terminal); opencode:
|
|
221
|
+
* text/reasoning part cursor deltas, running-state metadata for `output`.
|
|
222
|
+
* claude has no live command output — the tool card fills on completion
|
|
223
|
+
* (per-capability degradation, never per-backend).
|
|
224
|
+
*/
|
|
225
|
+
export interface UiItemDeltaEvent {
|
|
226
|
+
type: 'item.delta';
|
|
227
|
+
itemId: string;
|
|
228
|
+
field: 'text' | 'reasoning' | 'output';
|
|
229
|
+
delta: string;
|
|
230
|
+
}
|
|
231
|
+
/** Status flips and streamed-content snapshots (codex `item/updated`,
|
|
232
|
+
* opencode part-state flips such as pending→running). */
|
|
233
|
+
export interface UiItemUpdatedEvent {
|
|
234
|
+
type: 'item.updated';
|
|
235
|
+
item: UiItem;
|
|
236
|
+
}
|
|
237
|
+
/** Final snapshot of an item — safe to persist (snapshots, not deltas). */
|
|
238
|
+
export interface UiItemCompletedEvent {
|
|
239
|
+
type: 'item.completed';
|
|
240
|
+
item: UiItem;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Full-replacement plan snapshot (ACP semantics).
|
|
244
|
+
* claude: `TodoWrite` tool input; codex: `todoList`/`plan` items;
|
|
245
|
+
* opencode: `todowrite` tool — identical semantics across all three.
|
|
246
|
+
*/
|
|
247
|
+
export interface UiPlanUpdatedEvent {
|
|
248
|
+
type: 'plan.updated';
|
|
249
|
+
entries: PlanEntry[];
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* RESERVED — wired when auto-approve becomes optional. Types only for now.
|
|
253
|
+
* claude: `control_request can_use_tool`; codex: the per-item
|
|
254
|
+
* `requestApproval` JSON-RPC requests; opencode: `permission.updated`.
|
|
255
|
+
*/
|
|
256
|
+
export interface UiPermissionRequestedEvent {
|
|
257
|
+
type: 'permission.requested';
|
|
258
|
+
requestId: string;
|
|
259
|
+
itemId?: string;
|
|
260
|
+
title: string;
|
|
261
|
+
options: PermissionOption[];
|
|
262
|
+
}
|
|
263
|
+
/** RESERVED — the counterpart resolution (see `permission.requested`). */
|
|
264
|
+
export interface UiPermissionResolvedEvent {
|
|
265
|
+
type: 'permission.resolved';
|
|
266
|
+
requestId: string;
|
|
267
|
+
optionId: string;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Cumulative-for-session raw telemetry.
|
|
271
|
+
* claude: `result.usage` + `total_cost_usd`; codex:
|
|
272
|
+
* `thread/tokenUsage/updated` (cost stays absent); opencode:
|
|
273
|
+
* `message.updated` tokens/cost + `step-finish` increments.
|
|
274
|
+
*/
|
|
275
|
+
export interface UiUsageUpdatedEvent {
|
|
276
|
+
type: 'usage.updated';
|
|
277
|
+
usage: TokenUsage;
|
|
278
|
+
costUsd?: number;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* An image emitted mid-stream (kept from v1) — raw base64 on the wire here;
|
|
282
|
+
* the run manager persists it and re-emits a URL. claude: image blocks in
|
|
283
|
+
* `tool_result` (with `itemId` linking back to the tool item).
|
|
284
|
+
*/
|
|
285
|
+
export interface UiImageEvent {
|
|
286
|
+
type: 'image';
|
|
287
|
+
itemId?: string;
|
|
288
|
+
mediaType: string;
|
|
289
|
+
data: string;
|
|
290
|
+
}
|
|
291
|
+
export type UiEvent = UiSessionStartedEvent | UiSessionEndedEvent | UiSessionErrorEvent | UiTurnStartedEvent | UiTurnCompletedEvent | UiItemStartedEvent | UiItemDeltaEvent | UiItemUpdatedEvent | UiItemCompletedEvent | UiPlanUpdatedEvent | UiPermissionRequestedEvent | UiPermissionResolvedEvent | UiUsageUpdatedEvent | UiImageEvent;
|
|
292
|
+
/** Every v2 event discriminator — `satisfies Record<UiEventType, …>` maps
|
|
293
|
+
* downstream keep new event types honest at compile time. */
|
|
294
|
+
export type UiEventType = UiEvent['type'];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized agent-event protocol v2 — the shared vocabulary every runner
|
|
3
|
+
* emits ALONGSIDE the v1 `AgentEvent` stream (never replacing it; mixed
|
|
4
|
+
* NDJSON files stay valid — old recordings must keep replaying).
|
|
5
|
+
*
|
|
6
|
+
* Contract sources (authoritative, in this order):
|
|
7
|
+
* - `.ai/analysis/cockpit-ui-redesign/agent-event-protocols.md` §7 (schema)
|
|
8
|
+
* and §7.1 (per-backend mapping tables);
|
|
9
|
+
* - the spec `.ai/specs/2026-07-14-cockpit-ui-redesign.md`
|
|
10
|
+
* §"Normalized agent-event protocol v2".
|
|
11
|
+
*
|
|
12
|
+
* Design rules baked in:
|
|
13
|
+
* 1. Item-lifecycle model (Codex/ACP style): one stable `id` per item with
|
|
14
|
+
* started/delta/updated/completed phases — two of the three backends are
|
|
15
|
+
* natively item-shaped and claude maps trivially.
|
|
16
|
+
* 2. ACP vocabulary wherever a choice is arbitrary (tool status/kind, plan
|
|
17
|
+
* entries, diff shape, stop reasons) for ecosystem alignment.
|
|
18
|
+
* 3. Every v1 `AgentEvent` stays derivable from this stream, so consumers
|
|
19
|
+
* can migrate one panel at a time.
|
|
20
|
+
*
|
|
21
|
+
* This module is pure vocabulary: no runtime imports, no runner coupling.
|
|
22
|
+
* Step 2.4 mirrors it into `web/app/src/protocol/`.
|
|
23
|
+
*/
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=ui-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-events.js","sourceRoot":"","sources":["../../src/core/ui-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
|
package/dist/index.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Tarball bundle check — the pure half of `scripts/check-pack.mjs`.
|
|
2
|
+
*
|
|
3
|
+
* Phase R1 of the cockpit redesign shipped an npm tarball with no UI in it
|
|
4
|
+
* (`files` listed the sources, not the Vite build). This module pins that bug
|
|
5
|
+
* class: given the file list `npm pack --dry-run --json` reports, decide
|
|
6
|
+
* whether the package would ship a working cockpit. Kept dependency-free and
|
|
7
|
+
* side-effect-free so the decision is unit-testable; the script owns the
|
|
8
|
+
* `npm pack` invocation and the exit code.
|
|
9
|
+
*/
|
|
10
|
+
/** Human-readable problems with a would-be tarball; empty array = publishable.
|
|
11
|
+
*
|
|
12
|
+
* Requirements (spec `.ai/specs/2026-07-14-cockpit-ui-redesign.md`, Serving):
|
|
13
|
+
* - `web/dist/index.html` — the built shell every GET serves.
|
|
14
|
+
* - at least one `web/dist/assets/*` file — the hashed JS/CSS bundles; an
|
|
15
|
+
* index.html alone renders a blank page.
|
|
16
|
+
*/
|
|
17
|
+
export declare function findPackGaps(packedFiles: readonly string[]): string[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Tarball bundle check — the pure half of `scripts/check-pack.mjs`.
|
|
2
|
+
*
|
|
3
|
+
* Phase R1 of the cockpit redesign shipped an npm tarball with no UI in it
|
|
4
|
+
* (`files` listed the sources, not the Vite build). This module pins that bug
|
|
5
|
+
* class: given the file list `npm pack --dry-run --json` reports, decide
|
|
6
|
+
* whether the package would ship a working cockpit. Kept dependency-free and
|
|
7
|
+
* side-effect-free so the decision is unit-testable; the script owns the
|
|
8
|
+
* `npm pack` invocation and the exit code.
|
|
9
|
+
*/
|
|
10
|
+
/** Human-readable problems with a would-be tarball; empty array = publishable.
|
|
11
|
+
*
|
|
12
|
+
* Requirements (spec `.ai/specs/2026-07-14-cockpit-ui-redesign.md`, Serving):
|
|
13
|
+
* - `web/dist/index.html` — the built shell every GET serves.
|
|
14
|
+
* - at least one `web/dist/assets/*` file — the hashed JS/CSS bundles; an
|
|
15
|
+
* index.html alone renders a blank page.
|
|
16
|
+
*/
|
|
17
|
+
export function findPackGaps(packedFiles) {
|
|
18
|
+
const gaps = [];
|
|
19
|
+
if (!packedFiles.includes('web/dist/index.html')) {
|
|
20
|
+
gaps.push('web/dist/index.html is missing — the tarball would ship no UI shell (run `npm run build:web`)');
|
|
21
|
+
}
|
|
22
|
+
if (!packedFiles.some((f) => f.startsWith('web/dist/assets/') && f.length > 'web/dist/assets/'.length)) {
|
|
23
|
+
gaps.push('no web/dist/assets/* bundle in the tarball — the shell would load with no JS/CSS');
|
|
24
|
+
}
|
|
25
|
+
return gaps;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=pack-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-check.js","sourceRoot":"","sources":["../src/pack-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,WAA8B;IACzD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,+FAA+F,CAAC,CAAC;IAC7G,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;QACvG,IAAI,CAAC,IAAI,CAAC,kFAAkF,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-summary titles (#389): derive a short display title for a run from the
|
|
3
|
+
* text of an agent turn. Pure by design — the RunManager calls this on
|
|
4
|
+
* turn-end and stores the first informative answer as `RunRecord.titleSummary`.
|
|
5
|
+
*
|
|
6
|
+
* Honest absence beats a garbage title: when the turn text yields nothing
|
|
7
|
+
* better than the raw task's first line (too short, markdown noise only,
|
|
8
|
+
* or a verbatim echo of the task), the answer is `undefined` and the UI
|
|
9
|
+
* keeps showing the raw `title`.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Derive a display title from an agent turn's text, or `undefined` when the
|
|
13
|
+
* text yields nothing more informative than the task itself.
|
|
14
|
+
*/
|
|
15
|
+
export declare function deriveTitleSummary(text: string, task: string): string | undefined;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-summary titles (#389): derive a short display title for a run from the
|
|
3
|
+
* text of an agent turn. Pure by design — the RunManager calls this on
|
|
4
|
+
* turn-end and stores the first informative answer as `RunRecord.titleSummary`.
|
|
5
|
+
*
|
|
6
|
+
* Honest absence beats a garbage title: when the turn text yields nothing
|
|
7
|
+
* better than the raw task's first line (too short, markdown noise only,
|
|
8
|
+
* or a verbatim echo of the task), the answer is `undefined` and the UI
|
|
9
|
+
* keeps showing the raw `title`.
|
|
10
|
+
*/
|
|
11
|
+
/** Display cap, in code points — matches `makeTitle`'s 80-char convention. */
|
|
12
|
+
const MAX_LEN = 80;
|
|
13
|
+
/** A summary shorter than this many words says nothing — skip it. */
|
|
14
|
+
const MIN_WORDS = 4;
|
|
15
|
+
/** Conversational lead-ins that carry no information about the task. Applied
|
|
16
|
+
* repeatedly, so "Sure! I'll go ahead and fix …" strips down to "fix …". */
|
|
17
|
+
const FLUFF_RES = [
|
|
18
|
+
/^(?:sure|okay|ok|alright|great|certainly|understood|got it|sounds good|of course)[,.!:]*\s+/i,
|
|
19
|
+
/^(?:i(?:'|’)?ll|i will|i(?:'|’)?m going to|i(?:'|’)?m about to|let me|let(?:'|’)?s|first,?|now,?)\s+/i,
|
|
20
|
+
/^(?:go ahead and|start by|begin by)\s+/i,
|
|
21
|
+
];
|
|
22
|
+
/** Strip inline markdown emphasis/code from one line. */
|
|
23
|
+
function stripInlineMarkdown(line) {
|
|
24
|
+
return line
|
|
25
|
+
.replace(/^#{1,6}\s+/, '') // heading marker
|
|
26
|
+
.replace(/^(?:[-*+]|\d+[.)])\s+/, '') // list marker
|
|
27
|
+
.replace(/^>\s+/, '') // blockquote marker
|
|
28
|
+
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
|
29
|
+
.replace(/__([^_]+)__/g, '$1')
|
|
30
|
+
.replace(/\*([^*]+)\*/g, '$1')
|
|
31
|
+
.replace(/(^|[\s(])_([^_]+)_(?=[\s).,;:!?]|$)/g, '$1$2') // word-bound _emphasis_, not snake_case
|
|
32
|
+
.replace(/`([^`]*)`/g, '$1')
|
|
33
|
+
.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1') // [text](url) → text
|
|
34
|
+
.trim();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* First sentence of `s`. Sentence end = `.`/`!`/`?` followed by whitespace or
|
|
38
|
+
* end of string — so dots inside paths (`src/store.ts`) don't cut it short.
|
|
39
|
+
*/
|
|
40
|
+
function firstSentence(s) {
|
|
41
|
+
const match = /^(.*?[.!?])(?:\s|$)/.exec(s);
|
|
42
|
+
return (match?.[1] ?? s).replace(/[.!?]+$/, '').trim();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Derive a display title from an agent turn's text, or `undefined` when the
|
|
46
|
+
* text yields nothing more informative than the task itself.
|
|
47
|
+
*/
|
|
48
|
+
export function deriveTitleSummary(text, task) {
|
|
49
|
+
// Fenced code blocks are never titles — drop them (incl. an unclosed one).
|
|
50
|
+
const withoutFences = text.replace(/```[\s\S]*?(?:```|$)/g, '\n');
|
|
51
|
+
const line = withoutFences
|
|
52
|
+
.split('\n')
|
|
53
|
+
.map((l) => stripInlineMarkdown(l))
|
|
54
|
+
.find((l) => l.length > 0);
|
|
55
|
+
if (!line)
|
|
56
|
+
return undefined;
|
|
57
|
+
// Fluff first, sentence second: "Sure! I'll fix …" — the fluff IS the
|
|
58
|
+
// first sentence, so stripping after extraction would keep only "Sure".
|
|
59
|
+
let s = line;
|
|
60
|
+
for (let stripped = true; stripped;) {
|
|
61
|
+
stripped = false;
|
|
62
|
+
for (const re of FLUFF_RES) {
|
|
63
|
+
const next = s.replace(re, '');
|
|
64
|
+
if (next !== s) {
|
|
65
|
+
s = next;
|
|
66
|
+
stripped = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
s = firstSentence(s.trim());
|
|
71
|
+
// Cap by code points, not UTF-16 units — no split surrogate pairs.
|
|
72
|
+
const chars = [...s];
|
|
73
|
+
if (chars.length > MAX_LEN)
|
|
74
|
+
s = `${chars.slice(0, MAX_LEN - 1).join('').trimEnd()}…`;
|
|
75
|
+
// Informative? More than a few words, and not an echo of the task.
|
|
76
|
+
if (s.split(/\s+/).filter(Boolean).length < MIN_WORDS)
|
|
77
|
+
return undefined;
|
|
78
|
+
const taskFirstLine = (task.trim().split('\n')[0] ?? '').trim().toLowerCase();
|
|
79
|
+
if (s.toLowerCase() === taskFirstLine)
|
|
80
|
+
return undefined;
|
|
81
|
+
const first = [...s][0] ?? '';
|
|
82
|
+
return first.toUpperCase() + s.slice(first.length);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=title-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"title-summary.js","sourceRoot":"","sources":["../../src/runs/title-summary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol-v2 persistence + fan-out policy for one agent session — the
|
|
3
|
+
* RunManager feeds every `UiEvent` a runner emits (`SessionOptions.onUiEvent`)
|
|
4
|
+
* through one sink instance and the sink decides, per the spec's performance
|
|
5
|
+
* guardrails (§"Normalized agent-event protocol v2" and
|
|
6
|
+
* `.ai/analysis/cockpit-ui-redesign/protocol-rationale-and-performance.md` §5):
|
|
7
|
+
*
|
|
8
|
+
* - `item.delta` is COALESCED per item (~40 ms flush) onto the live wire
|
|
9
|
+
* only — raw deltas NEVER hit the NDJSON file; replay needs no delta
|
|
10
|
+
* reduction because every item's final state rides `item.completed`.
|
|
11
|
+
* - Everything else persists as snapshots via `persist()` (the store stamps
|
|
12
|
+
* `seq`/`ts`, and `appendEvent` already fans persisted lines out live, so
|
|
13
|
+
* a persisted event is never double-emitted here).
|
|
14
|
+
* - `item.updated` hits disk only on a meaningful change (status flip, new
|
|
15
|
+
* diffs/title/…) — streamed-content-only growth goes to the live wire
|
|
16
|
+
* ephemerally, mirroring what the deltas already carry.
|
|
17
|
+
* - `usage.updated` is cumulative per session: persisted only when the
|
|
18
|
+
* total (or cost) actually moved, mirroring v1's token-usage cadence.
|
|
19
|
+
*
|
|
20
|
+
* Output errors are swallowed: v2 is additive and best-effort — a persist
|
|
21
|
+
* failure (e.g. the run was deleted mid-flush) must never disturb the v1
|
|
22
|
+
* stream or crash a timer callback.
|
|
23
|
+
*/
|
|
24
|
+
import type { StopReason, UiEvent } from '../core/ui-events.js';
|
|
25
|
+
/** Spec guardrail: coalesce `item.delta` at ~30–50 ms boundaries. */
|
|
26
|
+
export declare const DELTA_FLUSH_MS = 40;
|
|
27
|
+
/**
|
|
28
|
+
* v2 lines share the NDJSON file and the store's event bus with v1, but ride
|
|
29
|
+
* a DIFFERENT SSE event name (`ui-event` vs `run-event`) so the legacy UI —
|
|
30
|
+
* which knew only `run-event` and JSON-dumped unknown types into the
|
|
31
|
+
* transcript; retired in R7, but the name split stays as wire shape — never
|
|
32
|
+
* saw them. The discriminator is the dotted namespace:
|
|
33
|
+
* every v2 type on the wire is dotted (`item.started`, `turn.completed`, …)
|
|
34
|
+
* and no v1 type is. v2's only undotted type, `image`, never reaches the
|
|
35
|
+
* store: the sink drops it in favor of the v1 image pipeline (which persists
|
|
36
|
+
* the file and re-emits a URL instead of raw base64).
|
|
37
|
+
*/
|
|
38
|
+
export declare function isV2WireEventType(type: string): boolean;
|
|
39
|
+
/** A loose event as the store consumes it (the sink adds no seq/ts — the
|
|
40
|
+
* store stamps those). */
|
|
41
|
+
type WireEvent = {
|
|
42
|
+
type: string;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
};
|
|
45
|
+
export interface UiEventSinkOutput {
|
|
46
|
+
/** Append to the run's NDJSON file (which also fans out live). */
|
|
47
|
+
persist(event: WireEvent): void;
|
|
48
|
+
/** Fan out live only — never written to disk (coalesced deltas). */
|
|
49
|
+
emitLive(event: WireEvent): void;
|
|
50
|
+
}
|
|
51
|
+
export declare class UiEventSink {
|
|
52
|
+
private readonly out;
|
|
53
|
+
private readonly flushMs;
|
|
54
|
+
/** Pending delta text per open item, per streamed field (insertion order
|
|
55
|
+
* preserved so a flush replays fields in first-arrival order). */
|
|
56
|
+
private readonly buffers;
|
|
57
|
+
private readonly timers;
|
|
58
|
+
/** Last persisted non-streamed shape per item — the `item.updated`
|
|
59
|
+
* meaningful-change filter. */
|
|
60
|
+
private readonly shapes;
|
|
61
|
+
private lastUsage;
|
|
62
|
+
private ended;
|
|
63
|
+
constructor(out: UiEventSinkOutput, flushMs?: number);
|
|
64
|
+
/** Fold one runner-emitted v2 event into the persist/live outputs. */
|
|
65
|
+
handle(event: UiEvent): void;
|
|
66
|
+
/**
|
|
67
|
+
* Session close, called by the RunManager exactly once when the v1 session
|
|
68
|
+
* settles: flush everything, then persist v2 `session.error` (fatal) when
|
|
69
|
+
* the session failed, and `session.ended` — the v2 counterparts of v1's
|
|
70
|
+
* `done`/`error`, which the mappers deliberately left to the RunManager.
|
|
71
|
+
*/
|
|
72
|
+
sessionEnded(reason: StopReason, errorMessage?: string): void;
|
|
73
|
+
/** Flush every open coalescer now (turn end, session settle). */
|
|
74
|
+
flushAll(): void;
|
|
75
|
+
private bufferDelta;
|
|
76
|
+
/** Emit one merged `item.delta` per buffered field of the item, live only. */
|
|
77
|
+
private flushItem;
|
|
78
|
+
private persist;
|
|
79
|
+
private emitLive;
|
|
80
|
+
}
|
|
81
|
+
export {};
|