sidekick-shared 0.29.0 → 0.29.1
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/CHANGELOG.md +22 -0
- package/README.md +56 -25
- package/dist/aggregation/EventAggregator.d.ts +19 -0
- package/dist/aggregation/EventAggregator.js +174 -35
- package/dist/aggregation/snapshot.d.ts +37 -8
- package/dist/aggregation/snapshot.js +31 -9
- package/dist/aggregation/types.d.ts +13 -0
- package/dist/browser.d.ts +5 -1
- package/dist/browser.js +9 -3
- package/dist/context/sessionContext.js +4 -0
- package/dist/esm/aggregation/EventAggregator.js +174 -35
- package/dist/esm/aggregation/snapshot.js +31 -9
- package/dist/esm/browser.js +1 -0
- package/dist/esm/context/sessionContext.js +4 -0
- package/dist/esm/formatters/toolSummary.js +4 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/modelCatalog.js +5 -4
- package/dist/esm/modelContext.js +4 -3
- package/dist/esm/modelInfo.js +4 -3
- package/dist/esm/node.js +1 -1
- package/dist/esm/observedContextWindows.js +2 -1
- package/dist/esm/ownKey.js +26 -0
- package/dist/esm/parsers/codexParser.js +180 -4
- package/dist/esm/parsers/openCodeParser.js +2 -1
- package/dist/esm/parsers/subagentScanner.js +33 -12
- package/dist/esm/parsers/subagentTraceParser.js +97 -37
- package/dist/esm/pricingCatalog.js +3 -2
- package/dist/esm/providers/claudeCode.js +10 -2
- package/dist/esm/providers/codex.js +82 -38
- package/dist/esm/schemas/sessionEvent.js +35 -14
- package/dist/esm/subagentSpawn.js +163 -0
- package/dist/esm/transcript.js +4 -0
- package/dist/esm/turns/assistantTurn.js +3 -2
- package/dist/esm/usage/claudeUsageDedupe.js +68 -0
- package/dist/esm/watchers/eventBridge.js +15 -0
- package/dist/esm/watchers/factory.js +5 -3
- package/dist/esm/watchers/jsonlTail.js +73 -8
- package/dist/esm/watchers/jsonlWatcher.js +51 -4
- package/dist/esm/watchers/providerReaderWatcher.js +13 -0
- package/dist/esm/zaiQuota.js +2 -1
- package/dist/formatters/toolSummary.js +4 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.js +18 -9
- package/dist/modelCatalog.js +5 -4
- package/dist/modelContext.js +4 -3
- package/dist/modelInfo.js +4 -3
- package/dist/node.d.ts +1 -1
- package/dist/node.js +3 -1
- package/dist/observedContextWindows.js +2 -1
- package/dist/ownKey.d.ts +16 -0
- package/dist/ownKey.js +30 -0
- package/dist/parsers/codexParser.d.ts +63 -0
- package/dist/parsers/codexParser.js +181 -4
- package/dist/parsers/openCodeParser.js +2 -1
- package/dist/parsers/subagentScanner.d.ts +19 -6
- package/dist/parsers/subagentScanner.js +33 -12
- package/dist/parsers/subagentTraceParser.d.ts +25 -5
- package/dist/parsers/subagentTraceParser.js +97 -37
- package/dist/pricingCatalog.js +3 -2
- package/dist/providers/claudeCode.js +9 -1
- package/dist/providers/codex.js +82 -38
- package/dist/providers/types.d.ts +14 -0
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/sessionEvent.d.ts +27 -10
- package/dist/schemas/sessionEvent.js +35 -14
- package/dist/subagentSpawn.d.ts +80 -0
- package/dist/subagentSpawn.js +174 -0
- package/dist/transcript.js +4 -0
- package/dist/turns/assistantTurn.d.ts +1 -0
- package/dist/turns/assistantTurn.js +3 -2
- package/dist/usage/claudeUsageDedupe.d.ts +31 -0
- package/dist/usage/claudeUsageDedupe.js +68 -0
- package/dist/watchers/eventBridge.js +15 -0
- package/dist/watchers/factory.d.ts +6 -0
- package/dist/watchers/factory.js +5 -3
- package/dist/watchers/jsonlTail.d.ts +27 -4
- package/dist/watchers/jsonlTail.js +75 -8
- package/dist/watchers/jsonlWatcher.d.ts +18 -1
- package/dist/watchers/jsonlWatcher.js +50 -3
- package/dist/watchers/providerReaderWatcher.d.ts +7 -0
- package/dist/watchers/providerReaderWatcher.js +13 -0
- package/dist/watchers/types.d.ts +12 -0
- package/dist/zaiQuota.js +2 -1
- package/package.json +1 -1
package/dist/ownKey.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Own-property lookups for plain-object tables indexed by external strings.
|
|
3
|
+
*
|
|
4
|
+
* `table[key]` also finds inherited members, so a model id, tool name, or
|
|
5
|
+
* error code such as `constructor`, `toString`, or `__proto__` would return an
|
|
6
|
+
* `Object.prototype` member instead of `undefined`.
|
|
7
|
+
*
|
|
8
|
+
* @module ownKey
|
|
9
|
+
*/
|
|
10
|
+
/** The value stored under `key` in `table` itself, or `undefined`. */
|
|
11
|
+
export declare function ownValue<T>(table: Readonly<Record<string, T>>, key: string): T | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Store `value` as an own, enumerable property, even for `__proto__`, which a
|
|
14
|
+
* plain assignment would treat as a prototype change.
|
|
15
|
+
*/
|
|
16
|
+
export declare function setOwnValue<T>(table: Record<string, T>, key: string, value: T): void;
|
package/dist/ownKey.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Own-property lookups for plain-object tables indexed by external strings.
|
|
4
|
+
*
|
|
5
|
+
* `table[key]` also finds inherited members, so a model id, tool name, or
|
|
6
|
+
* error code such as `constructor`, `toString`, or `__proto__` would return an
|
|
7
|
+
* `Object.prototype` member instead of `undefined`.
|
|
8
|
+
*
|
|
9
|
+
* @module ownKey
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ownValue = ownValue;
|
|
13
|
+
exports.setOwnValue = setOwnValue;
|
|
14
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
15
|
+
/** The value stored under `key` in `table` itself, or `undefined`. */
|
|
16
|
+
function ownValue(table, key) {
|
|
17
|
+
return hasOwn.call(table, key) ? table[key] : undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Store `value` as an own, enumerable property, even for `__proto__`, which a
|
|
21
|
+
* plain assignment would treat as a prototype change.
|
|
22
|
+
*/
|
|
23
|
+
function setOwnValue(table, key, value) {
|
|
24
|
+
Object.defineProperty(table, key, {
|
|
25
|
+
value,
|
|
26
|
+
enumerable: true,
|
|
27
|
+
writable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -13,6 +13,21 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { SessionEvent } from '../types/sessionEvent';
|
|
15
15
|
import type { CodexRolloutLine, CodexSessionMeta, CodexTokenUsage, CodexRateLimits } from '../types/codex';
|
|
16
|
+
/** Pending exec command awaiting its end event. */
|
|
17
|
+
interface PendingExecCommand {
|
|
18
|
+
call_id: string;
|
|
19
|
+
command: string[];
|
|
20
|
+
workdir?: string;
|
|
21
|
+
timestamp: string;
|
|
22
|
+
}
|
|
23
|
+
/** Pending MCP tool call awaiting its end event. */
|
|
24
|
+
interface PendingMcpToolCall {
|
|
25
|
+
call_id: string;
|
|
26
|
+
server_name?: string;
|
|
27
|
+
tool_name: string;
|
|
28
|
+
arguments?: Record<string, unknown>;
|
|
29
|
+
timestamp: string;
|
|
30
|
+
}
|
|
16
31
|
/**
|
|
17
32
|
* Resolve the usage of the single call a Codex `token_count` event reports.
|
|
18
33
|
*
|
|
@@ -85,6 +100,35 @@ export interface CodexPendingStats {
|
|
|
85
100
|
/** Pending calls forgotten because `maxPendingBytes` was exceeded. */
|
|
86
101
|
evictedByBytes: number;
|
|
87
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* JSON-serializable continuation state of a {@link CodexRolloutParser}, from
|
|
105
|
+
* `exportResumeState()`, for `importResumeState()` in a parser that resumes
|
|
106
|
+
* at the same rollout position (for example from a persisted session
|
|
107
|
+
* snapshot). Every table lists its entries oldest first.
|
|
108
|
+
*/
|
|
109
|
+
export interface CodexRolloutResumeState {
|
|
110
|
+
version: 1;
|
|
111
|
+
sessionMeta: CodexSessionMeta | null;
|
|
112
|
+
/** Model from the latest `turn_context`, attributed to later events. */
|
|
113
|
+
currentModel: string | null;
|
|
114
|
+
/** Cumulative total of the previous token_count event, to skip its duplicates. */
|
|
115
|
+
prevTotalUsage: CodexTokenUsage | null;
|
|
116
|
+
lastTokenUsage: CodexTokenUsage | null;
|
|
117
|
+
modelContextWindow: number | null;
|
|
118
|
+
lastRateLimits: CodexRateLimits | null;
|
|
119
|
+
hasAggregateRateLimits: boolean;
|
|
120
|
+
inPlanMode: boolean;
|
|
121
|
+
/** Call ids whose tool use was already emitted, so their end event does not emit another. */
|
|
122
|
+
emittedToolUseIds: string[];
|
|
123
|
+
/** `apply_patch` calls awaiting output: call id and its per-file tool-use ids. */
|
|
124
|
+
patchExpandedToolUseIds: Array<[string, string[]]>;
|
|
125
|
+
/** `exec_command_begin` events awaiting their end. */
|
|
126
|
+
pendingExecCommands: PendingExecCommand[];
|
|
127
|
+
/** `mcp_tool_call_begin` events awaiting their end. */
|
|
128
|
+
pendingMcpToolCalls: PendingMcpToolCall[];
|
|
129
|
+
}
|
|
130
|
+
/** A valid `CodexTokenUsage` (copied), `null` for `null`, or `undefined` when invalid. Internal. */
|
|
131
|
+
export declare function codexTokenUsageFrom(value: unknown): CodexTokenUsage | null | undefined;
|
|
88
132
|
/**
|
|
89
133
|
* Stateful parser for Codex JSONL rollout lines.
|
|
90
134
|
*
|
|
@@ -130,6 +174,21 @@ export declare class CodexRolloutParser {
|
|
|
130
174
|
getModelContextWindow(): number | null;
|
|
131
175
|
/** Get the last observed rate limits from token_count events. */
|
|
132
176
|
getLastRateLimits(): CodexRateLimits | null;
|
|
177
|
+
/**
|
|
178
|
+
* The state later lines depend on — session metadata, current model,
|
|
179
|
+
* previous cumulative token total, context window, rate limits, plan mode,
|
|
180
|
+
* and the tool-call correlation tables (newest `limit` entries each) — as
|
|
181
|
+
* JSON-serializable data. Eviction counters are not included.
|
|
182
|
+
*/
|
|
183
|
+
exportResumeState(limit?: number): CodexRolloutResumeState;
|
|
184
|
+
/**
|
|
185
|
+
* Replaces this parser's state with one from {@link exportResumeState}, as
|
|
186
|
+
* if it had parsed the rollout up to where that state was taken. Returns
|
|
187
|
+
* false, changing nothing, when `state` is not a valid version-1 state.
|
|
188
|
+
* This parser's own `maxTrackedToolCalls`, `maxPendingBytes`, and
|
|
189
|
+
* `metadataOnly` options apply to the imported tables.
|
|
190
|
+
*/
|
|
191
|
+
importResumeState(state: unknown): boolean;
|
|
133
192
|
/**
|
|
134
193
|
* Converts a single rollout line to zero or more session events.
|
|
135
194
|
*/
|
|
@@ -142,6 +201,9 @@ export declare class CodexRolloutParser {
|
|
|
142
201
|
* first.
|
|
143
202
|
*/
|
|
144
203
|
private putPending;
|
|
204
|
+
private putPendingExec;
|
|
205
|
+
private putPendingMcp;
|
|
206
|
+
private putPendingPatch;
|
|
145
207
|
/** Removes and returns a pending call. */
|
|
146
208
|
private takePending;
|
|
147
209
|
private forgetPendingBytes;
|
|
@@ -164,3 +226,4 @@ export declare class CodexRolloutParser {
|
|
|
164
226
|
private handleEventMsg;
|
|
165
227
|
private handleTokenCount;
|
|
166
228
|
}
|
|
229
|
+
export {};
|
|
@@ -18,6 +18,7 @@ exports.resolveCodexCallUsage = resolveCodexCallUsage;
|
|
|
18
18
|
exports.normalizeCodexToolName = normalizeCodexToolName;
|
|
19
19
|
exports.normalizeCodexToolInput = normalizeCodexToolInput;
|
|
20
20
|
exports.extractPatchFilePaths = extractPatchFilePaths;
|
|
21
|
+
exports.codexTokenUsageFrom = codexTokenUsageFrom;
|
|
21
22
|
const codex_1 = require("../types/codex");
|
|
22
23
|
const openCodeParser_1 = require("./openCodeParser");
|
|
23
24
|
const usageNormalization_1 = require("../usageNormalization");
|
|
@@ -178,6 +179,61 @@ function extractPatchFilePaths(input) {
|
|
|
178
179
|
}
|
|
179
180
|
/** Default cap on each piece of per-call correlation state the parser keeps. */
|
|
180
181
|
exports.DEFAULT_MAX_TRACKED_TOOL_CALLS = 4096;
|
|
182
|
+
/** Entries per table {@link CodexRolloutParser.exportResumeState} keeps by default. */
|
|
183
|
+
const DEFAULT_RESUME_STATE_LIMIT = 256;
|
|
184
|
+
function isPlainObject(value) {
|
|
185
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
186
|
+
}
|
|
187
|
+
function cloneJson(value) {
|
|
188
|
+
return JSON.parse(JSON.stringify(value));
|
|
189
|
+
}
|
|
190
|
+
function isOptionalString(value) {
|
|
191
|
+
return value === undefined || typeof value === 'string';
|
|
192
|
+
}
|
|
193
|
+
function isStringArray(value) {
|
|
194
|
+
return Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
195
|
+
}
|
|
196
|
+
function isTokenCount(value) {
|
|
197
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
198
|
+
}
|
|
199
|
+
/** A valid `CodexTokenUsage` (copied), `null` for `null`, or `undefined` when invalid. Internal. */
|
|
200
|
+
function codexTokenUsageFrom(value) {
|
|
201
|
+
if (value === null)
|
|
202
|
+
return null;
|
|
203
|
+
if (!isPlainObject(value))
|
|
204
|
+
return undefined;
|
|
205
|
+
if (!isTokenCount(value.input_tokens) || !isTokenCount(value.output_tokens))
|
|
206
|
+
return undefined;
|
|
207
|
+
const usage = {
|
|
208
|
+
input_tokens: value.input_tokens,
|
|
209
|
+
output_tokens: value.output_tokens,
|
|
210
|
+
};
|
|
211
|
+
for (const field of ['cached_input_tokens', 'reasoning_output_tokens', 'total_tokens']) {
|
|
212
|
+
const count = value[field];
|
|
213
|
+
if (count === undefined)
|
|
214
|
+
continue;
|
|
215
|
+
if (!isTokenCount(count))
|
|
216
|
+
return undefined;
|
|
217
|
+
usage[field] = count;
|
|
218
|
+
}
|
|
219
|
+
return usage;
|
|
220
|
+
}
|
|
221
|
+
/** Last `limit` items of an insertion-ordered collection, oldest first. */
|
|
222
|
+
function newest(items, size, limit) {
|
|
223
|
+
const count = limit >= 0 ? Math.min(size, Math.floor(limit)) : 0;
|
|
224
|
+
const kept = [];
|
|
225
|
+
let skip = size - count;
|
|
226
|
+
for (const item of items) {
|
|
227
|
+
if (kept.length >= count)
|
|
228
|
+
break;
|
|
229
|
+
if (skip > 0) {
|
|
230
|
+
skip -= 1;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
kept.push(item);
|
|
234
|
+
}
|
|
235
|
+
return kept;
|
|
236
|
+
}
|
|
181
237
|
/** Fixed per-entry cost added to each pending call's byte estimate. */
|
|
182
238
|
const PENDING_ENTRY_OVERHEAD_BYTES = 64;
|
|
183
239
|
/** Prefix of the bookkeeping keys the parser stashes inside tool inputs. */
|
|
@@ -289,6 +345,118 @@ class CodexRolloutParser {
|
|
|
289
345
|
getLastRateLimits() {
|
|
290
346
|
return this.lastRateLimits;
|
|
291
347
|
}
|
|
348
|
+
/**
|
|
349
|
+
* The state later lines depend on — session metadata, current model,
|
|
350
|
+
* previous cumulative token total, context window, rate limits, plan mode,
|
|
351
|
+
* and the tool-call correlation tables (newest `limit` entries each) — as
|
|
352
|
+
* JSON-serializable data. Eviction counters are not included.
|
|
353
|
+
*/
|
|
354
|
+
exportResumeState(limit = DEFAULT_RESUME_STATE_LIMIT) {
|
|
355
|
+
return cloneJson({
|
|
356
|
+
version: 1,
|
|
357
|
+
sessionMeta: this.sessionMeta,
|
|
358
|
+
currentModel: this.currentModel,
|
|
359
|
+
prevTotalUsage: this.prevTotalUsage,
|
|
360
|
+
lastTokenUsage: this.lastTokenUsage,
|
|
361
|
+
modelContextWindow: this.modelContextWindow,
|
|
362
|
+
lastRateLimits: this.lastRateLimits,
|
|
363
|
+
hasAggregateRateLimits: this.hasAggregateRateLimits,
|
|
364
|
+
inPlanMode: this.inPlanMode,
|
|
365
|
+
emittedToolUseIds: newest(this.emittedToolUseIds, this.emittedToolUseIds.size, limit),
|
|
366
|
+
patchExpandedToolUseIds: newest(this.patchExpandedToolUseIds, this.patchExpandedToolUseIds.size, limit),
|
|
367
|
+
pendingExecCommands: newest(this.pendingExecCommands.values(), this.pendingExecCommands.size, limit),
|
|
368
|
+
pendingMcpToolCalls: newest(this.pendingMcpToolCalls.values(), this.pendingMcpToolCalls.size, limit),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Replaces this parser's state with one from {@link exportResumeState}, as
|
|
373
|
+
* if it had parsed the rollout up to where that state was taken. Returns
|
|
374
|
+
* false, changing nothing, when `state` is not a valid version-1 state.
|
|
375
|
+
* This parser's own `maxTrackedToolCalls`, `maxPendingBytes`, and
|
|
376
|
+
* `metadataOnly` options apply to the imported tables.
|
|
377
|
+
*/
|
|
378
|
+
importResumeState(state) {
|
|
379
|
+
if (!isPlainObject(state) || state.version !== 1)
|
|
380
|
+
return false;
|
|
381
|
+
const s = state;
|
|
382
|
+
const prevTotalUsage = codexTokenUsageFrom(s.prevTotalUsage);
|
|
383
|
+
const lastTokenUsage = codexTokenUsageFrom(s.lastTokenUsage);
|
|
384
|
+
if (prevTotalUsage === undefined || lastTokenUsage === undefined)
|
|
385
|
+
return false;
|
|
386
|
+
if (s.sessionMeta !== null && !isPlainObject(s.sessionMeta))
|
|
387
|
+
return false;
|
|
388
|
+
if (s.lastRateLimits !== null && !isPlainObject(s.lastRateLimits))
|
|
389
|
+
return false;
|
|
390
|
+
if (s.currentModel !== null && typeof s.currentModel !== 'string')
|
|
391
|
+
return false;
|
|
392
|
+
const contextWindow = s.modelContextWindow;
|
|
393
|
+
if (contextWindow !== null &&
|
|
394
|
+
!(typeof contextWindow === 'number' && Number.isFinite(contextWindow) && contextWindow > 0)) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
if (typeof s.hasAggregateRateLimits !== 'boolean' || typeof s.inPlanMode !== 'boolean') {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
if (!isStringArray(s.emittedToolUseIds))
|
|
401
|
+
return false;
|
|
402
|
+
const patches = s.patchExpandedToolUseIds;
|
|
403
|
+
if (!Array.isArray(patches) ||
|
|
404
|
+
!patches.every((entry) => Array.isArray(entry) &&
|
|
405
|
+
entry.length === 2 &&
|
|
406
|
+
typeof entry[0] === 'string' &&
|
|
407
|
+
isStringArray(entry[1]))) {
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
const execs = s.pendingExecCommands;
|
|
411
|
+
if (!Array.isArray(execs) ||
|
|
412
|
+
!execs.every((entry) => isPlainObject(entry) &&
|
|
413
|
+
typeof entry.call_id === 'string' &&
|
|
414
|
+
isStringArray(entry.command) &&
|
|
415
|
+
isOptionalString(entry.workdir) &&
|
|
416
|
+
typeof entry.timestamp === 'string')) {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
const mcps = s.pendingMcpToolCalls;
|
|
420
|
+
if (!Array.isArray(mcps) ||
|
|
421
|
+
!mcps.every((entry) => isPlainObject(entry) &&
|
|
422
|
+
typeof entry.call_id === 'string' &&
|
|
423
|
+
typeof entry.tool_name === 'string' &&
|
|
424
|
+
isOptionalString(entry.server_name) &&
|
|
425
|
+
(entry.arguments === undefined || isPlainObject(entry.arguments)) &&
|
|
426
|
+
typeof entry.timestamp === 'string')) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
const copy = cloneJson({
|
|
430
|
+
sessionMeta: s.sessionMeta,
|
|
431
|
+
lastRateLimits: s.lastRateLimits,
|
|
432
|
+
patches: patches,
|
|
433
|
+
execs: execs,
|
|
434
|
+
mcps: mcps,
|
|
435
|
+
});
|
|
436
|
+
this.reset();
|
|
437
|
+
this.sessionMeta = copy.sessionMeta;
|
|
438
|
+
this.currentModel = s.currentModel;
|
|
439
|
+
this.prevTotalUsage = prevTotalUsage;
|
|
440
|
+
this.lastTokenUsage = lastTokenUsage;
|
|
441
|
+
this.modelContextWindow = contextWindow;
|
|
442
|
+
this.lastRateLimits = copy.lastRateLimits;
|
|
443
|
+
this.hasAggregateRateLimits = s.hasAggregateRateLimits;
|
|
444
|
+
this.inPlanMode = s.inPlanMode;
|
|
445
|
+
for (const id of s.emittedToolUseIds) {
|
|
446
|
+
rememberBounded(this.emittedToolUseIds, id, this.maxTrackedToolCalls);
|
|
447
|
+
}
|
|
448
|
+
for (const [callId, toolUseIds] of copy.patches)
|
|
449
|
+
this.putPendingPatch(callId, toolUseIds);
|
|
450
|
+
for (const pending of copy.execs) {
|
|
451
|
+
this.putPendingExec(this.metadataOnly
|
|
452
|
+
? { call_id: pending.call_id, command: [], timestamp: pending.timestamp }
|
|
453
|
+
: pending);
|
|
454
|
+
}
|
|
455
|
+
for (const pending of copy.mcps) {
|
|
456
|
+
this.putPendingMcp(this.metadataOnly ? { ...pending, arguments: undefined } : pending);
|
|
457
|
+
}
|
|
458
|
+
return true;
|
|
459
|
+
}
|
|
292
460
|
/**
|
|
293
461
|
* Converts a single rollout line to zero or more session events.
|
|
294
462
|
*/
|
|
@@ -360,6 +528,16 @@ class CodexRolloutParser {
|
|
|
360
528
|
this.evictedByBytes += 1;
|
|
361
529
|
}
|
|
362
530
|
}
|
|
531
|
+
putPendingExec(pending) {
|
|
532
|
+
this.putPending(this.pendingExecCommands, pending.call_id, pending, () => stringBytes(pending.call_id, pending.workdir, pending.timestamp, ...(Array.isArray(pending.command) ? pending.command : [])));
|
|
533
|
+
}
|
|
534
|
+
putPendingMcp(pending) {
|
|
535
|
+
this.putPending(this.pendingMcpToolCalls, pending.call_id, pending, () => stringBytes(pending.call_id, pending.server_name, pending.tool_name, pending.timestamp) +
|
|
536
|
+
approxJsonBytes(pending.arguments));
|
|
537
|
+
}
|
|
538
|
+
putPendingPatch(callId, toolUseIds) {
|
|
539
|
+
this.putPending(this.patchExpandedToolUseIds, callId, toolUseIds, () => stringBytes(callId, ...toolUseIds));
|
|
540
|
+
}
|
|
363
541
|
/** Removes and returns a pending call. */
|
|
364
542
|
takePending(table, callId) {
|
|
365
543
|
const value = table.get(callId);
|
|
@@ -589,7 +767,7 @@ class CodexRolloutParser {
|
|
|
589
767
|
if (filePaths.length === 0)
|
|
590
768
|
return [];
|
|
591
769
|
const toolUseIds = filePaths.map((fp) => `${item.call_id}-${fp}`);
|
|
592
|
-
this.
|
|
770
|
+
this.putPendingPatch(item.call_id, toolUseIds);
|
|
593
771
|
for (const id of toolUseIds) {
|
|
594
772
|
rememberBounded(this.emittedToolUseIds, id, this.maxTrackedToolCalls);
|
|
595
773
|
}
|
|
@@ -739,7 +917,7 @@ class CodexRolloutParser {
|
|
|
739
917
|
const pending = this.metadataOnly
|
|
740
918
|
? { call_id: e.call_id, command: [], timestamp }
|
|
741
919
|
: { call_id: e.call_id, command: e.command, workdir: e.workdir, timestamp };
|
|
742
|
-
this.
|
|
920
|
+
this.putPendingExec(pending);
|
|
743
921
|
return [];
|
|
744
922
|
}
|
|
745
923
|
case 'exec_command_end': {
|
|
@@ -802,8 +980,7 @@ class CodexRolloutParser {
|
|
|
802
980
|
arguments: this.metadataOnly ? undefined : e.arguments,
|
|
803
981
|
timestamp,
|
|
804
982
|
};
|
|
805
|
-
this.
|
|
806
|
-
approxJsonBytes(pending.arguments));
|
|
983
|
+
this.putPendingMcp(pending);
|
|
807
984
|
return [];
|
|
808
985
|
}
|
|
809
986
|
case 'mcp_tool_call_end': {
|
|
@@ -22,6 +22,7 @@ exports.convertOpenCodeMessage = convertOpenCodeMessage;
|
|
|
22
22
|
exports.parseDbMessageData = parseDbMessageData;
|
|
23
23
|
exports.parseDbPartData = parseDbPartData;
|
|
24
24
|
const usageNormalization_1 = require("../usageNormalization");
|
|
25
|
+
const ownKey_1 = require("../ownKey");
|
|
25
26
|
/**
|
|
26
27
|
* OpenCode → Claude Code tool name mapping.
|
|
27
28
|
* OpenCode uses lowercase names; downstream code expects PascalCase.
|
|
@@ -57,7 +58,7 @@ function normalizeToolName(name) {
|
|
|
57
58
|
if (!name)
|
|
58
59
|
return name;
|
|
59
60
|
// Check mapping (case-insensitive lookup via lowercase key)
|
|
60
|
-
const mapped = TOOL_NAME_MAP
|
|
61
|
+
const mapped = (0, ownKey_1.ownValue)(TOOL_NAME_MAP, name.toLowerCase());
|
|
61
62
|
if (mapped)
|
|
62
63
|
return mapped;
|
|
63
64
|
// Already PascalCase (starts with uppercase)? Pass through.
|
|
@@ -8,15 +8,28 @@
|
|
|
8
8
|
* @module parsers/subagentScanner
|
|
9
9
|
*/
|
|
10
10
|
import type { SubagentStats, TaskState } from '../types/sessionEvent';
|
|
11
|
+
/** Fields of a Claude Code `agent-<id>.meta.json`; each is present only when well-typed. */
|
|
12
|
+
export interface AgentMeta {
|
|
13
|
+
agentType?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
/** The tool_use id of the `Agent` / `Task` call that spawned this agent. */
|
|
16
|
+
toolUseId?: string;
|
|
17
|
+
/** The agent that spawned this one, for spawns nested inside a subagent. */
|
|
18
|
+
parentAgentId?: string;
|
|
19
|
+
/** `'background'` for an agent launched in the background. */
|
|
20
|
+
requestShape?: string;
|
|
21
|
+
/** Written as `true` once a background agent is done (newer Claude Code). */
|
|
22
|
+
finished?: boolean;
|
|
23
|
+
/** The user stopped the agent. */
|
|
24
|
+
stoppedByUser?: boolean;
|
|
25
|
+
}
|
|
11
26
|
/**
|
|
12
27
|
* Reads `agent-<id>.meta.json` (written by Claude Code next to the transcript)
|
|
13
|
-
* for the spawn's agent type
|
|
14
|
-
* yield an empty object
|
|
28
|
+
* for the spawn's agent type, description, spawning tool_use id, parent agent,
|
|
29
|
+
* and completion flags. Missing or malformed files yield an empty object;
|
|
30
|
+
* wrongly typed fields are left out.
|
|
15
31
|
*/
|
|
16
|
-
export declare function readAgentMeta(agentFilePath: string):
|
|
17
|
-
agentType?: string;
|
|
18
|
-
description?: string;
|
|
19
|
-
};
|
|
32
|
+
export declare function readAgentMeta(agentFilePath: string): AgentMeta;
|
|
20
33
|
/**
|
|
21
34
|
* Scans subagent directory for agent JSONL files and extracts tool calls.
|
|
22
35
|
*
|
|
@@ -51,28 +51,49 @@ const path = __importStar(require("path"));
|
|
|
51
51
|
const claudeUsageDedupe_1 = require("../usage/claudeUsageDedupe");
|
|
52
52
|
const subagentUsage_1 = require("../usage/subagentUsage");
|
|
53
53
|
const subagentStatsCache_1 = require("./subagentStatsCache");
|
|
54
|
+
const subagentSpawn_1 = require("../subagentSpawn");
|
|
54
55
|
/**
|
|
55
56
|
* Pattern for matching subagent JSONL files.
|
|
56
57
|
* Files are named like: agent-<hash>.jsonl
|
|
57
58
|
*/
|
|
58
59
|
const AGENT_FILE_PATTERN = /^agent-(.+)\.jsonl$/;
|
|
60
|
+
function metaString(value) {
|
|
61
|
+
return typeof value === 'string' && value ? value : undefined;
|
|
62
|
+
}
|
|
59
63
|
/**
|
|
60
64
|
* Reads `agent-<id>.meta.json` (written by Claude Code next to the transcript)
|
|
61
|
-
* for the spawn's agent type
|
|
62
|
-
* yield an empty object
|
|
65
|
+
* for the spawn's agent type, description, spawning tool_use id, parent agent,
|
|
66
|
+
* and completion flags. Missing or malformed files yield an empty object;
|
|
67
|
+
* wrongly typed fields are left out.
|
|
63
68
|
*/
|
|
64
69
|
function readAgentMeta(agentFilePath) {
|
|
65
70
|
try {
|
|
66
71
|
const metaPath = agentFilePath.replace(/\.jsonl$/, '.meta.json');
|
|
67
72
|
const parsed = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
74
|
+
return {};
|
|
75
|
+
const record = parsed;
|
|
76
|
+
const meta = {};
|
|
77
|
+
const agentType = metaString(record.agentType);
|
|
78
|
+
if (agentType)
|
|
79
|
+
meta.agentType = agentType;
|
|
80
|
+
const description = metaString(record.description);
|
|
81
|
+
if (description)
|
|
82
|
+
meta.description = description;
|
|
83
|
+
const toolUseId = metaString(record.toolUseId);
|
|
84
|
+
if (toolUseId)
|
|
85
|
+
meta.toolUseId = toolUseId;
|
|
86
|
+
const parentAgentId = metaString(record.parentAgentId);
|
|
87
|
+
if (parentAgentId)
|
|
88
|
+
meta.parentAgentId = parentAgentId;
|
|
89
|
+
const requestShape = metaString(record.requestShape);
|
|
90
|
+
if (requestShape)
|
|
91
|
+
meta.requestShape = requestShape;
|
|
92
|
+
if (typeof record.finished === 'boolean')
|
|
93
|
+
meta.finished = record.finished;
|
|
94
|
+
if (typeof record.stoppedByUser === 'boolean')
|
|
95
|
+
meta.stoppedByUser = record.stoppedByUser;
|
|
96
|
+
return meta;
|
|
76
97
|
}
|
|
77
98
|
catch {
|
|
78
99
|
return {};
|
|
@@ -247,8 +268,8 @@ function parseAgentFile(filePath, agentId, log) {
|
|
|
247
268
|
}
|
|
248
269
|
}
|
|
249
270
|
toolCalls.push(toolCall);
|
|
250
|
-
// If this is
|
|
251
|
-
if (toolUse.name
|
|
271
|
+
// If this is an Agent/Task call (spawning another subagent), extract info
|
|
272
|
+
if ((0, subagentSpawn_1.isSubagentSpawnTool)(toolUse.name) && toolUse.input) {
|
|
252
273
|
if (toolUse.input.subagent_type && !agentType) {
|
|
253
274
|
agentType = String(toolUse.input.subagent_type);
|
|
254
275
|
}
|
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
* (not just stats) for drill-down trace visualization. Applies noise classification
|
|
6
6
|
* and tool summary formatting from the formatters module.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* 1.
|
|
10
|
-
* 2.
|
|
11
|
-
*
|
|
8
|
+
* Parent-child linking, exact links first:
|
|
9
|
+
* 1. Meta `parentAgentId` (Claude Code writes it for nested spawns)
|
|
10
|
+
* 2. The child's spawning tool_use id (meta `toolUseId`) found in another
|
|
11
|
+
* trace's `Agent` / `Task` calls
|
|
12
|
+
* Only traces without that spawn metadata (older Claude Code) fall back to:
|
|
13
|
+
* 3. Team summary: parse <teammate-message> XML blocks
|
|
14
|
+
* 4. Positional fallback: timestamp ordering + proximity
|
|
12
15
|
*
|
|
13
16
|
* @module parsers/subagentTraceParser
|
|
14
17
|
*/
|
|
@@ -41,8 +44,25 @@ export interface SubagentTrace {
|
|
|
41
44
|
children: SubagentTrace[];
|
|
42
45
|
/** Basic stats (tokens, duration, etc.) */
|
|
43
46
|
stats: SubagentStats;
|
|
44
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Parent tool_use_id that spawned this agent: the spawning `Agent` / `Task`
|
|
49
|
+
* call's id when Claude Code recorded it (also for top-level traces, whose
|
|
50
|
+
* spawn is in the main session). For a heuristic link without that
|
|
51
|
+
* metadata it holds the parent's agent id, as before; see `parentAgentId`.
|
|
52
|
+
*/
|
|
45
53
|
parentToolUseId?: string;
|
|
54
|
+
/** Path of this agent's `agent-<id>.jsonl` transcript. */
|
|
55
|
+
filePath?: string;
|
|
56
|
+
/** The tool_use id of the call that spawned this agent (meta `toolUseId`). */
|
|
57
|
+
spawnToolUseId?: string;
|
|
58
|
+
/** Agent id of the parent trace, for agents spawned by another subagent. */
|
|
59
|
+
parentAgentId?: string;
|
|
60
|
+
/** Launched in the background (meta `requestShape: 'background'`). */
|
|
61
|
+
background?: boolean;
|
|
62
|
+
/** Claude Code marked the background agent done (meta `finished`). */
|
|
63
|
+
finished?: boolean;
|
|
64
|
+
/** The user stopped the agent (meta `stoppedByUser`). */
|
|
65
|
+
stoppedByUser?: boolean;
|
|
46
66
|
}
|
|
47
67
|
/**
|
|
48
68
|
* Scans and parses subagent traces with full conversation events.
|