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
package/dist/providers/codex.js
CHANGED
|
@@ -43,8 +43,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
43
43
|
};
|
|
44
44
|
})();
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
-
exports.CodexProvider = void 0;
|
|
47
|
-
exports.findCodexRolloutFile = findCodexRolloutFile;
|
|
46
|
+
exports.CodexProvider = exports.findCodexRolloutFile = void 0;
|
|
48
47
|
const fs = __importStar(require("fs"));
|
|
49
48
|
const path = __importStar(require("path"));
|
|
50
49
|
const string_decoder_1 = require("string_decoder");
|
|
@@ -56,6 +55,7 @@ const subagentStatsCache_1 = require("../parsers/subagentStatsCache");
|
|
|
56
55
|
const rolloutWalker_1 = require("./rolloutWalker");
|
|
57
56
|
const codexPaths_1 = require("../codexPaths");
|
|
58
57
|
const codexRollouts_1 = require("./codexRollouts");
|
|
58
|
+
Object.defineProperty(exports, "findCodexRolloutFile", { enumerable: true, get: function () { return codexRollouts_1.findCodexRolloutFile; } });
|
|
59
59
|
const directoryListingCache_1 = require("./directoryListingCache");
|
|
60
60
|
const diagnostics_1 = require("./diagnostics");
|
|
61
61
|
const modelContext_1 = require("../modelContext");
|
|
@@ -84,29 +84,6 @@ function hasRolloutFile(dir) {
|
|
|
84
84
|
function findRolloutFilesInConfiguredHomes(options = {}) {
|
|
85
85
|
return (0, rolloutWalker_1.walkRolloutFiles)((0, codexRollouts_1.getSessionsDirs)(), options);
|
|
86
86
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Locates the rollout file for a Codex session id by walking
|
|
89
|
-
* `<home>/sessions/YYYY/MM/DD/rollout-<timestamp>-<id>.jsonl`, then
|
|
90
|
-
* `<home>/archived_sessions/`, where Codex moves the rollouts of archived
|
|
91
|
-
* threads.
|
|
92
|
-
*
|
|
93
|
-
* Filename-only match, case-insensitive on the id — no file contents are read
|
|
94
|
-
* and files are only stat'ed once their name matches, so the walk stays cheap
|
|
95
|
-
* even over a large history. When the same id exists in more than one home,
|
|
96
|
-
* the most recently modified copy wins. Returns `null` for an unknown, empty,
|
|
97
|
-
* or whitespace id. Consumers who want the database's answer instead can query
|
|
98
|
-
* the already-public `CodexDatabase` (requires the external `sqlite3` binary).
|
|
99
|
-
*/
|
|
100
|
-
function findCodexRolloutFile(sessionId, options = {}) {
|
|
101
|
-
const normalizedId = sessionId.trim().toLowerCase();
|
|
102
|
-
if (!normalizedId)
|
|
103
|
-
return null;
|
|
104
|
-
const sessionsDirs = options.codexHome
|
|
105
|
-
? (0, codexPaths_1.getCodexSessionsDirs)([options.codexHome])
|
|
106
|
-
: (0, codexRollouts_1.getSessionsDirs)();
|
|
107
|
-
// Name-only filter, one walk across the homes, newest modification first.
|
|
108
|
-
return (0, rolloutWalker_1.walkRolloutFiles)(sessionsDirs, { sessionId: normalizedId })[0]?.path ?? null;
|
|
109
|
-
}
|
|
110
87
|
/** Pick the best sessions directory for UI/search entrypoints. */
|
|
111
88
|
function getSessionsDir() {
|
|
112
89
|
const dirs = (0, codexPaths_1.getCodexSessionsDirs)(undefined, { includeArchived: false });
|
|
@@ -329,6 +306,12 @@ class CodexReader {
|
|
|
329
306
|
lineBuffer = '';
|
|
330
307
|
_wasTruncated = false;
|
|
331
308
|
decoder = new string_decoder_1.StringDecoder('utf8');
|
|
309
|
+
/**
|
|
310
|
+
* Position from `seekTo()` whose prefix still has to be parsed for model,
|
|
311
|
+
* turn, and cumulative-token context; `restoreResumeState()` supplies that
|
|
312
|
+
* context instead and clears it.
|
|
313
|
+
*/
|
|
314
|
+
pendingSeek = null;
|
|
332
315
|
constructor(rolloutPath, onContextWindowLimit, onRateLimits, onTokenUsage) {
|
|
333
316
|
this.rolloutPath = rolloutPath;
|
|
334
317
|
this.onContextWindowLimit = onContextWindowLimit;
|
|
@@ -338,9 +321,11 @@ class CodexReader {
|
|
|
338
321
|
}
|
|
339
322
|
/** Get the parser's session metadata. */
|
|
340
323
|
getSessionMeta() {
|
|
324
|
+
this.settleSeek();
|
|
341
325
|
return this.parser.getSessionMeta();
|
|
342
326
|
}
|
|
343
327
|
readNew(endPosition) {
|
|
328
|
+
this.settleSeek();
|
|
344
329
|
this._wasTruncated = false;
|
|
345
330
|
const events = [];
|
|
346
331
|
try {
|
|
@@ -394,47 +379,51 @@ class CodexReader {
|
|
|
394
379
|
// Skip malformed JSON lines
|
|
395
380
|
}
|
|
396
381
|
}
|
|
397
|
-
|
|
398
|
-
// The model comes along so the value can be persisted per model — the
|
|
399
|
-
// window Codex reports is tier-specific, not the published maximum.
|
|
400
|
-
const mcw = this.parser.getModelContextWindow();
|
|
401
|
-
if (mcw && this.onContextWindowLimit) {
|
|
402
|
-
this.onContextWindowLimit(mcw, this.parser.getCurrentModel() ?? undefined);
|
|
403
|
-
}
|
|
404
|
-
// Propagate rate_limits from token_count events to the provider
|
|
405
|
-
const rateLimits = this.parser.getLastRateLimits();
|
|
406
|
-
if (rateLimits && this.onRateLimits) {
|
|
407
|
-
this.onRateLimits(rateLimits);
|
|
408
|
-
}
|
|
409
|
-
// Propagate last token usage snapshot to the provider
|
|
410
|
-
const lastUsage = this.parser.getLastTokenUsage();
|
|
411
|
-
if (lastUsage && this.onTokenUsage) {
|
|
412
|
-
const normalized = (0, usageNormalization_1.normalizeProviderUsage)({
|
|
413
|
-
semantics: 'openai',
|
|
414
|
-
provider: 'openai',
|
|
415
|
-
source: 'codex-reader-snapshot',
|
|
416
|
-
model: this.parser.getCurrentModel() || undefined,
|
|
417
|
-
inputTokens: lastUsage.input_tokens,
|
|
418
|
-
outputTokens: lastUsage.output_tokens,
|
|
419
|
-
cacheReadTokens: lastUsage.cached_input_tokens,
|
|
420
|
-
reasoningTokens: lastUsage.reasoning_output_tokens,
|
|
421
|
-
});
|
|
422
|
-
this.onTokenUsage({
|
|
423
|
-
inputTokens: normalized.uncachedInputTokens,
|
|
424
|
-
outputTokens: normalized.outputTokens,
|
|
425
|
-
cacheWriteTokens: normalized.cacheWriteTokens,
|
|
426
|
-
cacheReadTokens: normalized.cacheReadTokens,
|
|
427
|
-
reasoningTokens: normalized.reasoningTokens,
|
|
428
|
-
model: this.parser.getCurrentModel() || 'unknown',
|
|
429
|
-
timestamp: new Date(),
|
|
430
|
-
});
|
|
431
|
-
}
|
|
382
|
+
this.propagateParserState();
|
|
432
383
|
}
|
|
433
384
|
catch {
|
|
434
385
|
// Skip read errors
|
|
435
386
|
}
|
|
436
387
|
return events;
|
|
437
388
|
}
|
|
389
|
+
/** Hands the parser's context window, rate limits, and last usage to the provider. */
|
|
390
|
+
propagateParserState() {
|
|
391
|
+
// Propagate model_context_window from token_count events to the provider.
|
|
392
|
+
// The model comes along so the value can be persisted per model — the
|
|
393
|
+
// window Codex reports is tier-specific, not the published maximum.
|
|
394
|
+
const mcw = this.parser.getModelContextWindow();
|
|
395
|
+
if (mcw && this.onContextWindowLimit) {
|
|
396
|
+
this.onContextWindowLimit(mcw, this.parser.getCurrentModel() ?? undefined);
|
|
397
|
+
}
|
|
398
|
+
// Propagate rate_limits from token_count events to the provider
|
|
399
|
+
const rateLimits = this.parser.getLastRateLimits();
|
|
400
|
+
if (rateLimits && this.onRateLimits) {
|
|
401
|
+
this.onRateLimits(rateLimits);
|
|
402
|
+
}
|
|
403
|
+
// Propagate last token usage snapshot to the provider
|
|
404
|
+
const lastUsage = this.parser.getLastTokenUsage();
|
|
405
|
+
if (lastUsage && this.onTokenUsage) {
|
|
406
|
+
const normalized = (0, usageNormalization_1.normalizeProviderUsage)({
|
|
407
|
+
semantics: 'openai',
|
|
408
|
+
provider: 'openai',
|
|
409
|
+
source: 'codex-reader-snapshot',
|
|
410
|
+
model: this.parser.getCurrentModel() || undefined,
|
|
411
|
+
inputTokens: lastUsage.input_tokens,
|
|
412
|
+
outputTokens: lastUsage.output_tokens,
|
|
413
|
+
cacheReadTokens: lastUsage.cached_input_tokens,
|
|
414
|
+
reasoningTokens: lastUsage.reasoning_output_tokens,
|
|
415
|
+
});
|
|
416
|
+
this.onTokenUsage({
|
|
417
|
+
inputTokens: normalized.uncachedInputTokens,
|
|
418
|
+
outputTokens: normalized.outputTokens,
|
|
419
|
+
cacheWriteTokens: normalized.cacheWriteTokens,
|
|
420
|
+
cacheReadTokens: normalized.cacheReadTokens,
|
|
421
|
+
reasoningTokens: normalized.reasoningTokens,
|
|
422
|
+
model: this.parser.getCurrentModel() || 'unknown',
|
|
423
|
+
timestamp: new Date(),
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
438
427
|
readAll() {
|
|
439
428
|
this.reset();
|
|
440
429
|
return this.readNew();
|
|
@@ -446,11 +435,13 @@ class CodexReader {
|
|
|
446
435
|
this.parser.reset();
|
|
447
436
|
this.decoder = new string_decoder_1.StringDecoder('utf8');
|
|
448
437
|
this._wasTruncated = false;
|
|
438
|
+
this.pendingSeek = null;
|
|
449
439
|
}
|
|
450
440
|
exists() {
|
|
451
441
|
return fs.existsSync(this.rolloutPath);
|
|
452
442
|
}
|
|
453
443
|
flush() {
|
|
444
|
+
this.settleSeek();
|
|
454
445
|
this.lineBuffer += this.decoder.end();
|
|
455
446
|
// Process any remaining data in the line buffer
|
|
456
447
|
if (this.lineBuffer.trim()) {
|
|
@@ -468,15 +459,45 @@ class CodexReader {
|
|
|
468
459
|
return this.committedPosition;
|
|
469
460
|
}
|
|
470
461
|
seekTo(position) {
|
|
471
|
-
// Rebuild model, turn, and cumulative-token context before resuming.
|
|
472
|
-
// The prefix is parsed only; its events are already in the consumer snapshot.
|
|
473
462
|
this.reset();
|
|
463
|
+
this.filePosition = position;
|
|
464
|
+
this.committedPosition = position;
|
|
465
|
+
// The prefix's context is rebuilt on the next read, unless
|
|
466
|
+
// restoreResumeState() supplies it first.
|
|
474
467
|
if (position > 0)
|
|
475
|
-
this.
|
|
468
|
+
this.pendingSeek = position;
|
|
476
469
|
}
|
|
477
470
|
wasTruncated() {
|
|
478
471
|
return this._wasTruncated;
|
|
479
472
|
}
|
|
473
|
+
/** The parser's continuation state (see `CodexRolloutParser.exportResumeState`). */
|
|
474
|
+
getResumeState() {
|
|
475
|
+
this.settleSeek();
|
|
476
|
+
return this.parser.exportResumeState();
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Restores parser state from `getResumeState()`; after `seekTo()` this
|
|
480
|
+
* replaces re-parsing the rollout up to that position.
|
|
481
|
+
*/
|
|
482
|
+
restoreResumeState(state) {
|
|
483
|
+
if (!this.parser.importResumeState(state))
|
|
484
|
+
return false;
|
|
485
|
+
this.pendingSeek = null;
|
|
486
|
+
this.propagateParserState();
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Rebuilds model, turn, and cumulative-token context for a pending
|
|
491
|
+
* `seekTo()` position. The prefix is parsed only; its events are already
|
|
492
|
+
* in the consumer snapshot.
|
|
493
|
+
*/
|
|
494
|
+
settleSeek() {
|
|
495
|
+
const position = this.pendingSeek;
|
|
496
|
+
if (position === null)
|
|
497
|
+
return;
|
|
498
|
+
this.reset();
|
|
499
|
+
this.readNew(position);
|
|
500
|
+
}
|
|
480
501
|
}
|
|
481
502
|
// ---------------------------------------------------------------------------
|
|
482
503
|
// CodexProvider — full SessionProviderBase implementation
|
|
@@ -634,7 +655,7 @@ class CodexProvider {
|
|
|
634
655
|
}
|
|
635
656
|
findSessionById(_workspacePath, sessionId) {
|
|
636
657
|
try {
|
|
637
|
-
const found = findCodexRolloutFile(sessionId);
|
|
658
|
+
const found = (0, codexRollouts_1.findCodexRolloutFile)(sessionId);
|
|
638
659
|
this.recordDatabaseFallback('findSessionById', true);
|
|
639
660
|
return found;
|
|
640
661
|
}
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { CodexDbThread } from '../types/codex';
|
|
6
6
|
import type { ProviderRuntimeStatus } from './types';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { codexSpawnParentFromSource as spawnParentFromSource } from '../parsers/codexRolloutOrigin';
|
|
8
|
+
/** Parent thread id of a spawned subagent's `source`; one rule shared with rollout parsing. */
|
|
9
|
+
export { spawnParentFromSource };
|
|
9
10
|
export declare class CodexDatabase {
|
|
10
11
|
private readonly dbPath;
|
|
11
12
|
private sqlite3Available;
|
|
@@ -37,35 +37,17 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
};
|
|
38
38
|
})();
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
exports.CodexDatabase = void 0;
|
|
41
|
-
exports.spawnParentFromSource = spawnParentFromSource;
|
|
40
|
+
exports.CodexDatabase = exports.spawnParentFromSource = void 0;
|
|
42
41
|
const fs = __importStar(require("fs"));
|
|
43
42
|
const path = __importStar(require("path"));
|
|
44
43
|
const child_process_1 = require("child_process");
|
|
44
|
+
const codexRolloutOrigin_1 = require("../parsers/codexRolloutOrigin");
|
|
45
|
+
Object.defineProperty(exports, "spawnParentFromSource", { enumerable: true, get: function () { return codexRolloutOrigin_1.codexSpawnParentFromSource; } });
|
|
45
46
|
/**
|
|
46
47
|
* Ids per inlined `IN (…)` query. Values are passed inside a single argv
|
|
47
48
|
* element to sqlite3, so an unbounded list would hit E2BIG on large stores.
|
|
48
49
|
*/
|
|
49
50
|
const SQL_ID_CHUNK_SIZE = 400;
|
|
50
|
-
/** Parent thread id recorded in a Codex `threads.source` JSON value, if any. */
|
|
51
|
-
function spawnParentFromSource(source) {
|
|
52
|
-
let parsed = source;
|
|
53
|
-
if (typeof source === 'string') {
|
|
54
|
-
if (!source.startsWith('{'))
|
|
55
|
-
return undefined;
|
|
56
|
-
try {
|
|
57
|
-
parsed = JSON.parse(source);
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (!parsed || typeof parsed !== 'object')
|
|
64
|
-
return undefined;
|
|
65
|
-
const parent = parsed
|
|
66
|
-
.subagent?.thread_spawn?.parent_thread_id;
|
|
67
|
-
return typeof parent === 'string' ? parent : undefined;
|
|
68
|
-
}
|
|
69
51
|
class CodexDatabase {
|
|
70
52
|
dbPath;
|
|
71
53
|
sqlite3Available = null;
|
|
@@ -246,7 +228,7 @@ class CodexDatabase {
|
|
|
246
228
|
if (!this.getThreadColumns().has('source'))
|
|
247
229
|
return [];
|
|
248
230
|
const rows = this.query('SELECT * FROM threads WHERE source LIKE ? ORDER BY created_at ASC', [`%${parentId}%`]);
|
|
249
|
-
return rows.filter((row) =>
|
|
231
|
+
return rows.filter((row) => (0, codexRolloutOrigin_1.codexSpawnParentFromSource)(row.source) === parentId);
|
|
250
232
|
}
|
|
251
233
|
/** Get the database file's mtime (ms epoch). Returns 0 if unavailable. */
|
|
252
234
|
getDbMtime() {
|
|
@@ -42,3 +42,25 @@ export declare function normalizePath(input: string): string;
|
|
|
42
42
|
* sits inside it.
|
|
43
43
|
*/
|
|
44
44
|
export declare function listCodexSessionFilesAsync(workspacePath?: string, options?: ListSessionFilesOptions, cache?: DirectoryListingCache): Promise<SessionFileInfo[]>;
|
|
45
|
+
export interface FindCodexRolloutFileOptions {
|
|
46
|
+
/**
|
|
47
|
+
* Codex home whose `sessions/` subtree to search. Defaults to every
|
|
48
|
+
* monitored home: `$CODEX_HOME` if set, else `~/.codex`, plus managed
|
|
49
|
+
* profile homes that recorded sessions.
|
|
50
|
+
*/
|
|
51
|
+
codexHome?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Locates the rollout file for a Codex session id by walking
|
|
55
|
+
* `<home>/sessions/YYYY/MM/DD/rollout-<timestamp>-<id>.jsonl`, then
|
|
56
|
+
* `<home>/archived_sessions/`, where Codex moves the rollouts of archived
|
|
57
|
+
* threads.
|
|
58
|
+
*
|
|
59
|
+
* Filename-only match, case-insensitive on the id — no file contents are read
|
|
60
|
+
* and files are only stat'ed once their name matches, so the walk stays cheap
|
|
61
|
+
* even over a large history. When the same id exists in more than one home,
|
|
62
|
+
* the most recently modified copy wins. Returns `null` for an unknown, empty,
|
|
63
|
+
* or whitespace id. Consumers who want the database's answer instead can query
|
|
64
|
+
* the already-public `CodexDatabase` (requires the external `sqlite3` binary).
|
|
65
|
+
*/
|
|
66
|
+
export declare function findCodexRolloutFile(sessionId: string, options?: FindCodexRolloutFileOptions): string | null;
|
|
@@ -50,6 +50,7 @@ exports.initializeCounters = initializeCounters;
|
|
|
50
50
|
exports.cwdMatches = cwdMatches;
|
|
51
51
|
exports.normalizePath = normalizePath;
|
|
52
52
|
exports.listCodexSessionFilesAsync = listCodexSessionFilesAsync;
|
|
53
|
+
exports.findCodexRolloutFile = findCodexRolloutFile;
|
|
53
54
|
const fs = __importStar(require("fs"));
|
|
54
55
|
const path = __importStar(require("path"));
|
|
55
56
|
const codexPaths_1 = require("../codexPaths");
|
|
@@ -259,3 +260,26 @@ async function listCodexSessionFilesAsync(workspacePath, options = {}, cache = n
|
|
|
259
260
|
}
|
|
260
261
|
return results;
|
|
261
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Locates the rollout file for a Codex session id by walking
|
|
265
|
+
* `<home>/sessions/YYYY/MM/DD/rollout-<timestamp>-<id>.jsonl`, then
|
|
266
|
+
* `<home>/archived_sessions/`, where Codex moves the rollouts of archived
|
|
267
|
+
* threads.
|
|
268
|
+
*
|
|
269
|
+
* Filename-only match, case-insensitive on the id — no file contents are read
|
|
270
|
+
* and files are only stat'ed once their name matches, so the walk stays cheap
|
|
271
|
+
* even over a large history. When the same id exists in more than one home,
|
|
272
|
+
* the most recently modified copy wins. Returns `null` for an unknown, empty,
|
|
273
|
+
* or whitespace id. Consumers who want the database's answer instead can query
|
|
274
|
+
* the already-public `CodexDatabase` (requires the external `sqlite3` binary).
|
|
275
|
+
*/
|
|
276
|
+
function findCodexRolloutFile(sessionId, options = {}) {
|
|
277
|
+
const normalizedId = sessionId.trim().toLowerCase();
|
|
278
|
+
if (!normalizedId)
|
|
279
|
+
return null;
|
|
280
|
+
const sessionsDirs = options.codexHome
|
|
281
|
+
? (0, codexPaths_1.getCodexSessionsDirs)([options.codexHome])
|
|
282
|
+
: getSessionsDirs();
|
|
283
|
+
// Name-only filter, one walk across the homes, newest modification first.
|
|
284
|
+
return (0, rolloutWalker_1.walkRolloutFiles)(sessionsDirs, { sessionId: normalizedId })[0]?.path ?? null;
|
|
285
|
+
}
|
|
@@ -60,7 +60,6 @@ const sessionContext_1 = require("../context/sessionContext");
|
|
|
60
60
|
const sessionStats_1 = require("../sessionStats");
|
|
61
61
|
const openCodeParser_1 = require("../parsers/openCodeParser");
|
|
62
62
|
const openCodeDatabase_1 = require("./openCodeDatabase");
|
|
63
|
-
const usageNormalization_1 = require("../usageNormalization");
|
|
64
63
|
const subagentUsage_1 = require("../usage/subagentUsage");
|
|
65
64
|
const zaiQuota_1 = require("../zaiQuota");
|
|
66
65
|
const diagnostics_1 = require("./diagnostics");
|
|
@@ -1493,21 +1492,14 @@ class OpenCodeProvider {
|
|
|
1493
1492
|
if (tokens) {
|
|
1494
1493
|
// Same semantics as the main-session parser (openCodeParser).
|
|
1495
1494
|
const cache = tokens.cache;
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
inputTokens: tokens.input,
|
|
1505
|
-
outputTokens: tokens.output,
|
|
1506
|
-
cacheReadTokens: cache?.read,
|
|
1507
|
-
cacheWriteTokens: cache?.write,
|
|
1508
|
-
reasoningTokens: tokens.reasoning,
|
|
1509
|
-
reasoningIncludedInOutput: providerID === 'openai',
|
|
1510
|
-
}));
|
|
1495
|
+
(0, subagentUsage_1.addUsageToSubagent)(tokenTotals, (0, openCodeParser_1.normalizeOpenCodeUsage)({
|
|
1496
|
+
input: tokens.input,
|
|
1497
|
+
output: tokens.output,
|
|
1498
|
+
cacheRead: cache?.read,
|
|
1499
|
+
cacheWrite: cache?.write,
|
|
1500
|
+
reasoning: tokens.reasoning,
|
|
1501
|
+
total: tokens.total,
|
|
1502
|
+
}, msgData.providerID));
|
|
1511
1503
|
}
|
|
1512
1504
|
}
|
|
1513
1505
|
catch {
|
|
@@ -186,6 +186,20 @@ export interface SessionReader {
|
|
|
186
186
|
seekTo(position: number): void;
|
|
187
187
|
/** Check if file was truncated (size < position). */
|
|
188
188
|
wasTruncated(): boolean;
|
|
189
|
+
/**
|
|
190
|
+
* Parse state the reader needs, beyond `getPosition()`, to continue
|
|
191
|
+
* correctly after a restart: JSON-serializable data to persist together
|
|
192
|
+
* with that position (for example which Claude responses were already
|
|
193
|
+
* counted). `undefined` when the reader keeps none. Optional.
|
|
194
|
+
*/
|
|
195
|
+
getResumeState?(): unknown;
|
|
196
|
+
/**
|
|
197
|
+
* Restores state from `getResumeState()`. Call it right after `seekTo()`
|
|
198
|
+
* the position that state was taken at. Returns false, changing nothing,
|
|
199
|
+
* when `state` is missing or not valid for this reader; the caller should
|
|
200
|
+
* then replay from the start instead. Optional.
|
|
201
|
+
*/
|
|
202
|
+
restoreResumeState?(state: unknown): boolean;
|
|
189
203
|
}
|
|
190
204
|
/**
|
|
191
205
|
* Expanded session provider interface (no VS Code dependencies).
|
|
@@ -599,14 +599,51 @@ function renderContentBlock(block, includeThinking, includeToolDetail) {
|
|
|
599
599
|
return '';
|
|
600
600
|
}
|
|
601
601
|
}
|
|
602
|
-
|
|
602
|
+
/** Prefix of the bookkeeping keys session parsers stash inside tool inputs. */
|
|
603
|
+
const SIDEKICK_INPUT_KEY_PREFIX = '_sidekick';
|
|
604
|
+
/**
|
|
605
|
+
* The tool's display identity and user-facing input. Entries built outside
|
|
606
|
+
* the canonical projection may still carry the parsers' `_sidekick*` keys in
|
|
607
|
+
* `toolInput`; those are hidden here and read as the raw identity.
|
|
608
|
+
*/
|
|
609
|
+
function toolDisplay(block) {
|
|
603
610
|
const name = block.toolName || 'unknown';
|
|
611
|
+
const raw = block.toolInput;
|
|
612
|
+
let input = raw;
|
|
613
|
+
let rawToolName = block.rawToolName;
|
|
614
|
+
let mcpServerName = block.mcpServerName;
|
|
615
|
+
if (raw && Object.keys(raw).some((key) => key.startsWith(SIDEKICK_INPUT_KEY_PREFIX))) {
|
|
616
|
+
const clean = {};
|
|
617
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
618
|
+
if (!key.startsWith(SIDEKICK_INPUT_KEY_PREFIX))
|
|
619
|
+
clean[key] = value;
|
|
620
|
+
}
|
|
621
|
+
input = clean;
|
|
622
|
+
if (!rawToolName && typeof raw._sidekickRawToolName === 'string') {
|
|
623
|
+
rawToolName = raw._sidekickRawToolName;
|
|
624
|
+
}
|
|
625
|
+
if (!mcpServerName && typeof raw._sidekickMcpServerName === 'string') {
|
|
626
|
+
mcpServerName = raw._sidekickMcpServerName;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
const parts = [];
|
|
630
|
+
if (rawToolName && rawToolName.trim() && rawToolName !== name)
|
|
631
|
+
parts.push(rawToolName);
|
|
632
|
+
if (mcpServerName && mcpServerName.trim())
|
|
633
|
+
parts.push(`MCP: ${mcpServerName}`);
|
|
634
|
+
const hint = parts.length
|
|
635
|
+
? ` <span class="tool-raw-name" style="color:var(--text-muted);font-size:11px">${(0, htmlHelpers_1.escapeHtml)(parts.join(' · '))}</span>`
|
|
636
|
+
: '';
|
|
637
|
+
return { name, hint, input };
|
|
638
|
+
}
|
|
639
|
+
function renderToolUse(block, includeDetail) {
|
|
640
|
+
const { name, hint, input } = toolDisplay(block);
|
|
604
641
|
if (!includeDetail) {
|
|
605
|
-
return `<div class="tool-block" data-block-type="tool"><div style="padding:8px 12px;background:rgba(245,166,35,0.08);color:var(--accent-orange);font-family:monospace;font-size:13px;">${(0, htmlHelpers_1.escapeHtml)(name)}</div></div>`;
|
|
642
|
+
return `<div class="tool-block" data-block-type="tool"><div style="padding:8px 12px;background:rgba(245,166,35,0.08);color:var(--accent-orange);font-family:monospace;font-size:13px;">${(0, htmlHelpers_1.escapeHtml)(name)}${hint}</div></div>`;
|
|
606
643
|
}
|
|
607
644
|
let inputHtml = '';
|
|
608
|
-
if (
|
|
609
|
-
const inputJson = JSON.stringify(
|
|
645
|
+
if (input && Object.keys(input).length > 0) {
|
|
646
|
+
const inputJson = JSON.stringify(input, null, 2);
|
|
610
647
|
const highlighted = (0, htmlHelpers_1.highlightCodeBlock)(inputJson, 'json');
|
|
611
648
|
inputHtml = `<div class="tool-io">
|
|
612
649
|
<div class="tool-label">Input</div>
|
|
@@ -614,7 +651,7 @@ function renderToolUse(block, includeDetail) {
|
|
|
614
651
|
</div>`;
|
|
615
652
|
}
|
|
616
653
|
return `<details class="tool-block" data-block-type="tool">
|
|
617
|
-
<summary>${(0, htmlHelpers_1.escapeHtml)(name)}${block.toolUseId ? ` <span style="color:var(--text-muted);font-size:11px">${(0, htmlHelpers_1.escapeHtml)(block.toolUseId)}</span>` : ''}</summary>
|
|
654
|
+
<summary>${(0, htmlHelpers_1.escapeHtml)(name)}${hint}${block.toolUseId ? ` <span style="color:var(--text-muted);font-size:11px">${(0, htmlHelpers_1.escapeHtml)(block.toolUseId)}</span>` : ''}</summary>
|
|
618
655
|
<div class="tool-content">
|
|
619
656
|
${inputHtml}
|
|
620
657
|
</div>
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/** Report compatibility adapters over the canonical transcript projection. */
|
|
2
2
|
import type { SessionEvent } from '../types/sessionEvent';
|
|
3
|
-
import type { TranscriptEntry } from './types';
|
|
3
|
+
import type { TranscriptEntry, TranscriptContentBlock } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* A report tool block that also names the provider's raw tool and MCP
|
|
6
|
+
* server. Internal: the extra fields are optional and read only by the HTML
|
|
7
|
+
* renderer.
|
|
8
|
+
*/
|
|
9
|
+
export type ReportToolUseBlock = TranscriptContentBlock & {
|
|
10
|
+
rawToolName?: string;
|
|
11
|
+
mcpServerName?: string;
|
|
12
|
+
};
|
|
4
13
|
/** Parse a Claude Code JSONL session through the canonical provider reader. */
|
|
5
14
|
export declare function parseTranscript(sessionPath: string): TranscriptEntry[];
|
|
6
15
|
/** Project canonical provider events into the legacy report transcript shape. */
|
|
@@ -79,17 +79,19 @@ function toReportBlock(block) {
|
|
|
79
79
|
return block.text ? [{ type: 'text', text: block.text }] : [];
|
|
80
80
|
case 'thinking':
|
|
81
81
|
return block.text ? [{ type: 'thinking', text: block.text }] : [];
|
|
82
|
-
case 'tool_use':
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
},
|
|
92
|
-
|
|
82
|
+
case 'tool_use': {
|
|
83
|
+
const toolBlock = {
|
|
84
|
+
type: 'tool_use',
|
|
85
|
+
toolName: block.toolName ?? 'unknown',
|
|
86
|
+
toolInput: block.input && typeof block.input === 'object'
|
|
87
|
+
? block.input
|
|
88
|
+
: {},
|
|
89
|
+
toolUseId: block.toolUseId,
|
|
90
|
+
...(block.rawToolName ? { rawToolName: block.rawToolName } : {}),
|
|
91
|
+
...(block.mcpServerName ? { mcpServerName: block.mcpServerName } : {}),
|
|
92
|
+
};
|
|
93
|
+
return [toolBlock];
|
|
94
|
+
}
|
|
93
95
|
case 'tool_result':
|
|
94
96
|
return [
|
|
95
97
|
{
|
package/dist/schemas/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export { ACCOUNT_PROVIDER_IDS, QUOTA_PROVIDER_IDS, RUNTIME_QUOTA_PROVIDER_IDS, SESSION_PROVIDER_IDS, } from '../providerIds';
|
|
14
14
|
export { messageUsageSchema, sessionMessageSchema, sessionEventSchema, permissionModeSchema, extractSessionEvents, userSessionEventSchema, assistantSessionEventSchema, summarySessionEventSchema, systemSessionEventSchema, toolUseSessionEventSchema, toolResultSessionEventSchema, } from './sessionEvent';
|
|
15
|
+
export type { ExtractSessionEventsOptions } from './sessionEvent';
|
|
15
16
|
export type { MessageUsage, SessionMessage, SessionEvent, PermissionMode, } from '../types/sessionEvent';
|
|
16
17
|
export { normalizedUsageSchema, normalizedUsageCostSchema } from './usageNormalization';
|
|
17
18
|
export type { NormalizedUsage, NormalizedUsageCost } from '../usageNormalization';
|
|
@@ -52,6 +52,25 @@ export declare const providerFailureEvidenceSchema: z.ZodObject<{
|
|
|
52
52
|
context_overflow: "context_overflow";
|
|
53
53
|
}>;
|
|
54
54
|
checkedAt: z.ZodOptional<z.ZodString>;
|
|
55
|
+
codexErrorInfo: z.ZodOptional<z.ZodEnum<{
|
|
56
|
+
other: "other";
|
|
57
|
+
contextWindowExceeded: "contextWindowExceeded";
|
|
58
|
+
sessionBudgetExceeded: "sessionBudgetExceeded";
|
|
59
|
+
usageLimitExceeded: "usageLimitExceeded";
|
|
60
|
+
rateLimitExceeded: "rateLimitExceeded";
|
|
61
|
+
serverOverloaded: "serverOverloaded";
|
|
62
|
+
cyberPolicy: "cyberPolicy";
|
|
63
|
+
httpConnectionFailed: "httpConnectionFailed";
|
|
64
|
+
responseStreamConnectionFailed: "responseStreamConnectionFailed";
|
|
65
|
+
internalServerError: "internalServerError";
|
|
66
|
+
unauthorized: "unauthorized";
|
|
67
|
+
badRequest: "badRequest";
|
|
68
|
+
threadRollbackFailed: "threadRollbackFailed";
|
|
69
|
+
sandboxError: "sandboxError";
|
|
70
|
+
responseStreamDisconnected: "responseStreamDisconnected";
|
|
71
|
+
responseTooManyFailedAttempts: "responseTooManyFailedAttempts";
|
|
72
|
+
activeTurnNotSteerable: "activeTurnNotSteerable";
|
|
73
|
+
}>>;
|
|
55
74
|
}, z.core.$strip>;
|
|
56
75
|
export declare const providerRetryAfterSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
57
76
|
kind: z.ZodLiteral<"delay">;
|
|
@@ -98,6 +117,7 @@ export declare const providerFailureDiagnosisSchema: z.ZodObject<{
|
|
|
98
117
|
reduce_context: "reduce_context";
|
|
99
118
|
inspect_error: "inspect_error";
|
|
100
119
|
}>;
|
|
120
|
+
retryable: z.ZodOptional<z.ZodBoolean>;
|
|
101
121
|
evidence: z.ZodArray<z.ZodObject<{
|
|
102
122
|
source: z.ZodEnum<{
|
|
103
123
|
message: "message";
|
|
@@ -122,6 +142,25 @@ export declare const providerFailureDiagnosisSchema: z.ZodObject<{
|
|
|
122
142
|
context_overflow: "context_overflow";
|
|
123
143
|
}>;
|
|
124
144
|
checkedAt: z.ZodOptional<z.ZodString>;
|
|
145
|
+
codexErrorInfo: z.ZodOptional<z.ZodEnum<{
|
|
146
|
+
other: "other";
|
|
147
|
+
contextWindowExceeded: "contextWindowExceeded";
|
|
148
|
+
sessionBudgetExceeded: "sessionBudgetExceeded";
|
|
149
|
+
usageLimitExceeded: "usageLimitExceeded";
|
|
150
|
+
rateLimitExceeded: "rateLimitExceeded";
|
|
151
|
+
serverOverloaded: "serverOverloaded";
|
|
152
|
+
cyberPolicy: "cyberPolicy";
|
|
153
|
+
httpConnectionFailed: "httpConnectionFailed";
|
|
154
|
+
responseStreamConnectionFailed: "responseStreamConnectionFailed";
|
|
155
|
+
internalServerError: "internalServerError";
|
|
156
|
+
unauthorized: "unauthorized";
|
|
157
|
+
badRequest: "badRequest";
|
|
158
|
+
threadRollbackFailed: "threadRollbackFailed";
|
|
159
|
+
sandboxError: "sandboxError";
|
|
160
|
+
responseStreamDisconnected: "responseStreamDisconnected";
|
|
161
|
+
responseTooManyFailedAttempts: "responseTooManyFailedAttempts";
|
|
162
|
+
activeTurnNotSteerable: "activeTurnNotSteerable";
|
|
163
|
+
}>>;
|
|
125
164
|
}, z.core.$strip>>;
|
|
126
165
|
authentication: z.ZodOptional<z.ZodObject<{
|
|
127
166
|
credentialKind: z.ZodEnum<{
|
|
@@ -23,6 +23,7 @@ exports.providerFailureEvidenceSchema = zod_1.z.object({
|
|
|
23
23
|
source: zod_1.z.enum(providerFailure_1.PROVIDER_FAILURE_EVIDENCE_SOURCES),
|
|
24
24
|
rule: zod_1.z.enum(providerFailure_1.PROVIDER_FAILURE_CODES),
|
|
25
25
|
checkedAt: zod_1.z.string().optional(),
|
|
26
|
+
codexErrorInfo: zod_1.z.enum(providerFailure_1.CODEX_ERROR_INFO_KINDS).optional(),
|
|
26
27
|
});
|
|
27
28
|
exports.providerRetryAfterSchema = zod_1.z.discriminatedUnion('kind', [
|
|
28
29
|
zod_1.z.object({ kind: zod_1.z.literal('delay'), delayMs: zod_1.z.number() }),
|
|
@@ -33,6 +34,7 @@ exports.providerFailureDiagnosisSchema = zod_1.z.object({
|
|
|
33
34
|
credentialKind: zod_1.z.enum(providerFailure_1.PROVIDER_CREDENTIAL_KINDS),
|
|
34
35
|
diagnosis: zod_1.z.enum(providerFailure_1.PROVIDER_FAILURE_CODES),
|
|
35
36
|
recovery: zod_1.z.enum(providerFailure_1.PROVIDER_RECOVERY_ACTIONS),
|
|
37
|
+
retryable: zod_1.z.boolean().optional(),
|
|
36
38
|
evidence: zod_1.z.array(exports.providerFailureEvidenceSchema),
|
|
37
39
|
authentication: exports.providerAuthenticationEvidenceSchema.optional(),
|
|
38
40
|
httpStatus: zod_1.z.number().optional(),
|