sidekick-shared 0.28.3 → 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 +86 -0
- package/README.md +188 -27
- 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/authStatus.d.ts +54 -0
- package/dist/authStatus.js +91 -0
- package/dist/browser.d.ts +16 -6
- package/dist/browser.js +24 -2
- package/dist/codex.d.ts +23 -0
- package/dist/codex.js +44 -0
- package/dist/codexProfiles.d.ts +1 -0
- package/dist/codexProfiles.js +8 -11
- 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/authStatus.js +87 -0
- package/dist/esm/browser.js +5 -2
- package/dist/esm/codex.js +18 -0
- package/dist/esm/codexProfiles.js +8 -11
- package/dist/esm/context/sessionContext.js +4 -0
- package/dist/esm/formatters/toolSummary.js +4 -1
- package/dist/esm/formatting.js +10 -1
- package/dist/esm/index.js +11 -6
- package/dist/esm/modelCatalog.js +7 -5
- package/dist/esm/modelContext.js +4 -3
- package/dist/esm/modelInfo.js +425 -47
- package/dist/esm/node.js +9 -3
- package/dist/esm/observedContextWindows.js +2 -1
- package/dist/esm/ownKey.js +26 -0
- package/dist/esm/parsers/codexParser.js +351 -54
- package/dist/esm/parsers/codexRolloutOrigin.js +107 -0
- package/dist/esm/parsers/openCodeParser.js +85 -22
- package/dist/esm/parsers/subagentScanner.js +33 -12
- package/dist/esm/parsers/subagentTraceParser.js +97 -37
- package/dist/esm/pricing.js +18 -0
- package/dist/esm/pricingCatalog.js +17 -9
- package/dist/esm/promptHistory.js +66 -4
- package/dist/esm/providerFailure.js +124 -3
- package/dist/esm/providers/claudeCode.js +10 -2
- package/dist/esm/providers/codex.js +84 -62
- package/dist/esm/providers/codexDatabase.js +3 -19
- package/dist/esm/providers/codexRollouts.js +24 -1
- package/dist/esm/providers/openCode.js +9 -17
- package/dist/esm/report/htmlReportGenerator.js +42 -5
- package/dist/esm/report/transcriptParser.js +13 -11
- package/dist/esm/schemas/providerFailure.js +3 -1
- package/dist/esm/schemas/sessionEvent.js +35 -14
- package/dist/esm/schemas/transcript.js +4 -0
- package/dist/esm/sessionPromptHistory.js +2 -2
- package/dist/esm/statusline/payload.js +9 -0
- package/dist/esm/subagentSpawn.js +163 -0
- package/dist/esm/testing/credentialGuard.js +87 -5
- package/dist/esm/toolCallOrigins.js +310 -0
- package/dist/esm/transcript.js +46 -3
- package/dist/esm/turns/assistantTurn.js +3 -2
- package/dist/esm/usage/claudeUsageDedupe.js +68 -0
- package/dist/esm/usage/usageEvents.js +146 -17
- package/dist/esm/usage/usageReports.js +39 -6
- package/dist/esm/watchers/eventBridge.js +15 -0
- package/dist/esm/watchers/factory.js +5 -3
- package/dist/esm/watchers/jsonlTail.js +442 -58
- 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/formatting.d.ts +26 -1
- package/dist/formatting.js +11 -1
- package/dist/index.d.ts +22 -11
- package/dist/index.js +44 -12
- package/dist/modelCatalog.js +7 -5
- package/dist/modelContext.js +4 -3
- package/dist/modelInfo.d.ts +185 -16
- package/dist/modelInfo.js +434 -49
- package/dist/node.d.ts +16 -5
- package/dist/node.js +33 -2
- 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 +118 -0
- package/dist/parsers/codexParser.js +352 -54
- package/dist/parsers/codexRolloutOrigin.d.ts +49 -0
- package/dist/parsers/codexRolloutOrigin.js +111 -0
- package/dist/parsers/openCodeParser.d.ts +38 -0
- package/dist/parsers/openCodeParser.js +88 -22
- 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/pricing.d.ts +22 -0
- package/dist/pricing.js +50 -0
- package/dist/pricingCatalog.d.ts +10 -0
- package/dist/pricingCatalog.js +16 -8
- package/dist/promptHistory.d.ts +65 -4
- package/dist/promptHistory.js +68 -5
- package/dist/providerFailure.d.ts +44 -0
- package/dist/providerFailure.js +126 -4
- package/dist/providers/claudeCode.js +9 -1
- package/dist/providers/codex.d.ts +3 -22
- package/dist/providers/codex.js +85 -64
- package/dist/providers/codexDatabase.d.ts +3 -2
- package/dist/providers/codexDatabase.js +4 -22
- package/dist/providers/codexRollouts.d.ts +22 -0
- package/dist/providers/codexRollouts.js +24 -0
- package/dist/providers/openCode.js +8 -16
- package/dist/providers/types.d.ts +14 -0
- package/dist/report/htmlReportGenerator.js +42 -5
- package/dist/report/transcriptParser.d.ts +10 -1
- package/dist/report/transcriptParser.js +13 -11
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/providerFailure.d.ts +39 -0
- package/dist/schemas/providerFailure.js +2 -0
- package/dist/schemas/sessionEvent.d.ts +27 -10
- package/dist/schemas/sessionEvent.js +35 -14
- package/dist/schemas/transcript.d.ts +2 -0
- package/dist/schemas/transcript.js +4 -0
- package/dist/sessionPromptHistory.d.ts +7 -1
- package/dist/sessionPromptHistory.js +1 -1
- package/dist/statusline/payload.d.ts +10 -0
- package/dist/statusline/payload.js +14 -0
- package/dist/subagentSpawn.d.ts +80 -0
- package/dist/subagentSpawn.js +174 -0
- package/dist/testing/credentialGuard.d.ts +14 -0
- package/dist/testing/credentialGuard.js +87 -5
- package/dist/toolCallOrigins.d.ts +70 -0
- package/dist/toolCallOrigins.js +346 -0
- package/dist/transcript.d.ts +12 -0
- package/dist/transcript.js +46 -3
- package/dist/turns/assistantTurn.d.ts +1 -0
- package/dist/turns/assistantTurn.js +3 -2
- package/dist/types/opencode.d.ts +2 -0
- package/dist/usage/claudeUsageDedupe.d.ts +31 -0
- package/dist/usage/claudeUsageDedupe.js +68 -0
- package/dist/usage/usageEvents.d.ts +54 -6
- package/dist/usage/usageEvents.js +146 -17
- package/dist/usage/usageReports.d.ts +35 -4
- package/dist/usage/usageReports.js +39 -6
- 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 +124 -4
- package/dist/watchers/jsonlTail.js +446 -59
- 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 +41 -1
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
11
|
+
/** The value stored under `key` in `table` itself, or `undefined`. */
|
|
12
|
+
export function ownValue(table, key) {
|
|
13
|
+
return hasOwn.call(table, key) ? table[key] : undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Store `value` as an own, enumerable property, even for `__proto__`, which a
|
|
17
|
+
* plain assignment would treat as a prototype change.
|
|
18
|
+
*/
|
|
19
|
+
export function setOwnValue(table, key, value) {
|
|
20
|
+
Object.defineProperty(table, key, {
|
|
21
|
+
value,
|
|
22
|
+
enumerable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -171,6 +171,65 @@ export function extractPatchFilePaths(input) {
|
|
|
171
171
|
}
|
|
172
172
|
/** Default cap on each piece of per-call correlation state the parser keeps. */
|
|
173
173
|
export const DEFAULT_MAX_TRACKED_TOOL_CALLS = 4096;
|
|
174
|
+
/** Entries per table {@link CodexRolloutParser.exportResumeState} keeps by default. */
|
|
175
|
+
const DEFAULT_RESUME_STATE_LIMIT = 256;
|
|
176
|
+
function isPlainObject(value) {
|
|
177
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
178
|
+
}
|
|
179
|
+
function cloneJson(value) {
|
|
180
|
+
return JSON.parse(JSON.stringify(value));
|
|
181
|
+
}
|
|
182
|
+
function isOptionalString(value) {
|
|
183
|
+
return value === undefined || typeof value === 'string';
|
|
184
|
+
}
|
|
185
|
+
function isStringArray(value) {
|
|
186
|
+
return Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
187
|
+
}
|
|
188
|
+
function isTokenCount(value) {
|
|
189
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
190
|
+
}
|
|
191
|
+
/** A valid `CodexTokenUsage` (copied), `null` for `null`, or `undefined` when invalid. Internal. */
|
|
192
|
+
export function codexTokenUsageFrom(value) {
|
|
193
|
+
if (value === null)
|
|
194
|
+
return null;
|
|
195
|
+
if (!isPlainObject(value))
|
|
196
|
+
return undefined;
|
|
197
|
+
if (!isTokenCount(value.input_tokens) || !isTokenCount(value.output_tokens))
|
|
198
|
+
return undefined;
|
|
199
|
+
const usage = {
|
|
200
|
+
input_tokens: value.input_tokens,
|
|
201
|
+
output_tokens: value.output_tokens,
|
|
202
|
+
};
|
|
203
|
+
for (const field of ['cached_input_tokens', 'reasoning_output_tokens', 'total_tokens']) {
|
|
204
|
+
const count = value[field];
|
|
205
|
+
if (count === undefined)
|
|
206
|
+
continue;
|
|
207
|
+
if (!isTokenCount(count))
|
|
208
|
+
return undefined;
|
|
209
|
+
usage[field] = count;
|
|
210
|
+
}
|
|
211
|
+
return usage;
|
|
212
|
+
}
|
|
213
|
+
/** Last `limit` items of an insertion-ordered collection, oldest first. */
|
|
214
|
+
function newest(items, size, limit) {
|
|
215
|
+
const count = limit >= 0 ? Math.min(size, Math.floor(limit)) : 0;
|
|
216
|
+
const kept = [];
|
|
217
|
+
let skip = size - count;
|
|
218
|
+
for (const item of items) {
|
|
219
|
+
if (kept.length >= count)
|
|
220
|
+
break;
|
|
221
|
+
if (skip > 0) {
|
|
222
|
+
skip -= 1;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
kept.push(item);
|
|
226
|
+
}
|
|
227
|
+
return kept;
|
|
228
|
+
}
|
|
229
|
+
/** Fixed per-entry cost added to each pending call's byte estimate. */
|
|
230
|
+
const PENDING_ENTRY_OVERHEAD_BYTES = 64;
|
|
231
|
+
/** Prefix of the bookkeeping keys the parser stashes inside tool inputs. */
|
|
232
|
+
const SIDEKICK_INPUT_KEY_PREFIX = '_sidekick';
|
|
174
233
|
/** Adds `key`, refreshing its position, and evicts the oldest past `max`. */
|
|
175
234
|
function rememberBounded(set, key, max) {
|
|
176
235
|
set.delete(key);
|
|
@@ -178,12 +237,31 @@ function rememberBounded(set, key, max) {
|
|
|
178
237
|
while (set.size > max)
|
|
179
238
|
set.delete(set.values().next().value);
|
|
180
239
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
240
|
+
function stringBytes(...values) {
|
|
241
|
+
let total = 0;
|
|
242
|
+
for (const value of values)
|
|
243
|
+
if (typeof value === 'string')
|
|
244
|
+
total += value.length;
|
|
245
|
+
return total;
|
|
246
|
+
}
|
|
247
|
+
function approxJsonBytes(value) {
|
|
248
|
+
if (value === undefined)
|
|
249
|
+
return 0;
|
|
250
|
+
try {
|
|
251
|
+
return JSON.stringify(value)?.length ?? 0;
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
return 0;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/** Only the `_sidekick*` bookkeeping keys of a tool input. */
|
|
258
|
+
function sidekickKeysOnly(input) {
|
|
259
|
+
const kept = {};
|
|
260
|
+
for (const key of Object.keys(input)) {
|
|
261
|
+
if (key.startsWith(SIDEKICK_INPUT_KEY_PREFIX))
|
|
262
|
+
kept[key] = input[key];
|
|
263
|
+
}
|
|
264
|
+
return kept;
|
|
187
265
|
}
|
|
188
266
|
/**
|
|
189
267
|
* Stateful parser for Codex JSONL rollout lines.
|
|
@@ -209,10 +287,35 @@ export class CodexRolloutParser {
|
|
|
209
287
|
emittedToolUseIds = new Set();
|
|
210
288
|
patchExpandedToolUseIds = new Map();
|
|
211
289
|
maxTrackedToolCalls;
|
|
290
|
+
/** Byte budget for pending calls; `null` = unlimited (no accounting). */
|
|
291
|
+
maxPendingBytes;
|
|
292
|
+
metadataOnly;
|
|
293
|
+
/** Pending calls in insertion order, for the byte budget. */
|
|
294
|
+
pendingLedger = new Map();
|
|
295
|
+
pendingBytes = 0;
|
|
296
|
+
evictedByCount = 0;
|
|
297
|
+
evictedByBytes = 0;
|
|
212
298
|
constructor(options = {}) {
|
|
213
299
|
const max = options.maxTrackedToolCalls;
|
|
214
300
|
this.maxTrackedToolCalls =
|
|
215
301
|
typeof max === 'number' && max >= 1 ? Math.floor(max) : DEFAULT_MAX_TRACKED_TOOL_CALLS;
|
|
302
|
+
const maxBytes = options.maxPendingBytes;
|
|
303
|
+
this.maxPendingBytes =
|
|
304
|
+
typeof maxBytes === 'number' && Number.isFinite(maxBytes) && maxBytes >= 1
|
|
305
|
+
? Math.floor(maxBytes)
|
|
306
|
+
: null;
|
|
307
|
+
this.metadataOnly = options.metadataOnly === true;
|
|
308
|
+
}
|
|
309
|
+
/** Counters for the pending tool-call state and what the caps evicted. */
|
|
310
|
+
getPendingStats() {
|
|
311
|
+
return {
|
|
312
|
+
pendingCalls: this.pendingExecCommands.size +
|
|
313
|
+
this.pendingMcpToolCalls.size +
|
|
314
|
+
this.patchExpandedToolUseIds.size,
|
|
315
|
+
pendingBytes: this.maxPendingBytes === null ? null : this.pendingBytes,
|
|
316
|
+
evictedByCount: this.evictedByCount,
|
|
317
|
+
evictedByBytes: this.evictedByBytes,
|
|
318
|
+
};
|
|
216
319
|
}
|
|
217
320
|
/** Get stored session metadata. */
|
|
218
321
|
getSessionMeta() {
|
|
@@ -234,6 +337,118 @@ export class CodexRolloutParser {
|
|
|
234
337
|
getLastRateLimits() {
|
|
235
338
|
return this.lastRateLimits;
|
|
236
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* The state later lines depend on — session metadata, current model,
|
|
342
|
+
* previous cumulative token total, context window, rate limits, plan mode,
|
|
343
|
+
* and the tool-call correlation tables (newest `limit` entries each) — as
|
|
344
|
+
* JSON-serializable data. Eviction counters are not included.
|
|
345
|
+
*/
|
|
346
|
+
exportResumeState(limit = DEFAULT_RESUME_STATE_LIMIT) {
|
|
347
|
+
return cloneJson({
|
|
348
|
+
version: 1,
|
|
349
|
+
sessionMeta: this.sessionMeta,
|
|
350
|
+
currentModel: this.currentModel,
|
|
351
|
+
prevTotalUsage: this.prevTotalUsage,
|
|
352
|
+
lastTokenUsage: this.lastTokenUsage,
|
|
353
|
+
modelContextWindow: this.modelContextWindow,
|
|
354
|
+
lastRateLimits: this.lastRateLimits,
|
|
355
|
+
hasAggregateRateLimits: this.hasAggregateRateLimits,
|
|
356
|
+
inPlanMode: this.inPlanMode,
|
|
357
|
+
emittedToolUseIds: newest(this.emittedToolUseIds, this.emittedToolUseIds.size, limit),
|
|
358
|
+
patchExpandedToolUseIds: newest(this.patchExpandedToolUseIds, this.patchExpandedToolUseIds.size, limit),
|
|
359
|
+
pendingExecCommands: newest(this.pendingExecCommands.values(), this.pendingExecCommands.size, limit),
|
|
360
|
+
pendingMcpToolCalls: newest(this.pendingMcpToolCalls.values(), this.pendingMcpToolCalls.size, limit),
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Replaces this parser's state with one from {@link exportResumeState}, as
|
|
365
|
+
* if it had parsed the rollout up to where that state was taken. Returns
|
|
366
|
+
* false, changing nothing, when `state` is not a valid version-1 state.
|
|
367
|
+
* This parser's own `maxTrackedToolCalls`, `maxPendingBytes`, and
|
|
368
|
+
* `metadataOnly` options apply to the imported tables.
|
|
369
|
+
*/
|
|
370
|
+
importResumeState(state) {
|
|
371
|
+
if (!isPlainObject(state) || state.version !== 1)
|
|
372
|
+
return false;
|
|
373
|
+
const s = state;
|
|
374
|
+
const prevTotalUsage = codexTokenUsageFrom(s.prevTotalUsage);
|
|
375
|
+
const lastTokenUsage = codexTokenUsageFrom(s.lastTokenUsage);
|
|
376
|
+
if (prevTotalUsage === undefined || lastTokenUsage === undefined)
|
|
377
|
+
return false;
|
|
378
|
+
if (s.sessionMeta !== null && !isPlainObject(s.sessionMeta))
|
|
379
|
+
return false;
|
|
380
|
+
if (s.lastRateLimits !== null && !isPlainObject(s.lastRateLimits))
|
|
381
|
+
return false;
|
|
382
|
+
if (s.currentModel !== null && typeof s.currentModel !== 'string')
|
|
383
|
+
return false;
|
|
384
|
+
const contextWindow = s.modelContextWindow;
|
|
385
|
+
if (contextWindow !== null &&
|
|
386
|
+
!(typeof contextWindow === 'number' && Number.isFinite(contextWindow) && contextWindow > 0)) {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
if (typeof s.hasAggregateRateLimits !== 'boolean' || typeof s.inPlanMode !== 'boolean') {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
if (!isStringArray(s.emittedToolUseIds))
|
|
393
|
+
return false;
|
|
394
|
+
const patches = s.patchExpandedToolUseIds;
|
|
395
|
+
if (!Array.isArray(patches) ||
|
|
396
|
+
!patches.every((entry) => Array.isArray(entry) &&
|
|
397
|
+
entry.length === 2 &&
|
|
398
|
+
typeof entry[0] === 'string' &&
|
|
399
|
+
isStringArray(entry[1]))) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
const execs = s.pendingExecCommands;
|
|
403
|
+
if (!Array.isArray(execs) ||
|
|
404
|
+
!execs.every((entry) => isPlainObject(entry) &&
|
|
405
|
+
typeof entry.call_id === 'string' &&
|
|
406
|
+
isStringArray(entry.command) &&
|
|
407
|
+
isOptionalString(entry.workdir) &&
|
|
408
|
+
typeof entry.timestamp === 'string')) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
const mcps = s.pendingMcpToolCalls;
|
|
412
|
+
if (!Array.isArray(mcps) ||
|
|
413
|
+
!mcps.every((entry) => isPlainObject(entry) &&
|
|
414
|
+
typeof entry.call_id === 'string' &&
|
|
415
|
+
typeof entry.tool_name === 'string' &&
|
|
416
|
+
isOptionalString(entry.server_name) &&
|
|
417
|
+
(entry.arguments === undefined || isPlainObject(entry.arguments)) &&
|
|
418
|
+
typeof entry.timestamp === 'string')) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
const copy = cloneJson({
|
|
422
|
+
sessionMeta: s.sessionMeta,
|
|
423
|
+
lastRateLimits: s.lastRateLimits,
|
|
424
|
+
patches: patches,
|
|
425
|
+
execs: execs,
|
|
426
|
+
mcps: mcps,
|
|
427
|
+
});
|
|
428
|
+
this.reset();
|
|
429
|
+
this.sessionMeta = copy.sessionMeta;
|
|
430
|
+
this.currentModel = s.currentModel;
|
|
431
|
+
this.prevTotalUsage = prevTotalUsage;
|
|
432
|
+
this.lastTokenUsage = lastTokenUsage;
|
|
433
|
+
this.modelContextWindow = contextWindow;
|
|
434
|
+
this.lastRateLimits = copy.lastRateLimits;
|
|
435
|
+
this.hasAggregateRateLimits = s.hasAggregateRateLimits;
|
|
436
|
+
this.inPlanMode = s.inPlanMode;
|
|
437
|
+
for (const id of s.emittedToolUseIds) {
|
|
438
|
+
rememberBounded(this.emittedToolUseIds, id, this.maxTrackedToolCalls);
|
|
439
|
+
}
|
|
440
|
+
for (const [callId, toolUseIds] of copy.patches)
|
|
441
|
+
this.putPendingPatch(callId, toolUseIds);
|
|
442
|
+
for (const pending of copy.execs) {
|
|
443
|
+
this.putPendingExec(this.metadataOnly
|
|
444
|
+
? { call_id: pending.call_id, command: [], timestamp: pending.timestamp }
|
|
445
|
+
: pending);
|
|
446
|
+
}
|
|
447
|
+
for (const pending of copy.mcps) {
|
|
448
|
+
this.putPendingMcp(this.metadataOnly ? { ...pending, arguments: undefined } : pending);
|
|
449
|
+
}
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
237
452
|
/**
|
|
238
453
|
* Converts a single rollout line to zero or more session events.
|
|
239
454
|
*/
|
|
@@ -267,6 +482,82 @@ export class CodexRolloutParser {
|
|
|
267
482
|
this.inPlanMode = false;
|
|
268
483
|
this.emittedToolUseIds.clear();
|
|
269
484
|
this.patchExpandedToolUseIds.clear();
|
|
485
|
+
this.pendingLedger.clear();
|
|
486
|
+
this.pendingBytes = 0;
|
|
487
|
+
this.evictedByCount = 0;
|
|
488
|
+
this.evictedByBytes = 0;
|
|
489
|
+
}
|
|
490
|
+
// --- Pending call state ---
|
|
491
|
+
/**
|
|
492
|
+
* Stores a pending call, refreshing its position, then enforces the count
|
|
493
|
+
* cap on its table and the byte budget across all pending tables, oldest
|
|
494
|
+
* first.
|
|
495
|
+
*/
|
|
496
|
+
putPending(table, callId, value, estimateBytes) {
|
|
497
|
+
this.forgetPendingBytes(table, callId);
|
|
498
|
+
table.delete(callId);
|
|
499
|
+
table.set(callId, value);
|
|
500
|
+
while (table.size > this.maxTrackedToolCalls) {
|
|
501
|
+
const oldest = table.keys().next().value;
|
|
502
|
+
table.delete(oldest);
|
|
503
|
+
this.forgetPendingBytes(table, oldest);
|
|
504
|
+
this.evictedByCount += 1;
|
|
505
|
+
}
|
|
506
|
+
if (this.maxPendingBytes === null)
|
|
507
|
+
return;
|
|
508
|
+
const bytes = PENDING_ENTRY_OVERHEAD_BYTES + estimateBytes();
|
|
509
|
+
this.pendingLedger.set(this.ledgerKey(table, callId), {
|
|
510
|
+
table: table,
|
|
511
|
+
callId,
|
|
512
|
+
bytes,
|
|
513
|
+
});
|
|
514
|
+
this.pendingBytes += bytes;
|
|
515
|
+
while (this.pendingBytes > this.maxPendingBytes && this.pendingLedger.size > 0) {
|
|
516
|
+
const [key, oldest] = this.pendingLedger.entries().next().value;
|
|
517
|
+
this.pendingLedger.delete(key);
|
|
518
|
+
this.pendingBytes -= oldest.bytes;
|
|
519
|
+
oldest.table.delete(oldest.callId);
|
|
520
|
+
this.evictedByBytes += 1;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
putPendingExec(pending) {
|
|
524
|
+
this.putPending(this.pendingExecCommands, pending.call_id, pending, () => stringBytes(pending.call_id, pending.workdir, pending.timestamp, ...(Array.isArray(pending.command) ? pending.command : [])));
|
|
525
|
+
}
|
|
526
|
+
putPendingMcp(pending) {
|
|
527
|
+
this.putPending(this.pendingMcpToolCalls, pending.call_id, pending, () => stringBytes(pending.call_id, pending.server_name, pending.tool_name, pending.timestamp) +
|
|
528
|
+
approxJsonBytes(pending.arguments));
|
|
529
|
+
}
|
|
530
|
+
putPendingPatch(callId, toolUseIds) {
|
|
531
|
+
this.putPending(this.patchExpandedToolUseIds, callId, toolUseIds, () => stringBytes(callId, ...toolUseIds));
|
|
532
|
+
}
|
|
533
|
+
/** Removes and returns a pending call. */
|
|
534
|
+
takePending(table, callId) {
|
|
535
|
+
const value = table.get(callId);
|
|
536
|
+
table.delete(callId);
|
|
537
|
+
this.forgetPendingBytes(table, callId);
|
|
538
|
+
return value;
|
|
539
|
+
}
|
|
540
|
+
forgetPendingBytes(table, callId) {
|
|
541
|
+
if (this.maxPendingBytes === null)
|
|
542
|
+
return;
|
|
543
|
+
const key = this.ledgerKey(table, callId);
|
|
544
|
+
const entry = this.pendingLedger.get(key);
|
|
545
|
+
if (!entry)
|
|
546
|
+
return;
|
|
547
|
+
this.pendingLedger.delete(key);
|
|
548
|
+
this.pendingBytes -= entry.bytes;
|
|
549
|
+
}
|
|
550
|
+
ledgerKey(table, callId) {
|
|
551
|
+
const kind = table === this.pendingExecCommands ? 'e' : table === this.pendingMcpToolCalls ? 'm' : 'p';
|
|
552
|
+
return `${kind}\u0000${callId}`;
|
|
553
|
+
}
|
|
554
|
+
/** The emitted tool input: unchanged, or only its `_sidekick*` keys in metadata-only mode. */
|
|
555
|
+
toolInput(input) {
|
|
556
|
+
return this.metadataOnly ? sidekickKeysOnly(input) : input;
|
|
557
|
+
}
|
|
558
|
+
/** The emitted tool-result content: unchanged, or empty in metadata-only mode. */
|
|
559
|
+
toolOutput(content) {
|
|
560
|
+
return this.metadataOnly ? '' : content;
|
|
270
561
|
}
|
|
271
562
|
// --- Handlers ---
|
|
272
563
|
handleSessionMeta(timestamp, payload) {
|
|
@@ -373,7 +664,9 @@ export class CodexRolloutParser {
|
|
|
373
664
|
role: 'assistant',
|
|
374
665
|
id: item.id,
|
|
375
666
|
model: this.currentModel || undefined,
|
|
376
|
-
content: [
|
|
667
|
+
content: [
|
|
668
|
+
{ type: 'thinking', thinking: this.metadataOnly ? '' : summaryTexts.join('\n') },
|
|
669
|
+
],
|
|
377
670
|
},
|
|
378
671
|
timestamp,
|
|
379
672
|
},
|
|
@@ -381,11 +674,13 @@ export class CodexRolloutParser {
|
|
|
381
674
|
}
|
|
382
675
|
handleFunctionCall(timestamp, item) {
|
|
383
676
|
let parsedArgs = {};
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
677
|
+
if (!this.metadataOnly) {
|
|
678
|
+
try {
|
|
679
|
+
parsedArgs = JSON.parse(item.arguments);
|
|
680
|
+
}
|
|
681
|
+
catch {
|
|
682
|
+
parsedArgs = { raw: item.arguments };
|
|
683
|
+
}
|
|
389
684
|
}
|
|
390
685
|
const canonicalName = normalizeCodexToolName(item.name);
|
|
391
686
|
const normalizedInput = normalizeCodexToolInput(canonicalName, item.name, parsedArgs);
|
|
@@ -402,7 +697,7 @@ export class CodexRolloutParser {
|
|
|
402
697
|
type: 'tool_use',
|
|
403
698
|
id: item.call_id,
|
|
404
699
|
name: canonicalName,
|
|
405
|
-
input: normalizedInput,
|
|
700
|
+
input: this.toolInput(normalizedInput),
|
|
406
701
|
},
|
|
407
702
|
],
|
|
408
703
|
},
|
|
@@ -422,7 +717,7 @@ export class CodexRolloutParser {
|
|
|
422
717
|
{
|
|
423
718
|
type: 'tool_result',
|
|
424
719
|
tool_use_id: item.call_id,
|
|
425
|
-
content: item.output,
|
|
720
|
+
content: this.toolOutput(item.output),
|
|
426
721
|
is_error: isError,
|
|
427
722
|
},
|
|
428
723
|
],
|
|
@@ -446,11 +741,11 @@ export class CodexRolloutParser {
|
|
|
446
741
|
type: 'tool_use',
|
|
447
742
|
id: item.call_id,
|
|
448
743
|
name: 'Bash',
|
|
449
|
-
input: {
|
|
744
|
+
input: this.toolInput({
|
|
450
745
|
command,
|
|
451
746
|
workdir: item.action?.workdir,
|
|
452
747
|
_sidekickRawToolName: 'local_shell_call',
|
|
453
|
-
},
|
|
748
|
+
}),
|
|
454
749
|
},
|
|
455
750
|
],
|
|
456
751
|
},
|
|
@@ -464,7 +759,7 @@ export class CodexRolloutParser {
|
|
|
464
759
|
if (filePaths.length === 0)
|
|
465
760
|
return [];
|
|
466
761
|
const toolUseIds = filePaths.map((fp) => `${item.call_id}-${fp}`);
|
|
467
|
-
|
|
762
|
+
this.putPendingPatch(item.call_id, toolUseIds);
|
|
468
763
|
for (const id of toolUseIds) {
|
|
469
764
|
rememberBounded(this.emittedToolUseIds, id, this.maxTrackedToolCalls);
|
|
470
765
|
}
|
|
@@ -479,10 +774,10 @@ export class CodexRolloutParser {
|
|
|
479
774
|
type: 'tool_use',
|
|
480
775
|
id: `${item.call_id}-${fp}`,
|
|
481
776
|
name: 'Edit',
|
|
482
|
-
input: {
|
|
777
|
+
input: this.toolInput({
|
|
483
778
|
file_path: fp,
|
|
484
779
|
_sidekickRawToolName: 'apply_patch',
|
|
485
|
-
},
|
|
780
|
+
}),
|
|
486
781
|
},
|
|
487
782
|
],
|
|
488
783
|
},
|
|
@@ -490,12 +785,14 @@ export class CodexRolloutParser {
|
|
|
490
785
|
}));
|
|
491
786
|
}
|
|
492
787
|
// Generic custom tool
|
|
493
|
-
let parsedInput;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
788
|
+
let parsedInput = {};
|
|
789
|
+
if (!this.metadataOnly) {
|
|
790
|
+
try {
|
|
791
|
+
parsedInput = JSON.parse(item.input);
|
|
792
|
+
}
|
|
793
|
+
catch {
|
|
794
|
+
parsedInput = { raw: item.input };
|
|
795
|
+
}
|
|
499
796
|
}
|
|
500
797
|
const canonicalName = normalizeCodexToolName(item.name);
|
|
501
798
|
const normalizedInput = normalizeCodexToolInput(canonicalName, item.name, parsedInput);
|
|
@@ -512,7 +809,7 @@ export class CodexRolloutParser {
|
|
|
512
809
|
type: 'tool_use',
|
|
513
810
|
id: item.call_id,
|
|
514
811
|
name: canonicalName,
|
|
515
|
-
input: normalizedInput,
|
|
812
|
+
input: this.toolInput(normalizedInput),
|
|
516
813
|
},
|
|
517
814
|
],
|
|
518
815
|
},
|
|
@@ -533,8 +830,9 @@ export class CodexRolloutParser {
|
|
|
533
830
|
catch {
|
|
534
831
|
isError = parsePlainTextFailure(item.output);
|
|
535
832
|
}
|
|
536
|
-
const toolUseIds = this.patchExpandedToolUseIds
|
|
537
|
-
|
|
833
|
+
const toolUseIds = this.takePending(this.patchExpandedToolUseIds, item.call_id) ?? [
|
|
834
|
+
item.call_id,
|
|
835
|
+
];
|
|
538
836
|
return toolUseIds.map((toolUseId) => ({
|
|
539
837
|
type: 'user',
|
|
540
838
|
message: {
|
|
@@ -544,7 +842,7 @@ export class CodexRolloutParser {
|
|
|
544
842
|
{
|
|
545
843
|
type: 'tool_result',
|
|
546
844
|
tool_use_id: toolUseId,
|
|
547
|
-
content: item.output,
|
|
845
|
+
content: this.toolOutput(item.output),
|
|
548
846
|
is_error: isError,
|
|
549
847
|
duration,
|
|
550
848
|
},
|
|
@@ -608,18 +906,15 @@ export class CodexRolloutParser {
|
|
|
608
906
|
// response_item/message events which carry richer metadata (id, content parts, role).
|
|
609
907
|
case 'exec_command_begin': {
|
|
610
908
|
const e = event;
|
|
611
|
-
|
|
612
|
-
call_id: e.call_id,
|
|
613
|
-
command: e.command,
|
|
614
|
-
|
|
615
|
-
timestamp,
|
|
616
|
-
}, this.maxTrackedToolCalls);
|
|
909
|
+
const pending = this.metadataOnly
|
|
910
|
+
? { call_id: e.call_id, command: [], timestamp }
|
|
911
|
+
: { call_id: e.call_id, command: e.command, workdir: e.workdir, timestamp };
|
|
912
|
+
this.putPendingExec(pending);
|
|
617
913
|
return [];
|
|
618
914
|
}
|
|
619
915
|
case 'exec_command_end': {
|
|
620
916
|
const e = event;
|
|
621
|
-
const pending = this.pendingExecCommands
|
|
622
|
-
this.pendingExecCommands.delete(e.call_id);
|
|
917
|
+
const pending = this.takePending(this.pendingExecCommands, e.call_id);
|
|
623
918
|
const command = pending?.command?.join(' ') || '';
|
|
624
919
|
const events = [];
|
|
625
920
|
if (!this.emittedToolUseIds.has(e.call_id)) {
|
|
@@ -635,18 +930,20 @@ export class CodexRolloutParser {
|
|
|
635
930
|
type: 'tool_use',
|
|
636
931
|
id: e.call_id,
|
|
637
932
|
name: 'Bash',
|
|
638
|
-
input: {
|
|
933
|
+
input: this.toolInput({
|
|
639
934
|
command,
|
|
640
935
|
workdir: pending?.workdir,
|
|
641
936
|
_sidekickRawToolName: 'exec_command',
|
|
642
|
-
},
|
|
937
|
+
}),
|
|
643
938
|
},
|
|
644
939
|
],
|
|
645
940
|
},
|
|
646
941
|
timestamp: pending?.timestamp || timestamp,
|
|
647
942
|
});
|
|
648
943
|
}
|
|
649
|
-
const output =
|
|
944
|
+
const output = this.metadataOnly
|
|
945
|
+
? ''
|
|
946
|
+
: [e.stdout, e.stderr].filter(Boolean).join('\n') || '';
|
|
650
947
|
events.push({
|
|
651
948
|
type: 'user',
|
|
652
949
|
message: {
|
|
@@ -668,19 +965,19 @@ export class CodexRolloutParser {
|
|
|
668
965
|
}
|
|
669
966
|
case 'mcp_tool_call_begin': {
|
|
670
967
|
const e = event;
|
|
671
|
-
|
|
968
|
+
const pending = {
|
|
672
969
|
call_id: e.call_id,
|
|
673
970
|
server_name: e.server_name,
|
|
674
971
|
tool_name: e.tool_name,
|
|
675
|
-
arguments: e.arguments,
|
|
972
|
+
arguments: this.metadataOnly ? undefined : e.arguments,
|
|
676
973
|
timestamp,
|
|
677
|
-
}
|
|
974
|
+
};
|
|
975
|
+
this.putPendingMcp(pending);
|
|
678
976
|
return [];
|
|
679
977
|
}
|
|
680
978
|
case 'mcp_tool_call_end': {
|
|
681
979
|
const e = event;
|
|
682
|
-
const pendingMcp = this.pendingMcpToolCalls
|
|
683
|
-
this.pendingMcpToolCalls.delete(e.call_id);
|
|
980
|
+
const pendingMcp = this.takePending(this.pendingMcpToolCalls, e.call_id);
|
|
684
981
|
const toolName = pendingMcp?.tool_name || 'McpTool';
|
|
685
982
|
const events = [];
|
|
686
983
|
if (!this.emittedToolUseIds.has(e.call_id)) {
|
|
@@ -696,11 +993,11 @@ export class CodexRolloutParser {
|
|
|
696
993
|
type: 'tool_use',
|
|
697
994
|
id: e.call_id,
|
|
698
995
|
name: normalizeCodexToolName(toolName),
|
|
699
|
-
input: {
|
|
996
|
+
input: this.toolInput({
|
|
700
997
|
...(pendingMcp?.arguments || {}),
|
|
701
998
|
_sidekickRawToolName: toolName,
|
|
702
999
|
_sidekickMcpServerName: pendingMcp?.server_name,
|
|
703
|
-
},
|
|
1000
|
+
}),
|
|
704
1001
|
},
|
|
705
1002
|
],
|
|
706
1003
|
},
|
|
@@ -716,7 +1013,7 @@ export class CodexRolloutParser {
|
|
|
716
1013
|
{
|
|
717
1014
|
type: 'tool_result',
|
|
718
1015
|
tool_use_id: e.call_id,
|
|
719
|
-
content: e.result || '',
|
|
1016
|
+
content: this.toolOutput(e.result || ''),
|
|
720
1017
|
is_error: e.is_error || false,
|
|
721
1018
|
duration: e.duration_ms,
|
|
722
1019
|
},
|
|
@@ -775,12 +1072,12 @@ export class CodexRolloutParser {
|
|
|
775
1072
|
type: 'tool_use',
|
|
776
1073
|
id: patchId,
|
|
777
1074
|
name: 'Edit',
|
|
778
|
-
input: {
|
|
1075
|
+
input: this.toolInput({
|
|
779
1076
|
file_path: e.file_path,
|
|
780
1077
|
additions: e.additions ?? 0,
|
|
781
1078
|
deletions: e.deletions ?? 0,
|
|
782
1079
|
_sidekickRawToolName: 'patch_applied',
|
|
783
|
-
},
|
|
1080
|
+
}),
|
|
784
1081
|
},
|
|
785
1082
|
],
|
|
786
1083
|
},
|
|
@@ -807,10 +1104,10 @@ export class CodexRolloutParser {
|
|
|
807
1104
|
type: 'tool_use',
|
|
808
1105
|
id: `plan-enter-${timestamp}`,
|
|
809
1106
|
name: 'EnterPlanMode',
|
|
810
|
-
input: {
|
|
1107
|
+
input: this.toolInput({
|
|
811
1108
|
source: 'codex_task_started',
|
|
812
1109
|
_sidekickRawToolName: 'task_started',
|
|
813
|
-
},
|
|
1110
|
+
}),
|
|
814
1111
|
},
|
|
815
1112
|
],
|
|
816
1113
|
},
|
|
@@ -834,10 +1131,10 @@ export class CodexRolloutParser {
|
|
|
834
1131
|
type: 'tool_use',
|
|
835
1132
|
id: `plan-exit-${timestamp}`,
|
|
836
1133
|
name: 'ExitPlanMode',
|
|
837
|
-
input: {
|
|
1134
|
+
input: this.toolInput({
|
|
838
1135
|
source: 'codex_task_complete',
|
|
839
1136
|
_sidekickRawToolName: 'task_complete',
|
|
840
|
-
},
|
|
1137
|
+
}),
|
|
841
1138
|
},
|
|
842
1139
|
],
|
|
843
1140
|
},
|