@exoscient/control-panel 0.1.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.
Files changed (77) hide show
  1. package/README.md +14 -0
  2. package/dist/AppControlPanel.d.ts +77 -0
  3. package/dist/AppControlPanel.d.ts.map +1 -0
  4. package/dist/AppControlPanel.js +1625 -0
  5. package/dist/AppControlPanel.js.map +1 -0
  6. package/dist/ControlPanelShell.d.ts +39 -0
  7. package/dist/ControlPanelShell.d.ts.map +1 -0
  8. package/dist/ControlPanelShell.js +152 -0
  9. package/dist/ControlPanelShell.js.map +1 -0
  10. package/dist/ExoLauncherSimulator.d.ts +36 -0
  11. package/dist/ExoLauncherSimulator.d.ts.map +1 -0
  12. package/dist/ExoLauncherSimulator.js +253 -0
  13. package/dist/ExoLauncherSimulator.js.map +1 -0
  14. package/dist/TaskDetail.d.ts +180 -0
  15. package/dist/TaskDetail.d.ts.map +1 -0
  16. package/dist/TaskDetail.js +889 -0
  17. package/dist/TaskDetail.js.map +1 -0
  18. package/dist/TaskListPage.d.ts +28 -0
  19. package/dist/TaskListPage.d.ts.map +1 -0
  20. package/dist/TaskListPage.js +16 -0
  21. package/dist/TaskListPage.js.map +1 -0
  22. package/dist/TaskWorkspace.d.ts +62 -0
  23. package/dist/TaskWorkspace.d.ts.map +1 -0
  24. package/dist/TaskWorkspace.js +592 -0
  25. package/dist/TaskWorkspace.js.map +1 -0
  26. package/dist/ai-plane.d.ts +75 -0
  27. package/dist/ai-plane.d.ts.map +1 -0
  28. package/dist/ai-plane.js +124 -0
  29. package/dist/ai-plane.js.map +1 -0
  30. package/dist/browser-icons.d.ts +25 -0
  31. package/dist/browser-icons.d.ts.map +1 -0
  32. package/dist/browser-icons.js +125 -0
  33. package/dist/browser-icons.js.map +1 -0
  34. package/dist/client-actions.d.ts +45 -0
  35. package/dist/client-actions.d.ts.map +1 -0
  36. package/dist/client-actions.js +48 -0
  37. package/dist/client-actions.js.map +1 -0
  38. package/dist/control-panel-shared.d.ts +58 -0
  39. package/dist/control-panel-shared.d.ts.map +1 -0
  40. package/dist/control-panel-shared.js +79 -0
  41. package/dist/control-panel-shared.js.map +1 -0
  42. package/dist/control-panel.css +4156 -0
  43. package/dist/index.d.ts +30 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +16 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/repository-workflow.d.ts +27 -0
  48. package/dist/repository-workflow.d.ts.map +1 -0
  49. package/dist/repository-workflow.js +24 -0
  50. package/dist/repository-workflow.js.map +1 -0
  51. package/dist/result.d.ts +6 -0
  52. package/dist/result.d.ts.map +1 -0
  53. package/dist/result.js +77 -0
  54. package/dist/result.js.map +1 -0
  55. package/dist/task-consistency.d.ts +28 -0
  56. package/dist/task-consistency.d.ts.map +1 -0
  57. package/dist/task-consistency.js +25 -0
  58. package/dist/task-consistency.js.map +1 -0
  59. package/dist/task-detail.browser.js +2709 -0
  60. package/dist/task-detail.css +1601 -0
  61. package/dist/task-state.d.ts +11 -0
  62. package/dist/task-state.d.ts.map +1 -0
  63. package/dist/task-state.js +103 -0
  64. package/dist/task-state.js.map +1 -0
  65. package/dist/telemetry.d.ts +39 -0
  66. package/dist/telemetry.d.ts.map +1 -0
  67. package/dist/telemetry.js +106 -0
  68. package/dist/telemetry.js.map +1 -0
  69. package/dist/trace.d.ts +80 -0
  70. package/dist/trace.d.ts.map +1 -0
  71. package/dist/trace.js +694 -0
  72. package/dist/trace.js.map +1 -0
  73. package/dist/updates.d.ts +72 -0
  74. package/dist/updates.d.ts.map +1 -0
  75. package/dist/updates.js +269 -0
  76. package/dist/updates.js.map +1 -0
  77. package/package.json +58 -0
