sidekick-shared 0.29.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +171 -60
- package/dist/aggregation/EventAggregator.d.ts +79 -4
- package/dist/aggregation/EventAggregator.js +391 -37
- package/dist/aggregation/types.d.ts +27 -3
- package/dist/browser.d.ts +7 -3
- package/dist/browser.js +23 -3
- package/dist/context/sessionContext.js +5 -1
- package/dist/esm/aggregation/EventAggregator.js +392 -38
- package/dist/esm/browser.js +3 -1
- package/dist/esm/context/sessionContext.js +6 -2
- package/dist/esm/fileContinuity.js +149 -0
- package/dist/esm/index.js +5 -2
- package/dist/esm/node.js +2 -0
- package/dist/esm/parsers/codexParser.js +192 -3
- package/dist/esm/parsers/subagentEndState.js +61 -0
- package/dist/esm/parsers/subagentScanner.js +4 -0
- package/dist/esm/parsers/subagentTraceParser.js +6 -0
- package/dist/esm/promptHistory.js +22 -11
- package/dist/esm/providers/claudeCode.js +50 -27
- package/dist/esm/providers/codex.js +153 -163
- package/dist/esm/providers/codexCompressedReader.js +184 -0
- package/dist/esm/providers/codexDatabase.js +47 -2
- package/dist/esm/providers/codexRollouts.js +20 -2
- package/dist/esm/providers/codexSubagents.js +456 -0
- package/dist/esm/providers/compressedRollouts.js +330 -0
- package/dist/esm/providers/factory.js +4 -1
- package/dist/esm/providers/rolloutWalker.js +32 -4
- package/dist/esm/schemas/sessionEvent.js +32 -4
- package/dist/esm/sessionMonitor.js +7 -1
- package/dist/esm/sessionPreviews.js +9 -0
- package/dist/esm/sessionPromptHistory.js +5 -3
- package/dist/esm/subagentActivity.js +125 -0
- package/dist/esm/subagentDisplayState.js +81 -0
- package/dist/esm/subagentSpawn.js +206 -6
- package/dist/esm/testing/codexSubagentFixtures.js +336 -0
- package/dist/esm/toolCallOrigins.js +17 -10
- package/dist/esm/toolCategory.js +6 -2
- package/dist/esm/transcript.js +6 -2
- package/dist/esm/turns/assistantTurn.js +6 -3
- package/dist/esm/usage/usageEvents.js +4 -1
- package/dist/esm/watchers/eventBridge.js +31 -1
- package/dist/esm/watchers/jsonlTail.js +11 -42
- package/dist/esm/watchers/jsonlWatcher.js +19 -9
- package/dist/esm/watchers/providerReaderWatcher.js +5 -4
- package/dist/fileContinuity.d.ts +95 -0
- package/dist/fileContinuity.js +187 -0
- package/dist/index.d.ts +14 -7
- package/dist/index.js +39 -10
- package/dist/node.d.ts +4 -0
- package/dist/node.js +16 -2
- package/dist/parsers/codexParser.d.ts +44 -0
- package/dist/parsers/codexParser.js +192 -3
- package/dist/parsers/subagentEndState.d.ts +20 -0
- package/dist/parsers/subagentEndState.js +65 -0
- package/dist/parsers/subagentScanner.js +4 -0
- package/dist/parsers/subagentTraceParser.d.ts +8 -0
- package/dist/parsers/subagentTraceParser.js +6 -0
- package/dist/promptHistory.d.ts +13 -3
- package/dist/promptHistory.js +22 -11
- package/dist/providers/claudeCode.js +50 -27
- package/dist/providers/codex.d.ts +35 -5
- package/dist/providers/codex.js +150 -160
- package/dist/providers/codexCompressedReader.d.ts +58 -0
- package/dist/providers/codexCompressedReader.js +221 -0
- package/dist/providers/codexDatabase.d.ts +25 -0
- package/dist/providers/codexDatabase.js +48 -2
- package/dist/providers/codexRollouts.d.ts +16 -2
- package/dist/providers/codexRollouts.js +20 -2
- package/dist/providers/codexSubagents.d.ts +66 -0
- package/dist/providers/codexSubagents.js +495 -0
- package/dist/providers/compressedRollouts.d.ts +114 -0
- package/dist/providers/compressedRollouts.js +380 -0
- package/dist/providers/factory.d.ts +5 -0
- package/dist/providers/factory.js +4 -1
- package/dist/providers/rolloutWalker.d.ts +15 -2
- package/dist/providers/rolloutWalker.js +33 -4
- package/dist/providers/types.d.ts +22 -1
- package/dist/schemas/sessionEvent.d.ts +26 -2
- package/dist/schemas/sessionEvent.js +31 -3
- package/dist/sessionMonitor.d.ts +6 -0
- package/dist/sessionMonitor.js +7 -1
- package/dist/sessionPreviews.js +9 -0
- package/dist/sessionPromptHistory.d.ts +5 -0
- package/dist/sessionPromptHistory.js +5 -36
- package/dist/subagentActivity.d.ts +91 -0
- package/dist/subagentActivity.js +132 -0
- package/dist/subagentDisplayState.d.ts +81 -0
- package/dist/subagentDisplayState.js +86 -0
- package/dist/subagentSpawn.d.ts +100 -5
- package/dist/subagentSpawn.js +218 -7
- package/dist/testing/codexSubagentFixtures.d.ts +48 -0
- package/dist/testing/codexSubagentFixtures.js +344 -0
- package/dist/toolCallOrigins.d.ts +5 -0
- package/dist/toolCallOrigins.js +17 -10
- package/dist/toolCategory.js +6 -2
- package/dist/transcript.js +5 -1
- package/dist/turns/assistantTurn.d.ts +4 -1
- package/dist/turns/assistantTurn.js +5 -2
- package/dist/types/codex.d.ts +71 -2
- package/dist/types/sessionEvent.d.ts +38 -3
- package/dist/usage/usageEvents.d.ts +4 -1
- package/dist/usage/usageEvents.js +4 -1
- package/dist/watchers/eventBridge.js +30 -0
- package/dist/watchers/jsonlTail.js +11 -42
- package/dist/watchers/jsonlWatcher.d.ts +5 -1
- package/dist/watchers/jsonlWatcher.js +18 -8
- package/dist/watchers/providerReaderWatcher.js +5 -4
- package/package.json +1 -1
package/dist/node.js
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.removeSessionSummary = exports.markFileImported = exports.isFileImported = exports.applySessionSummary = exports.subagentTokenTotals = exports.scanSessionSubagents = exports.readMainThreadTokenTotals = exports.combineSessionTokenTotals = exports.collectSessionTokenTotals = exports.pruneUsageCache = exports.getUsageCacheDir = exports.collectUsageEvents = exports.USAGE_CACHE_VERSION = exports.MAX_USAGE_CACHE_FILES = exports.findActiveBillingBlock = exports.computeBillingBlocks = exports.BILLING_BLOCK_DURATION_MS = exports.writeStateFile = exports.readStateFile = exports.quotaToStateFile = exports.getStateFilePath = exports.billingBlockToStateFile = exports.STATE_FILE_SCHEMA_VERSION = exports.STATE_FILE_NAME = exports.sessionFingerprintParts = exports.fingerprintString = exports.readSessionReportInputs = exports.readSessionFileStats = exports.providerContextSizeFn = exports.firstUserPrompt = exports.computeSessionFileStats = exports.isHumanPrompt = exports.humanPromptText = exports.HUMAN_CODEX_SOURCES = exports.HUMAN_CLAUDE_ENTRYPOINTS = exports.collectSessionPromptHistory = exports.findToolCallOrigins = exports.PROMPT_HISTORY_ORDINAL_MAX_RECORD_BYTES = exports.parsePromptHistoryCursor = exports.isPromptHistorySessionSettled = exports.collectPromptHistory = exports.readSessionTranscript = exports.listRecentSessions = exports.getObservedContextWindowPath = exports.recordObservedContextWindow = exports.loadObservedContextWindows = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmContextWindows = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = void 0;
|
|
11
|
-
exports.
|
|
12
|
-
exports.verifyJsonlCheckpoint = exports.JsonlTailSymlinkError = exports.JSONL_TAIL_CHECKPOINT_VERSION = exports.DEFAULT_JSONL_TAIL_READ_BYTES = exports.DEFAULT_JSONL_TAIL_MAX_LINE_BYTES = exports.createJsonlTail = exports.captureJsonlCheckpoint = exports.resolveWorktreeMainRepo = exports.discoverWorktreeSiblings = exports.resolveWorkspaceRoots = exports.TOOL_CATEGORIES = exports.classifyTool = exports.fetchProviderServiceStatus = exports.clearRegisteredModelPricing = exports.registerModelPricing = exports.getPricingCatalogInfo = exports.codexSpawnParentFromSource = exports.codexRolloutOrigin = exports.parseCodexLoginStatus = exports.parseClaudeAuthStatus = exports.atomicWriteJsonSync = void 0;
|
|
11
|
+
exports.readCodexAuthIdentity = exports.parseCodexAuthJson = exports.CODEX_ARCHIVED_SESSIONS_SUBDIR = exports.CODEX_SESSIONS_SUBDIR = exports.forceFileCredentialStore = exports.getCodexCredentialStoreMode = exports.getCodexSessionsDirs = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.getCodexMonitoringHomes = exports.getSystemCodexHome = exports.getClaudeProjectsDir = exports.isDefaultClaudeHome = exports.getDefaultClaudeHome = exports.getLiveClaudeHome = exports.getClaudeSessionDirectory = exports.encodeClaudeWorkspacePath = exports.getDefaultConfigDir = exports.getConfigDirOverride = exports.getConfigDir = exports.CONFIG_DIR_ENV_VAR = exports.SYNTHETIC_MODEL = exports.createSessionProviders = exports.splitWatchedPath = exports.DirectoryListingCache = exports.fileFingerprintParts = exports.fileFingerprint = exports.observedSessionSourceFromProvider = exports.ObservedSessionCollector = exports.readSessionPreviewAsync = exports.readSessionPreview = exports.listSessionPreviewsAsync = exports.listSessionPreviews = exports.readCodexHistory = exports.resolveRolloutPath = exports.readRolloutText = exports.readRolloutBuffer = exports.isZstdDecompressionAvailable = exports.isCompressedRolloutPath = exports.COMPRESSED_ROLLOUT_SUFFIX = exports.walkRolloutFilesAsync = exports.walkRolloutFiles = exports.findCodexRolloutFile = exports.weekKey = exports.usageBucketKey = exports.summarizeUsageRows = exports.bucketUsage = exports.importSessionHistory = exports.ACTIVE_SESSION_MTIME_THRESHOLD_MS = exports.sessionSummaryFromStats = void 0;
|
|
12
|
+
exports.verifyJsonlCheckpoint = exports.JsonlTailSymlinkError = exports.JSONL_TAIL_CHECKPOINT_VERSION = exports.DEFAULT_JSONL_TAIL_READ_BYTES = exports.DEFAULT_JSONL_TAIL_MAX_LINE_BYTES = exports.createJsonlTail = exports.captureJsonlCheckpoint = exports.resolveWorktreeMainRepo = exports.discoverWorktreeSiblings = exports.resolveWorkspaceRoots = exports.TOOL_CATEGORIES = exports.classifyTool = exports.fetchProviderServiceStatus = exports.clearRegisteredModelPricing = exports.registerModelPricing = exports.getPricingCatalogInfo = exports.subagentActivityFromEvent = exports.parseCodexAgentMessageText = exports.isSubagentActivitySystemEvent = exports.describeSubagentActivity = exports.SUBAGENT_ACTIVITY_ORIGIN_KIND = exports.SUBAGENT_ACTIVITY_BLOCK_TYPE = exports.codexSpawnParentFromSource = exports.codexRolloutOrigin = exports.parseCodexLoginStatus = exports.parseClaudeAuthStatus = exports.atomicWriteJsonSync = exports.atomicWriteJson = exports.compareCodexAuthFreshness = exports.codexRefreshFingerprint = exports.codexIdentitiesMatch = exports.readCodexAccessTokenExpiry = exports.readCodexLastRefresh = void 0;
|
|
13
13
|
var pricingCatalog_1 = require("./pricingCatalog");
|
|
14
14
|
Object.defineProperty(exports, "hydratePricingCatalog", { enumerable: true, get: function () { return pricingCatalog_1.hydratePricingCatalog; } });
|
|
15
15
|
Object.defineProperty(exports, "normalizeLiteLlmCatalog", { enumerable: true, get: function () { return pricingCatalog_1.normalizeLiteLlmCatalog; } });
|
|
@@ -89,6 +89,13 @@ Object.defineProperty(exports, "findCodexRolloutFile", { enumerable: true, get:
|
|
|
89
89
|
var rolloutWalker_1 = require("./providers/rolloutWalker");
|
|
90
90
|
Object.defineProperty(exports, "walkRolloutFiles", { enumerable: true, get: function () { return rolloutWalker_1.walkRolloutFiles; } });
|
|
91
91
|
Object.defineProperty(exports, "walkRolloutFilesAsync", { enumerable: true, get: function () { return rolloutWalker_1.walkRolloutFilesAsync; } });
|
|
92
|
+
var compressedRollouts_1 = require("./providers/compressedRollouts");
|
|
93
|
+
Object.defineProperty(exports, "COMPRESSED_ROLLOUT_SUFFIX", { enumerable: true, get: function () { return compressedRollouts_1.COMPRESSED_ROLLOUT_SUFFIX; } });
|
|
94
|
+
Object.defineProperty(exports, "isCompressedRolloutPath", { enumerable: true, get: function () { return compressedRollouts_1.isCompressedRolloutPath; } });
|
|
95
|
+
Object.defineProperty(exports, "isZstdDecompressionAvailable", { enumerable: true, get: function () { return compressedRollouts_1.isZstdDecompressionAvailable; } });
|
|
96
|
+
Object.defineProperty(exports, "readRolloutBuffer", { enumerable: true, get: function () { return compressedRollouts_1.readRolloutBuffer; } });
|
|
97
|
+
Object.defineProperty(exports, "readRolloutText", { enumerable: true, get: function () { return compressedRollouts_1.readRolloutText; } });
|
|
98
|
+
Object.defineProperty(exports, "resolveRolloutPath", { enumerable: true, get: function () { return compressedRollouts_1.resolveRolloutPath; } });
|
|
92
99
|
var codexHistory_1 = require("./parsers/codexHistory");
|
|
93
100
|
Object.defineProperty(exports, "readCodexHistory", { enumerable: true, get: function () { return codexHistory_1.readCodexHistory; } });
|
|
94
101
|
var sessionPreviews_1 = require("./sessionPreviews");
|
|
@@ -151,6 +158,13 @@ Object.defineProperty(exports, "parseCodexLoginStatus", { enumerable: true, get:
|
|
|
151
158
|
var codexRolloutOrigin_1 = require("./parsers/codexRolloutOrigin");
|
|
152
159
|
Object.defineProperty(exports, "codexRolloutOrigin", { enumerable: true, get: function () { return codexRolloutOrigin_1.codexRolloutOrigin; } });
|
|
153
160
|
Object.defineProperty(exports, "codexSpawnParentFromSource", { enumerable: true, get: function () { return codexRolloutOrigin_1.codexSpawnParentFromSource; } });
|
|
161
|
+
var subagentActivity_1 = require("./subagentActivity");
|
|
162
|
+
Object.defineProperty(exports, "SUBAGENT_ACTIVITY_BLOCK_TYPE", { enumerable: true, get: function () { return subagentActivity_1.SUBAGENT_ACTIVITY_BLOCK_TYPE; } });
|
|
163
|
+
Object.defineProperty(exports, "SUBAGENT_ACTIVITY_ORIGIN_KIND", { enumerable: true, get: function () { return subagentActivity_1.SUBAGENT_ACTIVITY_ORIGIN_KIND; } });
|
|
164
|
+
Object.defineProperty(exports, "describeSubagentActivity", { enumerable: true, get: function () { return subagentActivity_1.describeSubagentActivity; } });
|
|
165
|
+
Object.defineProperty(exports, "isSubagentActivitySystemEvent", { enumerable: true, get: function () { return subagentActivity_1.isSubagentActivitySystemEvent; } });
|
|
166
|
+
Object.defineProperty(exports, "parseCodexAgentMessageText", { enumerable: true, get: function () { return subagentActivity_1.parseCodexAgentMessageText; } });
|
|
167
|
+
Object.defineProperty(exports, "subagentActivityFromEvent", { enumerable: true, get: function () { return subagentActivity_1.subagentActivityFromEvent; } });
|
|
154
168
|
var modelInfo_1 = require("./modelInfo");
|
|
155
169
|
Object.defineProperty(exports, "getPricingCatalogInfo", { enumerable: true, get: function () { return modelInfo_1.getPricingCatalogInfo; } });
|
|
156
170
|
Object.defineProperty(exports, "registerModelPricing", { enumerable: true, get: function () { return modelInfo_1.registerModelPricing; } });
|
|
@@ -88,6 +88,15 @@ export interface CodexRolloutParserOptions {
|
|
|
88
88
|
* unchanged. For cheap passes over large rollouts; default false.
|
|
89
89
|
*/
|
|
90
90
|
metadataOnly?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Emit the multi-agent lifecycle as `system` events with `origin: { kind:
|
|
93
|
+
* 'subagent-activity' }` (read them with `subagentActivityFromEvent()`):
|
|
94
|
+
* `SubAgentActivity` items (`started`, `completed`, `interacted`,
|
|
95
|
+
* `interrupted`) and each `FINAL_ANSWER` agent message. Transcripts,
|
|
96
|
+
* context attribution, and the aggregator's message counts skip them.
|
|
97
|
+
* Default false: those records produce no events.
|
|
98
|
+
*/
|
|
99
|
+
includeSubagentActivity?: boolean;
|
|
91
100
|
}
|
|
92
101
|
/** Pending tool-call state counters from {@link CodexRolloutParser.getPendingStats}. */
|
|
93
102
|
export interface CodexPendingStats {
|
|
@@ -126,6 +135,18 @@ export interface CodexRolloutResumeState {
|
|
|
126
135
|
pendingExecCommands: PendingExecCommand[];
|
|
127
136
|
/** `mcp_tool_call_begin` events awaiting their end. */
|
|
128
137
|
pendingMcpToolCalls: PendingMcpToolCall[];
|
|
138
|
+
/**
|
|
139
|
+
* Multi-agent tool calls by call id with their raw tool names, so a later
|
|
140
|
+
* `SubAgentActivity` record names its tool. Optional: absent in states
|
|
141
|
+
* from earlier versions (and when `includeSubagentActivity` is off).
|
|
142
|
+
*/
|
|
143
|
+
collaborationCalls?: Array<[string, string]>;
|
|
144
|
+
/**
|
|
145
|
+
* Forked subagent rollouts: the ordinal below which records are the
|
|
146
|
+
* embedded parent history (see `getInheritedHistoryEnd()`). Optional:
|
|
147
|
+
* absent (nothing skipped) in states from earlier versions.
|
|
148
|
+
*/
|
|
149
|
+
inheritedHistoryEnd?: number | null;
|
|
129
150
|
}
|
|
130
151
|
/** A valid `CodexTokenUsage` (copied), `null` for `null`, or `undefined` when invalid. Internal. */
|
|
131
152
|
export declare function codexTokenUsageFrom(value: unknown): CodexTokenUsage | null | undefined;
|
|
@@ -156,6 +177,15 @@ export declare class CodexRolloutParser {
|
|
|
156
177
|
/** Byte budget for pending calls; `null` = unlimited (no accounting). */
|
|
157
178
|
private readonly maxPendingBytes;
|
|
158
179
|
private readonly metadataOnly;
|
|
180
|
+
private readonly includeSubagentActivity;
|
|
181
|
+
/**
|
|
182
|
+
* Set once a forked child's rollout embeds an ancestor's session_meta: its
|
|
183
|
+
* own `subagent_history_start_ordinal`, below which records are the
|
|
184
|
+
* parent's history and are skipped. `null` while nothing is skipped.
|
|
185
|
+
*/
|
|
186
|
+
private historyStartOrdinal;
|
|
187
|
+
/** Multi-agent tool calls (call id → raw name), only with `includeSubagentActivity`. */
|
|
188
|
+
private collaborationCalls;
|
|
159
189
|
/** Pending calls in insertion order, for the byte budget. */
|
|
160
190
|
private pendingLedger;
|
|
161
191
|
private pendingBytes;
|
|
@@ -174,6 +204,14 @@ export declare class CodexRolloutParser {
|
|
|
174
204
|
getModelContextWindow(): number | null;
|
|
175
205
|
/** Get the last observed rate limits from token_count events. */
|
|
176
206
|
getLastRateLimits(): CodexRateLimits | null;
|
|
207
|
+
/**
|
|
208
|
+
* In a forked subagent's rollout, the ordinal of its first own record:
|
|
209
|
+
* records with a lower `ordinal` are the parent history the fork embeds
|
|
210
|
+
* and produce no events. Known once the rollout's second line (an embedded
|
|
211
|
+
* ancestor `session_meta`) was parsed; `null` otherwise, and for every
|
|
212
|
+
* rollout that is not such a fork.
|
|
213
|
+
*/
|
|
214
|
+
getInheritedHistoryEnd(): number | null;
|
|
177
215
|
/**
|
|
178
216
|
* The state later lines depend on — session metadata, current model,
|
|
179
217
|
* previous cumulative token total, context window, rate limits, plan mode,
|
|
@@ -214,6 +252,12 @@ export declare class CodexRolloutParser {
|
|
|
214
252
|
private toolOutput;
|
|
215
253
|
private handleSessionMeta;
|
|
216
254
|
private handleResponseItem;
|
|
255
|
+
private rememberCollaborationCall;
|
|
256
|
+
private subagentActivityEvent;
|
|
257
|
+
/** A child's `FINAL_ANSWER` message to its parent (opt-in). */
|
|
258
|
+
private handleAgentMessage;
|
|
259
|
+
/** A `SubAgentActivity` turn item (opt-in). */
|
|
260
|
+
private handleItemCompleted;
|
|
217
261
|
private handleMessage;
|
|
218
262
|
private handleReasoning;
|
|
219
263
|
private handleFunctionCall;
|
|
@@ -22,6 +22,7 @@ exports.codexTokenUsageFrom = codexTokenUsageFrom;
|
|
|
22
22
|
const codex_1 = require("../types/codex");
|
|
23
23
|
const openCodeParser_1 = require("./openCodeParser");
|
|
24
24
|
const usageNormalization_1 = require("../usageNormalization");
|
|
25
|
+
const subagentActivity_1 = require("../subagentActivity");
|
|
25
26
|
function reportedCompaction(tokensBefore, tokensAfter) {
|
|
26
27
|
if (typeof tokensBefore !== 'number' ||
|
|
27
28
|
typeof tokensAfter !== 'number' ||
|
|
@@ -181,6 +182,29 @@ function extractPatchFilePaths(input) {
|
|
|
181
182
|
exports.DEFAULT_MAX_TRACKED_TOOL_CALLS = 4096;
|
|
182
183
|
/** Entries per table {@link CodexRolloutParser.exportResumeState} keeps by default. */
|
|
183
184
|
const DEFAULT_RESUME_STATE_LIMIT = 256;
|
|
185
|
+
/** Raw names of Codex's multi-agent tools (namespace `collaboration`). */
|
|
186
|
+
const COLLABORATION_TOOL_NAMES = new Set([
|
|
187
|
+
'spawn_agent',
|
|
188
|
+
'followup_task',
|
|
189
|
+
'send_message',
|
|
190
|
+
'interrupt_agent',
|
|
191
|
+
'wait_agent',
|
|
192
|
+
'list_agents',
|
|
193
|
+
]);
|
|
194
|
+
const SUBAGENT_ACTIVITY_KINDS = new Set([
|
|
195
|
+
'started',
|
|
196
|
+
'completed',
|
|
197
|
+
'interacted',
|
|
198
|
+
'interrupted',
|
|
199
|
+
]);
|
|
200
|
+
function nonEmptyString(value) {
|
|
201
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
202
|
+
}
|
|
203
|
+
/** `subagent_history_start_ordinal` of a session_meta payload, when valid. */
|
|
204
|
+
function historyStartOrdinalOf(meta) {
|
|
205
|
+
const value = meta?.subagent_history_start_ordinal;
|
|
206
|
+
return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : null;
|
|
207
|
+
}
|
|
184
208
|
function isPlainObject(value) {
|
|
185
209
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
186
210
|
}
|
|
@@ -298,6 +322,15 @@ class CodexRolloutParser {
|
|
|
298
322
|
/** Byte budget for pending calls; `null` = unlimited (no accounting). */
|
|
299
323
|
maxPendingBytes;
|
|
300
324
|
metadataOnly;
|
|
325
|
+
includeSubagentActivity;
|
|
326
|
+
/**
|
|
327
|
+
* Set once a forked child's rollout embeds an ancestor's session_meta: its
|
|
328
|
+
* own `subagent_history_start_ordinal`, below which records are the
|
|
329
|
+
* parent's history and are skipped. `null` while nothing is skipped.
|
|
330
|
+
*/
|
|
331
|
+
historyStartOrdinal = null;
|
|
332
|
+
/** Multi-agent tool calls (call id → raw name), only with `includeSubagentActivity`. */
|
|
333
|
+
collaborationCalls = new Map();
|
|
301
334
|
/** Pending calls in insertion order, for the byte budget. */
|
|
302
335
|
pendingLedger = new Map();
|
|
303
336
|
pendingBytes = 0;
|
|
@@ -313,6 +346,7 @@ class CodexRolloutParser {
|
|
|
313
346
|
? Math.floor(maxBytes)
|
|
314
347
|
: null;
|
|
315
348
|
this.metadataOnly = options.metadataOnly === true;
|
|
349
|
+
this.includeSubagentActivity = options.includeSubagentActivity === true;
|
|
316
350
|
}
|
|
317
351
|
/** Counters for the pending tool-call state and what the caps evicted. */
|
|
318
352
|
getPendingStats() {
|
|
@@ -345,6 +379,16 @@ class CodexRolloutParser {
|
|
|
345
379
|
getLastRateLimits() {
|
|
346
380
|
return this.lastRateLimits;
|
|
347
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* In a forked subagent's rollout, the ordinal of its first own record:
|
|
384
|
+
* records with a lower `ordinal` are the parent history the fork embeds
|
|
385
|
+
* and produce no events. Known once the rollout's second line (an embedded
|
|
386
|
+
* ancestor `session_meta`) was parsed; `null` otherwise, and for every
|
|
387
|
+
* rollout that is not such a fork.
|
|
388
|
+
*/
|
|
389
|
+
getInheritedHistoryEnd() {
|
|
390
|
+
return this.historyStartOrdinal;
|
|
391
|
+
}
|
|
348
392
|
/**
|
|
349
393
|
* The state later lines depend on — session metadata, current model,
|
|
350
394
|
* previous cumulative token total, context window, rate limits, plan mode,
|
|
@@ -366,6 +410,14 @@ class CodexRolloutParser {
|
|
|
366
410
|
patchExpandedToolUseIds: newest(this.patchExpandedToolUseIds, this.patchExpandedToolUseIds.size, limit),
|
|
367
411
|
pendingExecCommands: newest(this.pendingExecCommands.values(), this.pendingExecCommands.size, limit),
|
|
368
412
|
pendingMcpToolCalls: newest(this.pendingMcpToolCalls.values(), this.pendingMcpToolCalls.size, limit),
|
|
413
|
+
...(this.historyStartOrdinal !== null
|
|
414
|
+
? { inheritedHistoryEnd: this.historyStartOrdinal }
|
|
415
|
+
: {}),
|
|
416
|
+
...(this.collaborationCalls.size > 0
|
|
417
|
+
? {
|
|
418
|
+
collaborationCalls: newest(this.collaborationCalls, this.collaborationCalls.size, limit),
|
|
419
|
+
}
|
|
420
|
+
: {}),
|
|
369
421
|
});
|
|
370
422
|
}
|
|
371
423
|
/**
|
|
@@ -426,6 +478,21 @@ class CodexRolloutParser {
|
|
|
426
478
|
typeof entry.timestamp === 'string')) {
|
|
427
479
|
return false;
|
|
428
480
|
}
|
|
481
|
+
const inheritedEnd = s.inheritedHistoryEnd;
|
|
482
|
+
if (inheritedEnd !== undefined &&
|
|
483
|
+
inheritedEnd !== null &&
|
|
484
|
+
!(typeof inheritedEnd === 'number' && Number.isInteger(inheritedEnd) && inheritedEnd > 0)) {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
const collaborationCalls = s.collaborationCalls;
|
|
488
|
+
if (collaborationCalls !== undefined &&
|
|
489
|
+
!(Array.isArray(collaborationCalls) &&
|
|
490
|
+
collaborationCalls.every((entry) => Array.isArray(entry) &&
|
|
491
|
+
entry.length === 2 &&
|
|
492
|
+
typeof entry[0] === 'string' &&
|
|
493
|
+
typeof entry[1] === 'string'))) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
429
496
|
const copy = cloneJson({
|
|
430
497
|
sessionMeta: s.sessionMeta,
|
|
431
498
|
lastRateLimits: s.lastRateLimits,
|
|
@@ -435,6 +502,7 @@ class CodexRolloutParser {
|
|
|
435
502
|
});
|
|
436
503
|
this.reset();
|
|
437
504
|
this.sessionMeta = copy.sessionMeta;
|
|
505
|
+
this.historyStartOrdinal = typeof inheritedEnd === 'number' ? inheritedEnd : null;
|
|
438
506
|
this.currentModel = s.currentModel;
|
|
439
507
|
this.prevTotalUsage = prevTotalUsage;
|
|
440
508
|
this.lastTokenUsage = lastTokenUsage;
|
|
@@ -455,15 +523,28 @@ class CodexRolloutParser {
|
|
|
455
523
|
for (const pending of copy.mcps) {
|
|
456
524
|
this.putPendingMcp(this.metadataOnly ? { ...pending, arguments: undefined } : pending);
|
|
457
525
|
}
|
|
526
|
+
if (this.includeSubagentActivity && Array.isArray(collaborationCalls)) {
|
|
527
|
+
for (const [callId, name] of collaborationCalls) {
|
|
528
|
+
this.rememberCollaborationCall(callId, name);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
458
531
|
return true;
|
|
459
532
|
}
|
|
460
533
|
/**
|
|
461
534
|
* Converts a single rollout line to zero or more session events.
|
|
462
535
|
*/
|
|
463
536
|
convertLine(line) {
|
|
537
|
+
if (line.type === 'session_meta') {
|
|
538
|
+
return this.handleSessionMeta(line.timestamp, line.payload, line.ordinal);
|
|
539
|
+
}
|
|
540
|
+
// A forked subagent rollout embeds the parent's history before its own
|
|
541
|
+
// records; that history is the parent's, not the child's.
|
|
542
|
+
if (this.historyStartOrdinal !== null &&
|
|
543
|
+
typeof line.ordinal === 'number' &&
|
|
544
|
+
line.ordinal < this.historyStartOrdinal) {
|
|
545
|
+
return [];
|
|
546
|
+
}
|
|
464
547
|
switch (line.type) {
|
|
465
|
-
case 'session_meta':
|
|
466
|
-
return this.handleSessionMeta(line.timestamp, line.payload);
|
|
467
548
|
case 'response_item':
|
|
468
549
|
return this.handleResponseItem(line.timestamp, line.payload);
|
|
469
550
|
case 'compacted':
|
|
@@ -479,6 +560,8 @@ class CodexRolloutParser {
|
|
|
479
560
|
/** Reset all parser state. */
|
|
480
561
|
reset() {
|
|
481
562
|
this.sessionMeta = null;
|
|
563
|
+
this.historyStartOrdinal = null;
|
|
564
|
+
this.collaborationCalls.clear();
|
|
482
565
|
this.currentModel = null;
|
|
483
566
|
this.pendingExecCommands.clear();
|
|
484
567
|
this.pendingMcpToolCalls.clear();
|
|
@@ -568,7 +651,25 @@ class CodexRolloutParser {
|
|
|
568
651
|
return this.metadataOnly ? '' : content;
|
|
569
652
|
}
|
|
570
653
|
// --- Handlers ---
|
|
571
|
-
handleSessionMeta(timestamp, payload) {
|
|
654
|
+
handleSessionMeta(timestamp, payload, ordinal) {
|
|
655
|
+
if (this.sessionMeta) {
|
|
656
|
+
// Only the rollout's own (first) session_meta counts. A forked child
|
|
657
|
+
// embeds its parent's right after its own, followed by the parent's
|
|
658
|
+
// history up to the child's `subagent_history_start_ordinal`; from
|
|
659
|
+
// here on, records below that ordinal are skipped. (Children whose
|
|
660
|
+
// rollout has no embedded meta keep every record: in some, Codex
|
|
661
|
+
// recorded a start ordinal past their own records.)
|
|
662
|
+
const start = historyStartOrdinalOf(this.sessionMeta);
|
|
663
|
+
if (this.historyStartOrdinal === null &&
|
|
664
|
+
start !== null &&
|
|
665
|
+
typeof ordinal === 'number' &&
|
|
666
|
+
ordinal < start) {
|
|
667
|
+
this.historyStartOrdinal = start;
|
|
668
|
+
}
|
|
669
|
+
return [];
|
|
670
|
+
}
|
|
671
|
+
if (!payload || typeof payload !== 'object')
|
|
672
|
+
return [];
|
|
572
673
|
this.sessionMeta = payload;
|
|
573
674
|
const text = payload.base_instructions?.text?.trim();
|
|
574
675
|
return [
|
|
@@ -608,10 +709,91 @@ class CodexRolloutParser {
|
|
|
608
709
|
return this.handleCustomToolCall(timestamp, payload);
|
|
609
710
|
case 'custom_tool_call_output':
|
|
610
711
|
return this.handleCustomToolCallOutput(timestamp, payload);
|
|
712
|
+
case 'agent_message':
|
|
713
|
+
return this.handleAgentMessage(timestamp, payload);
|
|
611
714
|
default:
|
|
612
715
|
return [];
|
|
613
716
|
}
|
|
614
717
|
}
|
|
718
|
+
// --- Subagent activity ---
|
|
719
|
+
rememberCollaborationCall(callId, name) {
|
|
720
|
+
this.collaborationCalls.delete(callId);
|
|
721
|
+
this.collaborationCalls.set(callId, name);
|
|
722
|
+
while (this.collaborationCalls.size > this.maxTrackedToolCalls) {
|
|
723
|
+
this.collaborationCalls.delete(this.collaborationCalls.keys().next().value);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
subagentActivityEvent(timestamp, id, activity) {
|
|
727
|
+
return {
|
|
728
|
+
type: 'system',
|
|
729
|
+
timestamp,
|
|
730
|
+
origin: { kind: subagentActivity_1.SUBAGENT_ACTIVITY_ORIGIN_KIND },
|
|
731
|
+
message: {
|
|
732
|
+
role: 'system',
|
|
733
|
+
id: `subagent-activity:${id}`,
|
|
734
|
+
sourceLabel: 'subagent activity',
|
|
735
|
+
content: [{ type: subagentActivity_1.SUBAGENT_ACTIVITY_BLOCK_TYPE, ...activity }],
|
|
736
|
+
},
|
|
737
|
+
providerMetadata: { providerId: 'codex', source: 'codex-rollout' },
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
/** A child's `FINAL_ANSWER` message to its parent (opt-in). */
|
|
741
|
+
handleAgentMessage(timestamp, item) {
|
|
742
|
+
if (!this.includeSubagentActivity || !Array.isArray(item.content))
|
|
743
|
+
return [];
|
|
744
|
+
const first = item.content.find((part) => !!part && typeof part === 'object' && typeof part.text === 'string');
|
|
745
|
+
const parsed = (0, subagentActivity_1.parseCodexAgentMessageText)(first?.text);
|
|
746
|
+
if (!parsed || parsed.messageType !== 'FINAL_ANSWER')
|
|
747
|
+
return [];
|
|
748
|
+
const agentPath = nonEmptyString(item.author) ?? parsed.sender;
|
|
749
|
+
const recipient = nonEmptyString(item.recipient) ?? parsed.taskName;
|
|
750
|
+
const text = this.metadataOnly ? undefined : nonEmptyString(parsed.payload.trim());
|
|
751
|
+
const activity = {
|
|
752
|
+
kind: 'final-answer',
|
|
753
|
+
...(agentPath ? { agentPath } : {}),
|
|
754
|
+
...(recipient ? { recipient } : {}),
|
|
755
|
+
...(text ? { text } : {}),
|
|
756
|
+
};
|
|
757
|
+
return [
|
|
758
|
+
this.subagentActivityEvent(timestamp, nonEmptyString(item.id) ?? `final-answer-${timestamp}-${agentPath ?? ''}`, activity),
|
|
759
|
+
];
|
|
760
|
+
}
|
|
761
|
+
/** A `SubAgentActivity` turn item (opt-in). */
|
|
762
|
+
handleItemCompleted(timestamp, event) {
|
|
763
|
+
if (!this.includeSubagentActivity)
|
|
764
|
+
return [];
|
|
765
|
+
const item = event.item;
|
|
766
|
+
if (!item || item.type !== 'SubAgentActivity')
|
|
767
|
+
return [];
|
|
768
|
+
const kind = typeof item.kind === 'string' ? item.kind : '';
|
|
769
|
+
if (!SUBAGENT_ACTIVITY_KINDS.has(kind))
|
|
770
|
+
return [];
|
|
771
|
+
const itemId = nonEmptyString(item.id);
|
|
772
|
+
const completedTurn = kind === 'completed' && itemId?.startsWith('subagent-completed-')
|
|
773
|
+
? nonEmptyString(itemId.slice('subagent-completed-'.length))
|
|
774
|
+
: undefined;
|
|
775
|
+
const callId = kind === 'completed' ? undefined : itemId;
|
|
776
|
+
const tool = callId ? this.collaborationCalls.get(callId) : undefined;
|
|
777
|
+
const fields = {
|
|
778
|
+
callId,
|
|
779
|
+
tool,
|
|
780
|
+
turnId: completedTurn,
|
|
781
|
+
agentThreadId: nonEmptyString(item.agent_thread_id),
|
|
782
|
+
agentPath: nonEmptyString(item.agent_path),
|
|
783
|
+
model: nonEmptyString(item.model),
|
|
784
|
+
reasoningEffort: nonEmptyString(item.reasoning_effort),
|
|
785
|
+
parentThreadId: nonEmptyString(event.thread_id),
|
|
786
|
+
parentTurnId: nonEmptyString(event.turn_id),
|
|
787
|
+
};
|
|
788
|
+
const activity = { kind: kind };
|
|
789
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
790
|
+
if (value !== undefined)
|
|
791
|
+
activity[key] = value;
|
|
792
|
+
}
|
|
793
|
+
return [
|
|
794
|
+
this.subagentActivityEvent(timestamp, itemId ?? `${kind}-${timestamp}-${activity.agentThreadId ?? ''}`, activity),
|
|
795
|
+
];
|
|
796
|
+
}
|
|
615
797
|
handleMessage(timestamp, item) {
|
|
616
798
|
const text = extractTextFromContent(item.content);
|
|
617
799
|
if (!text)
|
|
@@ -693,6 +875,11 @@ class CodexRolloutParser {
|
|
|
693
875
|
const canonicalName = normalizeCodexToolName(item.name);
|
|
694
876
|
const normalizedInput = normalizeCodexToolInput(canonicalName, item.name, parsedArgs);
|
|
695
877
|
rememberBounded(this.emittedToolUseIds, item.call_id, this.maxTrackedToolCalls);
|
|
878
|
+
if (this.includeSubagentActivity &&
|
|
879
|
+
typeof item.call_id === 'string' &&
|
|
880
|
+
(item.namespace === 'collaboration' || COLLABORATION_TOOL_NAMES.has(item.name))) {
|
|
881
|
+
this.rememberCollaborationCall(item.call_id, item.name);
|
|
882
|
+
}
|
|
696
883
|
return [
|
|
697
884
|
{
|
|
698
885
|
type: 'assistant',
|
|
@@ -1149,6 +1336,8 @@ class CodexRolloutParser {
|
|
|
1149
1336
|
timestamp,
|
|
1150
1337
|
},
|
|
1151
1338
|
];
|
|
1339
|
+
case 'item_completed':
|
|
1340
|
+
return this.handleItemCompleted(timestamp, event);
|
|
1152
1341
|
case 'turn_aborted':
|
|
1153
1342
|
case 'agent_reasoning':
|
|
1154
1343
|
case 'agent_message':
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a Claude Code subagent transcript ended cleanly, read line by line.
|
|
3
|
+
*
|
|
4
|
+
* Clean means: the last user or assistant line is an assistant line that
|
|
5
|
+
* stopped with `end_turn` or `stop_sequence`, and every `tool_use` in the
|
|
6
|
+
* transcript has its `tool_result`. A user line after that (a resumed agent's
|
|
7
|
+
* new prompt) makes it unfinished again.
|
|
8
|
+
*
|
|
9
|
+
* Pure. Internal to the subagent parsers.
|
|
10
|
+
*
|
|
11
|
+
* @module parsers/subagentEndState
|
|
12
|
+
*/
|
|
13
|
+
export declare class SubagentEndTracker {
|
|
14
|
+
private readonly unanswered;
|
|
15
|
+
private lastWasCleanAssistant;
|
|
16
|
+
/** Feeds one parsed JSONL line, in file order. */
|
|
17
|
+
observe(raw: unknown): void;
|
|
18
|
+
/** Whether the lines so far end cleanly. */
|
|
19
|
+
endedCleanly(): boolean;
|
|
20
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Whether a Claude Code subagent transcript ended cleanly, read line by line.
|
|
4
|
+
*
|
|
5
|
+
* Clean means: the last user or assistant line is an assistant line that
|
|
6
|
+
* stopped with `end_turn` or `stop_sequence`, and every `tool_use` in the
|
|
7
|
+
* transcript has its `tool_result`. A user line after that (a resumed agent's
|
|
8
|
+
* new prompt) makes it unfinished again.
|
|
9
|
+
*
|
|
10
|
+
* Pure. Internal to the subagent parsers.
|
|
11
|
+
*
|
|
12
|
+
* @module parsers/subagentEndState
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.SubagentEndTracker = void 0;
|
|
16
|
+
const ENDED_STOP_REASONS = new Set(['end_turn', 'stop_sequence']);
|
|
17
|
+
/** Bounds the unanswered-call set for pathological transcripts. */
|
|
18
|
+
const MAX_PENDING_TOOL_USES = 4096;
|
|
19
|
+
class SubagentEndTracker {
|
|
20
|
+
unanswered = new Set();
|
|
21
|
+
lastWasCleanAssistant = false;
|
|
22
|
+
/** Feeds one parsed JSONL line, in file order. */
|
|
23
|
+
observe(raw) {
|
|
24
|
+
if (!raw || typeof raw !== 'object')
|
|
25
|
+
return;
|
|
26
|
+
const line = raw;
|
|
27
|
+
if (line.type !== 'assistant' && line.type !== 'user')
|
|
28
|
+
return;
|
|
29
|
+
const message = line.message && typeof line.message === 'object'
|
|
30
|
+
? line.message
|
|
31
|
+
: undefined;
|
|
32
|
+
const content = Array.isArray(message?.content) ? message.content : [];
|
|
33
|
+
if (line.type === 'user') {
|
|
34
|
+
for (const block of content) {
|
|
35
|
+
if (!block || typeof block !== 'object')
|
|
36
|
+
continue;
|
|
37
|
+
const typed = block;
|
|
38
|
+
if (typed.type === 'tool_result' && typeof typed.tool_use_id === 'string') {
|
|
39
|
+
this.unanswered.delete(typed.tool_use_id);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
this.lastWasCleanAssistant = false;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
let callsTool = false;
|
|
46
|
+
for (const block of content) {
|
|
47
|
+
if (!block || typeof block !== 'object')
|
|
48
|
+
continue;
|
|
49
|
+
const typed = block;
|
|
50
|
+
if (typed.type !== 'tool_use')
|
|
51
|
+
continue;
|
|
52
|
+
callsTool = true;
|
|
53
|
+
if (typeof typed.id === 'string' && this.unanswered.size < MAX_PENDING_TOOL_USES) {
|
|
54
|
+
this.unanswered.add(typed.id);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const stop = typeof message?.stop_reason === 'string' ? message.stop_reason : '';
|
|
58
|
+
this.lastWasCleanAssistant = !callsTool && ENDED_STOP_REASONS.has(stop);
|
|
59
|
+
}
|
|
60
|
+
/** Whether the lines so far end cleanly. */
|
|
61
|
+
endedCleanly() {
|
|
62
|
+
return this.lastWasCleanAssistant && this.unanswered.size === 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.SubagentEndTracker = SubagentEndTracker;
|
|
@@ -52,6 +52,7 @@ const claudeUsageDedupe_1 = require("../usage/claudeUsageDedupe");
|
|
|
52
52
|
const subagentUsage_1 = require("../usage/subagentUsage");
|
|
53
53
|
const subagentStatsCache_1 = require("./subagentStatsCache");
|
|
54
54
|
const subagentSpawn_1 = require("../subagentSpawn");
|
|
55
|
+
const subagentEndState_1 = require("./subagentEndState");
|
|
55
56
|
/**
|
|
56
57
|
* Pattern for matching subagent JSONL files.
|
|
57
58
|
* Files are named like: agent-<hash>.jsonl
|
|
@@ -199,6 +200,7 @@ function parseAgentFile(filePath, agentId, log) {
|
|
|
199
200
|
let description;
|
|
200
201
|
const tokens = { agentId, toolCalls: [], inputTokens: 0, outputTokens: 0 };
|
|
201
202
|
const usageDeduper = new claudeUsageDedupe_1.ClaudeUsageDeduper();
|
|
203
|
+
const endTracker = new subagentEndState_1.SubagentEndTracker();
|
|
202
204
|
let startTime;
|
|
203
205
|
let endTime;
|
|
204
206
|
// Task tracking state
|
|
@@ -215,6 +217,7 @@ function parseAgentFile(filePath, agentId, log) {
|
|
|
215
217
|
if (!startTime)
|
|
216
218
|
startTime = eventTimestamp;
|
|
217
219
|
endTime = eventTimestamp;
|
|
220
|
+
endTracker.observe(event);
|
|
218
221
|
// Extract token usage from assistant messages. Split lines of one
|
|
219
222
|
// response repeat its usage, so count each response once.
|
|
220
223
|
const usage = (0, claudeUsageDedupe_1.dedupedRawClaudeUsage)(event, usageDeduper);
|
|
@@ -337,6 +340,7 @@ function parseAgentFile(filePath, agentId, log) {
|
|
|
337
340
|
startTime,
|
|
338
341
|
endTime,
|
|
339
342
|
durationMs,
|
|
343
|
+
endedCleanly: endTracker.endedCleanly(),
|
|
340
344
|
};
|
|
341
345
|
}
|
|
342
346
|
return null;
|
|
@@ -63,6 +63,14 @@ export interface SubagentTrace {
|
|
|
63
63
|
finished?: boolean;
|
|
64
64
|
/** The user stopped the agent (meta `stoppedByUser`). */
|
|
65
65
|
stoppedByUser?: boolean;
|
|
66
|
+
/** Time of the transcript's last record (the same as `stats.endTime`). */
|
|
67
|
+
lastActivityAt?: Date;
|
|
68
|
+
/**
|
|
69
|
+
* The transcript ended cleanly: its last assistant turn stopped with
|
|
70
|
+
* `end_turn` / `stop_sequence` and no tool call is unanswered. A resumed
|
|
71
|
+
* agent's next prompt clears it.
|
|
72
|
+
*/
|
|
73
|
+
endedCleanly?: boolean;
|
|
66
74
|
}
|
|
67
75
|
/**
|
|
68
76
|
* Scans and parses subagent traces with full conversation events.
|
|
@@ -58,6 +58,7 @@ const noiseClassifier_1 = require("../formatters/noiseClassifier");
|
|
|
58
58
|
const claudeUsageDedupe_1 = require("../usage/claudeUsageDedupe");
|
|
59
59
|
const subagentUsage_1 = require("../usage/subagentUsage");
|
|
60
60
|
const subagentScanner_1 = require("./subagentScanner");
|
|
61
|
+
const subagentEndState_1 = require("./subagentEndState");
|
|
61
62
|
const subagentSpawn_1 = require("../subagentSpawn");
|
|
62
63
|
const ownKey_1 = require("../ownKey");
|
|
63
64
|
// ── Constants ──
|
|
@@ -109,6 +110,7 @@ function parseAgentTrace(filePath, agentId) {
|
|
|
109
110
|
let description;
|
|
110
111
|
const tokens = { agentId, toolCalls: [], inputTokens: 0, outputTokens: 0 };
|
|
111
112
|
const usageDeduper = new claudeUsageDedupe_1.ClaudeUsageDeduper();
|
|
113
|
+
const endTracker = new subagentEndState_1.SubagentEndTracker();
|
|
112
114
|
let startTime;
|
|
113
115
|
let endTime;
|
|
114
116
|
for (const line of lines) {
|
|
@@ -118,6 +120,7 @@ function parseAgentTrace(filePath, agentId) {
|
|
|
118
120
|
if (!startTime)
|
|
119
121
|
startTime = timestamp;
|
|
120
122
|
endTime = timestamp;
|
|
123
|
+
endTracker.observe(raw);
|
|
121
124
|
// Build SessionEvent from raw JSONL
|
|
122
125
|
const sessionEvent = rawToSessionEvent(raw);
|
|
123
126
|
if (!sessionEvent)
|
|
@@ -218,6 +221,8 @@ function parseAgentTrace(filePath, agentId) {
|
|
|
218
221
|
...(meta.requestShape ? { background: meta.requestShape === 'background' } : {}),
|
|
219
222
|
...(meta.finished !== undefined ? { finished: meta.finished } : {}),
|
|
220
223
|
...(meta.stoppedByUser !== undefined ? { stoppedByUser: meta.stoppedByUser } : {}),
|
|
224
|
+
...(endTime ? { lastActivityAt: endTime } : {}),
|
|
225
|
+
endedCleanly: endTracker.endedCleanly(),
|
|
221
226
|
events,
|
|
222
227
|
children: [],
|
|
223
228
|
stats: {
|
|
@@ -234,6 +239,7 @@ function parseAgentTrace(filePath, agentId) {
|
|
|
234
239
|
startTime,
|
|
235
240
|
endTime,
|
|
236
241
|
durationMs,
|
|
242
|
+
endedCleanly: endTracker.endedCleanly(),
|
|
237
243
|
},
|
|
238
244
|
};
|
|
239
245
|
}
|
package/dist/promptHistory.d.ts
CHANGED
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* the prompt is returned again under the same identity once its first
|
|
24
24
|
* answering call brings model or usage (`metadataStatus` says how settled).
|
|
25
25
|
*/
|
|
26
|
-
import * as fs from 'fs';
|
|
27
26
|
import { type HumanPromptProvider } from './humanPrompt';
|
|
27
|
+
import { type RolloutContentStat } from './providers/compressedRollouts';
|
|
28
28
|
export type PromptHistoryProvider = HumanPromptProvider;
|
|
29
29
|
export type PromptHistoryBound = 'maxSessions' | 'maxFileBytes' | 'maxTotalBytes' | 'maxEntries' | 'deadline' | 'aborted';
|
|
30
30
|
export interface PromptHistoryBounds {
|
|
@@ -235,6 +235,14 @@ export interface CollectPromptHistoryOptions {
|
|
|
235
235
|
* both modes, so a cursor written in one mode resumes in the other.
|
|
236
236
|
*/
|
|
237
237
|
includeText?: boolean;
|
|
238
|
+
/**
|
|
239
|
+
* Codex: also scan compressed rollouts (`….jsonl.zst`, decompressed when
|
|
240
|
+
* the runtime has `zlib.zstdDecompressSync`; skipped otherwise). Offsets
|
|
241
|
+
* and sizes in the cursor are then those of the decompressed content, so
|
|
242
|
+
* a session whose rollout Codex compressed continues where it stopped.
|
|
243
|
+
* Default false.
|
|
244
|
+
*/
|
|
245
|
+
includeCompressedRollouts?: boolean;
|
|
238
246
|
}
|
|
239
247
|
export interface PromptHistoryResult {
|
|
240
248
|
/** Ordered by provider, session, then ordinal. */
|
|
@@ -375,6 +383,8 @@ export declare function resolveRoots(workspacePaths: string[], includeWorktrees:
|
|
|
375
383
|
listingRoots: string[];
|
|
376
384
|
};
|
|
377
385
|
/** @internal */
|
|
378
|
-
export declare function listCandidates(providers: PromptHistoryProvider[], listingRoots: string[], sinceMs: number | null
|
|
386
|
+
export declare function listCandidates(providers: PromptHistoryProvider[], listingRoots: string[], sinceMs: number | null, options?: {
|
|
387
|
+
includeCompressedRollouts?: boolean;
|
|
388
|
+
}): Promise<Candidate[]>;
|
|
379
389
|
/** @internal */
|
|
380
|
-
export declare function scanFile(candidate: Candidate,
|
|
390
|
+
export declare function scanFile(candidate: Candidate, fileStat: RolloutContentStat, prior: PromptHistoryCursorSession | undefined, budget: FileBudget, context: ScanContext): Promise<FileOutcome>;
|