package/dist/trace.js ADDED
@@ -0,0 +1,694 @@
1
+ export function buildTraceItems(events) {
2
+ const items = [];
3
+ let assistantBuffer = '';
4
+ let assistantAt = '';
5
+ function flushAssistant() {
6
+ if (assistantBuffer) {
7
+ items.push({ kind: 'assistant', at: assistantAt, text: assistantBuffer });
8
+ assistantBuffer = '';
9
+ assistantAt = '';
10
+ }
11
+ }
12
+ for (const rawEvent of events) {
13
+ const event = normalizeTraceEvent(rawEvent);
14
+ if (event.type === 'assistant-delta') {
15
+ assistantAt || (assistantAt = event.at);
16
+ assistantBuffer += event.message;
17
+ continue;
18
+ }
19
+ if (event.type === 'assistant-message') {
20
+ if (!assistantBuffer) {
21
+ items.push({ kind: 'assistant', at: event.at, text: event.message });
22
+ }
23
+ continue;
24
+ }
25
+ if (event.type === 'assistant') {
26
+ flushAssistant();
27
+ items.push({ kind: 'assistant', at: event.at, text: event.message });
28
+ continue;
29
+ }
30
+ flushAssistant();
31
+ if (event.type === 'codex-event' || isCodexJsonEvent(event)) {
32
+ const item = parseCodexEvent(event);
33
+ if (item)
34
+ items.push(item);
35
+ continue;
36
+ }
37
+ if (event.type === 'turn-completed') {
38
+ items.push(parseTurnCompleted(event));
39
+ continue;
40
+ }
41
+ if (event.type === 'thread') {
42
+ items.push({ kind: 'status', at: event.at, text: 'Conversation thread started.', raw: readableJson(event.message) });
43
+ continue;
44
+ }
45
+ if (event.type === 'status') {
46
+ items.push({ kind: 'status', at: event.at, text: event.message });
47
+ continue;
48
+ }
49
+ if (event.type === 'output') {
50
+ items.push({ kind: 'output', at: event.at, stream: event.stream, text: event.message });
51
+ continue;
52
+ }
53
+ if (event.type === 'tokens') {
54
+ items.push({ kind: 'tokens', at: event.at, text: tokenUsageText(parseJson(event.message)), raw: readableJson(event.message) });
55
+ continue;
56
+ }
57
+ if (event.type === 'error') {
58
+ items.push({ kind: 'error', at: event.at, text: plainError(event.message), raw: readableJson(event.message) });
59
+ continue;
60
+ }
61
+ if (event.type === 'result') {
62
+ items.push({ kind: 'result', at: event.at, text: event.message });
63
+ continue;
64
+ }
65
+ items.push({ kind: 'raw', at: event.at, label: event.type, text: readableJson(event.message) });
66
+ }
67
+ flushAssistant();
68
+ return compactTraceItems(items);
69
+ }
70
+ export function traceItemMatchesFilter(item, filters) {
71
+ if (filters.length === 0)
72
+ return true;
73
+ return filters.includes(traceItemCategory(item));
74
+ }
75
+ export function latestContextPressure(events) {
76
+ for (let index = events.length - 1; index >= 0; index -= 1) {
77
+ const pressure = contextPressureFromUsage(tokenUsageFromEvent(normalizeTraceEvent(events[index])));
78
+ if (pressure)
79
+ return pressure;
80
+ }
81
+ return null;
82
+ }
83
+ function traceItemCategory(item) {
84
+ if (item.kind === 'assistant')
85
+ return 'narration';
86
+ if (item.kind === 'file' || item.kind === 'tool')
87
+ return 'coding';
88
+ if (item.kind === 'command' || item.kind === 'output')
89
+ return 'shell';
90
+ if (item.kind === 'tokens')
91
+ return 'tokens';
92
+ if (item.kind === 'status' || item.kind === 'result')
93
+ return 'status';
94
+ if (item.kind === 'error')
95
+ return 'errors';
96
+ return 'raw';
97
+ }
98
+ function normalizeTraceEvent(event) {
99
+ const source = isObject(event) ? event : {};
100
+ const messageValue = valueAtAny(source, ['message', 'Message', 'text', 'Text', 'payload', 'Payload', 'data', 'Data']);
101
+ const message = traceMessageFromValue(messageValue, source);
102
+ const traceLine = parseTraceLine(message);
103
+ const explicitTimestamp = firstString(stringValue(source, 'at'), stringValue(source, 'At'), stringValue(source, 'timestamp'), stringValue(source, 'Timestamp'), stringValue(source, 'time'), stringValue(source, 'Time'), stringValue(source, 'createdAt'), stringValue(source, 'CreatedAt'));
104
+ const explicitType = firstString(stringValue(source, 'type'), stringValue(source, 'Type'), stringValue(source, 'kind'), stringValue(source, 'Kind'));
105
+ const lineEvent = traceLine && (!explicitType || explicitType === 'event') ? eventFromTraceLine(traceLine) : null;
106
+ return {
107
+ type: lineEvent?.type ?? explicitType ?? 'event',
108
+ at: explicitTimestamp ?? traceLine?.at ?? '',
109
+ stream: lineEvent?.stream ?? firstString(stringValue(source, 'stream'), stringValue(source, 'Stream')),
110
+ message: lineEvent?.message ?? message,
111
+ };
112
+ }
113
+ function parseTraceLine(message) {
114
+ const match = message.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2}))\s+#\d+\s+([A-Za-z0-9._/-]+)(?:\s+([\s\S]*))?$/);
115
+ if (!match)
116
+ return null;
117
+ const payloadText = match[3]?.trim() ?? '';
118
+ const payload = parseJson(payloadText);
119
+ return { at: match[1], label: match[2], payloadText, payload };
120
+ }
121
+ function eventFromTraceLine(line) {
122
+ const payloadMessage = objectValue(line.payload, 'message');
123
+ if (line.label === 'agent.notification' && payloadMessage) {
124
+ return { type: 'codex-event', at: line.at, message: stringifyCompact(payloadMessage) ?? line.payloadText };
125
+ }
126
+ if (line.payload && (stringValue(line.payload, 'method') || stringValue(line.payload, 'type'))) {
127
+ return { type: 'codex-event', at: line.at, message: stringifyCompact(line.payload) ?? line.payloadText };
128
+ }
129
+ if (line.label === 'agent.stderr' || line.label === 'agent.stdout') {
130
+ return {
131
+ type: 'output',
132
+ at: line.at,
133
+ stream: line.label === 'agent.stderr' ? 'stderr' : 'stdout',
134
+ message: firstString(stringValue(line.payload, 'line'), line.payloadText) ?? '',
135
+ };
136
+ }
137
+ if (line.label.includes('error') || line.label.includes('failed')) {
138
+ return { type: 'error', at: line.at, message: firstString(stringValue(line.payload, 'message'), line.payloadText, titleFromTraceLabel(line.label)) ?? titleFromTraceLabel(line.label) };
139
+ }
140
+ return {
141
+ type: 'status',
142
+ at: line.at,
143
+ message: firstString(stringValue(line.payload, 'message'), titleFromTraceLabel(line.label)) ?? titleFromTraceLabel(line.label),
144
+ };
145
+ }
146
+ function titleFromTraceLabel(label) {
147
+ const text = label
148
+ .replace(/[._/-]+/g, ' ')
149
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
150
+ .replace(/\s+/g, ' ')
151
+ .trim();
152
+ return text ? `${text.charAt(0).toLocaleUpperCase()}${text.slice(1)}.` : 'Trace event.';
153
+ }
154
+ function traceMessageFromValue(value, source) {
155
+ if (typeof value === 'string')
156
+ return value;
157
+ if (value !== undefined)
158
+ return stringifyCompact(value) ?? '';
159
+ return stringifyCompact(source) ?? '';
160
+ }
161
+ function valueAtAny(value, keys) {
162
+ if (!isObject(value))
163
+ return undefined;
164
+ for (const key of keys) {
165
+ const child = value[key];
166
+ if (child !== undefined && child !== null)
167
+ return child;
168
+ }
169
+ return undefined;
170
+ }
171
+ function parseCodexEvent(event) {
172
+ const json = parseJson(event.message);
173
+ const cliType = stringValue(json, 'type');
174
+ const method = stringValue(json, 'method') || (cliType ? cliType.replace(/\./g, '/') : undefined) || event.stream || 'AI agent event';
175
+ const params = objectValue(json, 'params') ?? json;
176
+ const item = objectValue(params, 'item') || objectValue(params, 'entry') || objectValue(json, 'item') || params || json;
177
+ const itemType = stringValue(item, 'type') || stringValue(item, 'kind') || '';
178
+ const title = titleFromMethod(method, itemType);
179
+ const raw = readableJson(event.message);
180
+ if (method === 'item/completed' && itemType === 'agent_message') {
181
+ return {
182
+ kind: 'assistant',
183
+ at: event.at,
184
+ text: stringValue(item, 'text') ?? stringValue(item, 'message') ?? '',
185
+ };
186
+ }
187
+ if (method.includes('error')) {
188
+ return { kind: 'error', at: event.at, text: plainError(event.message), raw };
189
+ }
190
+ if (method === 'configWarning') {
191
+ return {
192
+ kind: 'status',
193
+ at: event.at,
194
+ text: firstString(stringValue(params, 'summary'), stringValue(params, 'details'), 'Configuration warning.') ?? 'Configuration warning.',
195
+ raw,
196
+ };
197
+ }
198
+ if (method === 'thread/started') {
199
+ const thread = objectValue(params, 'thread');
200
+ const cliVersion = stringValue(thread, 'cliVersion');
201
+ return {
202
+ kind: 'status',
203
+ at: event.at,
204
+ text: `Thread started${cliVersion ? ` with AI agent runtime ${cliVersion}` : ''}.`,
205
+ raw,
206
+ };
207
+ }
208
+ if (method === 'thread/status/changed') {
209
+ const status = objectValue(params, 'status');
210
+ return {
211
+ kind: 'status',
212
+ at: event.at,
213
+ text: `Thread status: ${stringValue(status, 'type') ?? 'updated'}.`,
214
+ raw,
215
+ };
216
+ }
217
+ if (method === 'thread/tokenUsage/updated') {
218
+ return {
219
+ kind: 'tokens',
220
+ at: event.at,
221
+ text: tokenUsageText(objectValue(params, 'tokenUsage')),
222
+ raw,
223
+ };
224
+ }
225
+ if (method === 'turn/started') {
226
+ const turn = objectValue(params, 'turn');
227
+ return {
228
+ kind: 'status',
229
+ at: event.at,
230
+ text: `Turn ${stringValue(turn, 'status') ?? 'started'}.`,
231
+ raw,
232
+ };
233
+ }
234
+ if (method === 'turn/diff/updated') {
235
+ return {
236
+ kind: 'file',
237
+ at: event.at,
238
+ title: 'Diff updated',
239
+ text: stringValue(params, 'diff'),
240
+ raw,
241
+ };
242
+ }
243
+ if (method === 'item/commandExecution/outputDelta') {
244
+ return parseCommandOutputDelta(event.at, params, raw);
245
+ }
246
+ if (method === 'item/fileChange/outputDelta') {
247
+ return {
248
+ kind: 'file',
249
+ at: event.at,
250
+ title: 'File change output',
251
+ text: stringValue(params, 'delta'),
252
+ raw,
253
+ };
254
+ }
255
+ if (method === 'rawResponseItem/completed') {
256
+ return parseRawResponseItem(event.at, item, raw);
257
+ }
258
+ if (isCommandEvent(method, itemType, item)) {
259
+ return {
260
+ kind: 'command',
261
+ at: event.at,
262
+ title: itemType === 'commandExecution' ? 'Command Execution' : title,
263
+ itemId: stringValue(item, 'id') ?? stringValue(params, 'itemId'),
264
+ command: firstString(stringValue(item, 'command'), stringValue(item, 'cmd'), stringValue(item, 'script'), commandFromToolArguments(item), stringValue(objectValue(item, 'args'), 'cmd'), stringValue(objectValue(item, 'arguments'), 'cmd'), stringValue(params, 'command')),
265
+ output: firstString(stringValue(item, 'aggregatedOutput'), stringValue(item, 'aggregated_output'), stringValue(item, 'output'), stringValue(item, 'stdout'), stringValue(item, 'stderr'), stringValue(params, 'output'), stringValue(params, 'delta')),
266
+ status: commandStatus(item) ?? statusFromEvent(method, item),
267
+ raw,
268
+ };
269
+ }
270
+ if (isFileEvent(method, itemType, item)) {
271
+ return {
272
+ kind: 'file',
273
+ at: event.at,
274
+ title,
275
+ path: firstString(stringValue(item, 'path'), stringValue(item, 'file'), stringValue(item, 'filename'), pathFromChanges(item), stringValue(objectValue(item, 'changes'), 'path')),
276
+ text: firstString(fileChangesText(item), stringValue(item, 'text'), stringValue(item, 'summary'), stringValue(params, 'delta')),
277
+ raw,
278
+ };
279
+ }
280
+ if (isToolEvent(method, itemType, item)) {
281
+ return {
282
+ kind: 'tool',
283
+ at: event.at,
284
+ title,
285
+ input: stringifyCompact(valueAt(item, 'input') ??
286
+ valueAt(item, 'arguments') ??
287
+ valueAt(item, 'args') ??
288
+ valueAt(params, 'input') ??
289
+ valueAt(params, 'arguments')),
290
+ output: firstString(stringValue(item, 'output'), stringValue(item, 'result'), stringValue(params, 'delta')),
291
+ status: statusFromEvent(method, item),
292
+ raw,
293
+ };
294
+ }
295
+ if (method.includes('started') || method.includes('completed') || method.includes('turn')) {
296
+ return { kind: 'status', at: event.at, text: title, raw };
297
+ }
298
+ return { kind: 'raw', at: event.at, label: title, text: raw };
299
+ }
300
+ function isCodexJsonEvent(event) {
301
+ const json = parseJson(event.message);
302
+ return Boolean(json && (stringValue(json, 'type') || stringValue(json, 'method')));
303
+ }
304
+ function parseRawResponseItem(at, item, raw) {
305
+ const itemType = stringValue(item, 'type') ?? 'response item';
306
+ const name = stringValue(item, 'name');
307
+ if (itemType === 'function_call') {
308
+ const args = parseJson(stringValue(item, 'arguments') ?? '');
309
+ if (isCommandToolName(name) && !args)
310
+ return null;
311
+ return {
312
+ kind: isCommandToolName(name) ? 'command' : 'tool',
313
+ at,
314
+ title: name ?? 'Tool call',
315
+ command: isCommandToolName(name) ? commandFromArguments(args) : undefined,
316
+ input: !isCommandToolName(name) ? stringifyCompact(args ?? stringValue(item, 'arguments')) : undefined,
317
+ status: stringValue(item, 'status') ?? 'called',
318
+ raw,
319
+ };
320
+ }
321
+ if (itemType === 'function_call_output') {
322
+ const output = stringValue(item, 'output');
323
+ if (!output)
324
+ return null;
325
+ return {
326
+ kind: 'tool',
327
+ at,
328
+ title: 'Tool output',
329
+ output,
330
+ status: outputStatus(output),
331
+ raw,
332
+ };
333
+ }
334
+ if (itemType === 'custom_tool_call') {
335
+ const toolName = name ?? 'Custom tool';
336
+ return {
337
+ kind: toolName === 'apply_patch' ? 'file' : 'tool',
338
+ at,
339
+ title: toolName,
340
+ text: toolName === 'apply_patch' ? stringValue(item, 'input') : undefined,
341
+ input: toolName === 'apply_patch' ? undefined : stringValue(item, 'input'),
342
+ status: stringValue(item, 'status') ?? 'called',
343
+ raw,
344
+ };
345
+ }
346
+ if (itemType === 'custom_tool_call_output') {
347
+ return {
348
+ kind: 'tool',
349
+ at,
350
+ title: 'Custom tool output',
351
+ output: decodedToolOutput(stringValue(item, 'output')),
352
+ status: outputStatus(stringValue(item, 'output')),
353
+ raw,
354
+ };
355
+ }
356
+ if (itemType === 'message') {
357
+ const text = contentText(valueAt(item, 'content'));
358
+ if (!text)
359
+ return null;
360
+ return {
361
+ kind: stringValue(item, 'role') === 'assistant' ? 'assistant' : 'status',
362
+ at,
363
+ text,
364
+ raw,
365
+ };
366
+ }
367
+ if (itemType === 'reasoning') {
368
+ const summary = contentText(valueAt(item, 'summary'));
369
+ return {
370
+ kind: 'status',
371
+ at,
372
+ text: summary ? `Reasoning summary: ${summary}` : 'Reasoning step completed.',
373
+ raw,
374
+ };
375
+ }
376
+ return { kind: 'raw', at, label: titleFromMethod('rawResponseItem/completed', itemType), text: raw };
377
+ }
378
+ function parseCommandOutputDelta(at, params, raw) {
379
+ const itemId = stringValue(params, 'itemId');
380
+ const delta = stringValue(params, 'delta') ?? '';
381
+ const parsedDelta = parseJson(delta);
382
+ if (parsedDelta) {
383
+ const stdout = stringValue(parsedDelta, 'stdout');
384
+ const stderr = stringValue(parsedDelta, 'stderr');
385
+ return {
386
+ kind: 'command',
387
+ at,
388
+ title: 'Command Execution',
389
+ itemId,
390
+ command: commandFromDelta(parsedDelta),
391
+ output: firstString(stringValue(parsedDelta, 'output'), stdout || stderr ? `${stdout ?? ''}${stderr ?? ''}` : undefined),
392
+ status: commandDeltaStatus(parsedDelta),
393
+ raw,
394
+ };
395
+ }
396
+ return {
397
+ kind: 'command',
398
+ at,
399
+ title: 'Command Execution',
400
+ itemId,
401
+ output: delta,
402
+ raw,
403
+ };
404
+ }
405
+ function parseTurnCompleted(event) {
406
+ const json = parseJson(event.message);
407
+ const usage = objectValue(objectValue(json, 'params'), 'usage') || objectValue(json, 'usage');
408
+ const usageText = usage ? ` Token use: ${stringifyCompact(usage)}.` : '';
409
+ return {
410
+ kind: usage ? 'tokens' : 'status',
411
+ at: event.at,
412
+ text: `Turn completed.${usageText}`,
413
+ raw: readableJson(event.message),
414
+ };
415
+ }
416
+ function compactTraceItems(items) {
417
+ const compacted = [];
418
+ for (const item of items) {
419
+ const previous = compacted[compacted.length - 1];
420
+ if (previous?.kind === 'output' && item.kind === 'output' && previous.stream === item.stream) {
421
+ previous.text += item.text;
422
+ continue;
423
+ }
424
+ if (item.kind === 'command') {
425
+ const command = findCommandToMerge(compacted, item);
426
+ if (command) {
427
+ command.title = item.title || command.title;
428
+ command.command = item.command ?? command.command;
429
+ command.output = item.command
430
+ ? item.output ?? command.output
431
+ : `${command.output ?? ''}${item.output ?? ''}`;
432
+ command.status = item.status ?? command.status;
433
+ command.raw = item.raw ?? command.raw;
434
+ continue;
435
+ }
436
+ }
437
+ if (previous?.kind === 'command' && item.kind === 'command' && previous.title === item.title && !item.command) {
438
+ previous.output = `${previous.output ?? ''}${item.output ?? ''}`;
439
+ previous.status = item.status ?? previous.status;
440
+ previous.raw = item.raw ?? previous.raw;
441
+ continue;
442
+ }
443
+ if (previous?.kind === 'file' && item.kind === 'file' && previous.title === item.title && !item.path) {
444
+ if (previous.text === item.text) {
445
+ previous.raw = item.raw ?? previous.raw;
446
+ continue;
447
+ }
448
+ previous.text = `${previous.text ?? ''}${item.text ?? ''}`;
449
+ previous.raw = item.raw ?? previous.raw;
450
+ continue;
451
+ }
452
+ compacted.push(item);
453
+ }
454
+ return compacted;
455
+ }
456
+ function findCommandToMerge(items, item) {
457
+ if (item.itemId) {
458
+ for (let index = items.length - 1; index >= 0; index -= 1) {
459
+ const candidate = items[index];
460
+ if (candidate.kind === 'command' && candidate.itemId === item.itemId)
461
+ return candidate;
462
+ }
463
+ }
464
+ const previous = items[items.length - 1];
465
+ if (previous?.kind === 'command' && !item.command && previous.title === item.title)
466
+ return previous;
467
+ return null;
468
+ }
469
+ function isCommandEvent(method, itemType, item) {
470
+ const text = `${method} ${itemType} ${stringValue(item, 'name') ?? ''}`.toLowerCase();
471
+ return text.includes('command') || text.includes('shell') || text.includes('exec');
472
+ }
473
+ function isFileEvent(method, itemType, item) {
474
+ const text = `${method} ${itemType} ${stringValue(item, 'name') ?? ''}`.toLowerCase();
475
+ return text.includes('file') || text.includes('patch') || text.includes('apply_patch') || Boolean(stringValue(item, 'path'));
476
+ }
477
+ function isToolEvent(method, itemType, item) {
478
+ const text = `${method} ${itemType} ${stringValue(item, 'name') ?? ''}`.toLowerCase();
479
+ return text.includes('tool') || Boolean(stringValue(item, 'name'));
480
+ }
481
+ function titleFromMethod(method, itemType) {
482
+ const base = itemType || method.split('/').filter(Boolean).pop() || method;
483
+ return base
484
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
485
+ .replace(/[_-]+/g, ' ')
486
+ .replace(/\s+/g, ' ')
487
+ .trim();
488
+ }
489
+ function statusFromEvent(method, item) {
490
+ return firstString(stringValue(item, 'status'), method.includes('completed') ? 'completed' : undefined, method.includes('started') ? 'started' : undefined);
491
+ }
492
+ function commandStatus(item) {
493
+ const status = stringValue(item, 'status');
494
+ const exitCode = valueAt(item, 'exitCode') ?? valueAt(item, 'exit_code');
495
+ const durationMs = valueAt(item, 'durationMs') ?? valueAt(item, 'duration_ms');
496
+ const parts = [
497
+ status,
498
+ typeof exitCode === 'number' ? `exit ${exitCode}` : undefined,
499
+ typeof durationMs === 'number' ? `${durationMs} ms` : undefined,
500
+ ].filter(Boolean);
501
+ return parts.length > 0 ? parts.join(' / ') : undefined;
502
+ }
503
+ function commandFromToolArguments(item) {
504
+ return commandFromArguments(parseJson(stringValue(item, 'arguments') ?? ''));
505
+ }
506
+ function commandFromArguments(args) {
507
+ return firstString(stringValue(args, 'cmd'), stringValue(args, 'command'), stringValue(args, 'chars'));
508
+ }
509
+ function commandFromDelta(delta) {
510
+ const command = valueAt(delta, 'command');
511
+ if (Array.isArray(command))
512
+ return command.map((part) => typeof part === 'string' ? part : String(part)).join(' ');
513
+ return firstString(stringValue(delta, 'command'), stringValue(delta, 'cmd'));
514
+ }
515
+ function commandDeltaStatus(delta) {
516
+ const exitCode = valueAt(delta, 'exitCode') ?? valueAt(delta, 'exit_code');
517
+ const status = stringValue(delta, 'status');
518
+ const parts = [
519
+ status,
520
+ typeof exitCode === 'number' ? `exit ${exitCode}` : undefined,
521
+ ].filter(Boolean);
522
+ return parts.length > 0 ? parts.join(' / ') : undefined;
523
+ }
524
+ function fileChangesText(item) {
525
+ const changes = valueAt(item, 'changes');
526
+ if (!Array.isArray(changes))
527
+ return undefined;
528
+ const seen = new Set();
529
+ return changes
530
+ .map((change) => {
531
+ if (!isObject(change))
532
+ return '';
533
+ const path = stringValue(change, 'path');
534
+ const kind = stringValue(change, 'kind');
535
+ const diff = stringValue(change, 'diff');
536
+ const text = [path, kind, diff].filter(Boolean).join('\n');
537
+ if (!text || seen.has(text))
538
+ return '';
539
+ seen.add(text);
540
+ return text;
541
+ })
542
+ .filter(Boolean)
543
+ .join('\n\n');
544
+ }
545
+ function pathFromChanges(item) {
546
+ const changes = valueAt(item, 'changes');
547
+ if (!Array.isArray(changes))
548
+ return undefined;
549
+ const paths = [...new Set(changes.map((change) => isObject(change) ? stringValue(change, 'path') : undefined).filter(Boolean))];
550
+ return paths.length === 1 ? paths[0] : undefined;
551
+ }
552
+ function tokenUsageFromEvent(event) {
553
+ const json = parseJson(event.message);
554
+ if (!json)
555
+ return undefined;
556
+ if (event.type === 'tokens') {
557
+ return normalizeTokenUsage(json);
558
+ }
559
+ if (event.type !== 'codex-event' && !isCodexJsonEvent(event)) {
560
+ return undefined;
561
+ }
562
+ const cliType = stringValue(json, 'type');
563
+ const method = stringValue(json, 'method') || (cliType ? cliType.replace(/\./g, '/') : undefined) || event.stream;
564
+ if (method !== 'thread/tokenUsage/updated')
565
+ return undefined;
566
+ const params = objectValue(json, 'params') ?? json;
567
+ return normalizeTokenUsage(params);
568
+ }
569
+ function normalizeTokenUsage(tokenUsage) {
570
+ return objectValue(tokenUsage, 'tokenUsage') ?? tokenUsage;
571
+ }
572
+ function contextPressureFromUsage(tokenUsage) {
573
+ const usage = normalizeTokenUsage(tokenUsage);
574
+ const lastUsage = objectValue(usage, 'last');
575
+ const last = lastUsage ?? usage;
576
+ const contextWindow = numberValue(usage, 'modelContextWindow') ?? numberValue(usage, 'model_context_window');
577
+ const lastInputTokens = numberValue(last, 'inputTokens') ?? numberValue(last, 'input_tokens');
578
+ const flatInputTokens = lastUsage ? undefined : numberValue(usage, 'inputTokens') ?? numberValue(usage, 'input_tokens');
579
+ const usedTokens = lastInputTokens ?? flatInputTokens;
580
+ if (!usedTokens || !contextWindow || contextWindow <= 0)
581
+ return null;
582
+ const ratio = usedTokens / contextWindow;
583
+ const percent = Math.round(ratio * 100);
584
+ const tone = ratio >= 0.9 ? 'critical' : ratio >= 0.75 ? 'high' : ratio >= 0.5 ? 'medium' : 'low';
585
+ const source = lastUsage ? 'last-input' : 'reported-input';
586
+ return {
587
+ usedTokens,
588
+ contextWindow,
589
+ ratio,
590
+ percent,
591
+ tone,
592
+ source,
593
+ label: `Latest request context: ${percent}% (${usedTokens.toLocaleString()} / ${contextWindow.toLocaleString()} input tokens).`,
594
+ };
595
+ }
596
+ function tokenUsageText(tokenUsage) {
597
+ const usage = normalizeTokenUsage(tokenUsage);
598
+ const total = objectValue(usage, 'total') ?? usage;
599
+ const last = objectValue(usage, 'last') ?? usage;
600
+ const contextWindow = numberValue(usage, 'modelContextWindow') ?? numberValue(usage, 'model_context_window');
601
+ const totalTokens = numberValue(total, 'totalTokens') ?? numberValue(total, 'total_tokens');
602
+ const inputTokens = numberValue(last, 'inputTokens') ?? numberValue(last, 'input_tokens');
603
+ const cachedInputTokens = numberValue(last, 'cachedInputTokens') ?? numberValue(last, 'cached_input_tokens');
604
+ const outputTokens = numberValue(last, 'outputTokens') ?? numberValue(last, 'output_tokens');
605
+ const reasoningOutputTokens = numberValue(last, 'reasoningOutputTokens') ?? numberValue(last, 'reasoning_output_tokens');
606
+ const parts = [
607
+ totalTokens !== undefined ? `${totalTokens.toLocaleString()} total tokens` : undefined,
608
+ inputTokens !== undefined ? `${inputTokens.toLocaleString()} input` : undefined,
609
+ cachedInputTokens !== undefined ? `${cachedInputTokens.toLocaleString()} cached input` : undefined,
610
+ outputTokens !== undefined ? `${outputTokens.toLocaleString()} output` : undefined,
611
+ reasoningOutputTokens !== undefined ? `${reasoningOutputTokens.toLocaleString()} reasoning` : undefined,
612
+ contextWindow !== undefined ? `${contextWindow.toLocaleString()} context` : undefined,
613
+ ].filter(Boolean);
614
+ return parts.length > 0 ? `Token usage: ${parts.join(' / ')}.` : 'Token usage updated.';
615
+ }
616
+ function contentText(value) {
617
+ if (!Array.isArray(value))
618
+ return undefined;
619
+ return value
620
+ .map((entry) => isObject(entry) ? firstString(stringValue(entry, 'text'), stringValue(entry, 'summary')) : undefined)
621
+ .filter(Boolean)
622
+ .join('\n');
623
+ }
624
+ function decodedToolOutput(value) {
625
+ const json = parseJson(value ?? '');
626
+ return firstString(stringValue(json, 'output'), stringValue(json, 'message'), value);
627
+ }
628
+ function outputStatus(value) {
629
+ const metadata = objectValue(parseJson(value ?? ''), 'metadata');
630
+ const exitCode = valueAt(metadata, 'exit_code');
631
+ return typeof exitCode === 'number' ? `exit ${exitCode}` : undefined;
632
+ }
633
+ function isCommandToolName(name) {
634
+ return name === 'exec_command' || name === 'write_stdin';
635
+ }
636
+ export function readableJson(value) {
637
+ try {
638
+ return JSON.stringify(JSON.parse(value), null, 2);
639
+ }
640
+ catch {
641
+ return value;
642
+ }
643
+ }
644
+ function plainError(value) {
645
+ const json = parseJson(value);
646
+ return firstString(stringValue(json, 'message'), stringValue(json, 'error'), stringValue(objectValue(json, 'error'), 'message'), value) ?? value;
647
+ }
648
+ function parseJson(value) {
649
+ try {
650
+ const parsed = JSON.parse(value);
651
+ return isObject(parsed) ? parsed : undefined;
652
+ }
653
+ catch {
654
+ return undefined;
655
+ }
656
+ }
657
+ function objectValue(value, key) {
658
+ if (!isObject(value))
659
+ return undefined;
660
+ const child = value[key];
661
+ return isObject(child) ? child : undefined;
662
+ }
663
+ function valueAt(value, key) {
664
+ return isObject(value) ? value[key] : undefined;
665
+ }
666
+ function numberValue(value, key) {
667
+ const child = valueAt(value, key);
668
+ return typeof child === 'number' && Number.isFinite(child) ? child : undefined;
669
+ }
670
+ function stringValue(value, key) {
671
+ if (!isObject(value))
672
+ return undefined;
673
+ const child = value[key];
674
+ return typeof child === 'string' && child.length > 0 ? child : undefined;
675
+ }
676
+ function firstString(...values) {
677
+ return values.find((value) => value && value.length > 0);
678
+ }
679
+ function stringifyCompact(value) {
680
+ if (value === undefined || value === null)
681
+ return undefined;
682
+ if (typeof value === 'string')
683
+ return value;
684
+ try {
685
+ return JSON.stringify(value, null, 2);
686
+ }
687
+ catch {
688
+ return String(value);
689
+ }
690
+ }
691
+ function isObject(value) {
692
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
693
+ }
694
+ //# sourceMappingURL=trace.js.map