@bbigbang/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/dist/config.js +380 -0
  2. package/dist/execution/executionDispatcher.js +3810 -0
  3. package/dist/main.js +90 -0
  4. package/dist/nodeEventHistory.js +206 -0
  5. package/dist/scheduler/dreamLogic.js +50 -0
  6. package/dist/scheduler/dreamScheduler.js +65 -0
  7. package/dist/services/agentFileAccessService.js +1913 -0
  8. package/dist/services/agentRuntimeCleanupBroker.js +62 -0
  9. package/dist/services/agentSkillsBroker.js +118 -0
  10. package/dist/services/agentSkillsService.js +83 -0
  11. package/dist/services/agentWorkspaceBroker.js +937 -0
  12. package/dist/services/agentWorkspaceService.js +70 -0
  13. package/dist/services/appVersion.js +14 -0
  14. package/dist/services/auth.js +586 -0
  15. package/dist/services/claudeControlBroker.js +154 -0
  16. package/dist/services/claudeTranscriptBroker.js +100 -0
  17. package/dist/services/claudeTranscriptService.js +359 -0
  18. package/dist/services/codexAppServerBroker.js +155 -0
  19. package/dist/services/codexTranscriptBroker.js +98 -0
  20. package/dist/services/codexTranscriptService.js +961 -0
  21. package/dist/services/droidMissionBroker.js +124 -0
  22. package/dist/services/droidMissionImporter.js +630 -0
  23. package/dist/services/droidModelOptions.js +165 -0
  24. package/dist/services/hubServerRegistrationService.js +268 -0
  25. package/dist/services/libraryManifest.js +43 -0
  26. package/dist/services/libraryScaffold.js +26 -0
  27. package/dist/services/libraryService.js +2263 -0
  28. package/dist/services/memoryService.js +386 -0
  29. package/dist/services/missionEvidence.js +377 -0
  30. package/dist/services/missionService.js +2361 -0
  31. package/dist/services/missionTrace.js +158 -0
  32. package/dist/services/nativeMissionBriefParser.js +120 -0
  33. package/dist/services/nativeMissionOrchestrator.js +2045 -0
  34. package/dist/services/nativeMissionReportGenerator.js +227 -0
  35. package/dist/services/nativeMissionValidationRunner.js +452 -0
  36. package/dist/services/nativeMissionWorkerBroker.js +190 -0
  37. package/dist/services/nodeRegistry.js +34 -0
  38. package/dist/services/nodeStateReconciler.js +97 -0
  39. package/dist/services/panelMediaScanner.js +119 -0
  40. package/dist/services/persistentRuntimeJsonlClient.js +153 -0
  41. package/dist/services/platformAgentPolicy.js +180 -0
  42. package/dist/services/platformAgentService.js +2041 -0
  43. package/dist/services/projectAccessResolver.js +93 -0
  44. package/dist/services/projectService.js +392 -0
  45. package/dist/services/resourceSpaceService.js +140 -0
  46. package/dist/services/scenarioRuntimeService.js +1130 -0
  47. package/dist/services/suggestedPlannerService.js +868 -0
  48. package/dist/services/workbenchGitBroker.js +161 -0
  49. package/dist/services/workbenchGitService.js +69 -0
  50. package/dist/services/workbenchInspectBroker.js +65 -0
  51. package/dist/services/workbenchNodePathService.js +79 -0
  52. package/dist/services/workbenchRegistryService.js +240 -0
  53. package/dist/services/workbenchRootService.js +181 -0
  54. package/dist/services/workbenchTerminalBroker.js +378 -0
  55. package/dist/services/workspaceRunOwnership.js +60 -0
  56. package/dist/services/workspaceScaffold.js +105 -0
  57. package/dist/services/workspaceSessionRuntimeService.js +576 -0
  58. package/dist/services/workspaceSessionService.js +245 -0
  59. package/dist/services/workspaceToolActionRunner.js +1582 -0
  60. package/dist/services/workspaceToolErrors.js +10 -0
  61. package/dist/services/workspaceToolExecutionUtils.js +895 -0
  62. package/dist/services/workspaceToolLatestStateProjector.js +91 -0
  63. package/dist/services/workspaceToolManifest.js +572 -0
  64. package/dist/services/workspaceToolMutationQueue.js +43 -0
  65. package/dist/services/workspaceToolPanelProjection.js +460 -0
  66. package/dist/services/workspaceToolPromotion.js +255 -0
  67. package/dist/services/workspaceToolPromotionState.js +224 -0
  68. package/dist/services/workspaceToolPublishDiagnostics.js +189 -0
  69. package/dist/services/workspaceToolPublishIdentityResolver.js +146 -0
  70. package/dist/services/workspaceToolReadModel.js +378 -0
  71. package/dist/services/workspaceToolRunLedger.js +239 -0
  72. package/dist/services/workspaceToolService.js +3067 -0
  73. package/dist/services/workspaceToolSnapshotPanelSync.js +293 -0
  74. package/dist/services/workspaceToolTerminalLifecycle.js +283 -0
  75. package/dist/services/workspaceToolTypes.js +1 -0
  76. package/dist/services/workspaceToolUploadMaterializer.js +228 -0
  77. package/dist/web/actionCardRoutes.js +129 -0
  78. package/dist/web/actionCards.js +469 -0
  79. package/dist/web/activationContext.js +684 -0
  80. package/dist/web/agentChannelGuards.js +48 -0
  81. package/dist/web/agentMentionCooldowns.js +32 -0
  82. package/dist/web/agentReminders.js +1668 -0
  83. package/dist/web/agentRuntimePresence.js +197 -0
  84. package/dist/web/agentSelfState.js +494 -0
  85. package/dist/web/agentTaskLinks.js +26 -0
  86. package/dist/web/agentVisibility.js +79 -0
  87. package/dist/web/assets.js +95 -0
  88. package/dist/web/channelActivationPrompt.js +395 -0
  89. package/dist/web/channelMemoryNotes.js +127 -0
  90. package/dist/web/channelMentions.js +10 -0
  91. package/dist/web/channelMessageSequences.js +19 -0
  92. package/dist/web/channelSubscriptions.js +26 -0
  93. package/dist/web/clearedTaskRoots.js +10 -0
  94. package/dist/web/collaborationPromptGuidance.js +36 -0
  95. package/dist/web/collaborationSurfaceState.js +140 -0
  96. package/dist/web/contextBundleRanking.js +154 -0
  97. package/dist/web/contextBundleResolver.js +488 -0
  98. package/dist/web/conversationBuiltinSkillRoots.js +50 -0
  99. package/dist/web/conversationControls.js +232 -0
  100. package/dist/web/conversationHandoffs.js +612 -0
  101. package/dist/web/conversationManager.js +2511 -0
  102. package/dist/web/conversationSummaries.js +876 -0
  103. package/dist/web/conversationSurfaceKinds.js +17 -0
  104. package/dist/web/conversationTargets.js +173 -0
  105. package/dist/web/directActivationPrompt.js +122 -0
  106. package/dist/web/directReplyTargets.js +69 -0
  107. package/dist/web/directThreadResolver.js +129 -0
  108. package/dist/web/dmTaskHandoffPrompt.js +120 -0
  109. package/dist/web/dmTaskThreadStatusProjection.js +229 -0
  110. package/dist/web/ftsQuery.js +33 -0
  111. package/dist/web/internalAgentRouter.js +11341 -0
  112. package/dist/web/libraryCuratorScheduler.js +58 -0
  113. package/dist/web/libraryDocumentPromptGuidance.js +8 -0
  114. package/dist/web/messageCheckpoints.js +19 -0
  115. package/dist/web/nodeWsHandler.js +2495 -0
  116. package/dist/web/notificationRounds.js +1061 -0
  117. package/dist/web/panelActionMessages.js +108 -0
  118. package/dist/web/panelActivationPrompt.js +18 -0
  119. package/dist/web/panelAudit.js +273 -0
  120. package/dist/web/panelLifecycle.js +222 -0
  121. package/dist/web/panelMediaPolicy.js +43 -0
  122. package/dist/web/panelPathPolicy.js +63 -0
  123. package/dist/web/panelPreviews.js +175 -0
  124. package/dist/web/panelQueryHandles.js +2749 -0
  125. package/dist/web/panelRoutes.js +2147 -0
  126. package/dist/web/panels.js +904 -0
  127. package/dist/web/peerInboxAggregates.js +1247 -0
  128. package/dist/web/planApprovalState.js +92 -0
  129. package/dist/web/platformAgentScheduler.js +66 -0
  130. package/dist/web/proactiveOpportunities.js +452 -0
  131. package/dist/web/promptContextSections.js +242 -0
  132. package/dist/web/promptHistorySanitizer.js +26 -0
  133. package/dist/web/promptSlashCommands.js +158 -0
  134. package/dist/web/rollingConversationSummary.js +453 -0
  135. package/dist/web/routeHelpers.js +11 -0
  136. package/dist/web/routes/handoff.js +288 -0
  137. package/dist/web/routes/history.js +345 -0
  138. package/dist/web/routes/memory.js +258 -0
  139. package/dist/web/routes/selfState.js +171 -0
  140. package/dist/web/routes/workspace.js +154 -0
  141. package/dist/web/runSurfaceWatermarks.js +431 -0
  142. package/dist/web/runtimeCapabilities.js +48 -0
  143. package/dist/web/sameAgentHandoffs.js +494 -0
  144. package/dist/web/server.js +15567 -0
  145. package/dist/web/sharedCollaborationCapsules.js +163 -0
  146. package/dist/web/soloSessionRelay.js +42 -0
  147. package/dist/web/soloWsHandler.js +138 -0
  148. package/dist/web/suggestedPlannerScheduler.js +56 -0
  149. package/dist/web/surfaceActivationPolicy.js +108 -0
  150. package/dist/web/surfaceCollaborators.js +61 -0
  151. package/dist/web/surfaceSystemStatus.js +263 -0
  152. package/dist/web/targetParticipants.js +77 -0
  153. package/dist/web/taskEvents.js +49 -0
  154. package/dist/web/taskLifecycleMessages.js +165 -0
  155. package/dist/web/taskLoops.js +732 -0
  156. package/dist/web/taskMemoryNotes.js +224 -0
  157. package/dist/web/taskNumbers.js +16 -0
  158. package/dist/web/taskOwnerGuards.js +49 -0
  159. package/dist/web/taskParticipantResolver.js +42 -0
  160. package/dist/web/taskParticipants.js +97 -0
  161. package/dist/web/taskSourceDetails.js +20 -0
  162. package/dist/web/taskStateViews.js +210 -0
  163. package/dist/web/taskStatusTransitions.js +9 -0
  164. package/dist/web/taskThreadFollowups.js +599 -0
  165. package/dist/web/taskThreadRuntimeClosure.js +685 -0
  166. package/dist/web/taskUpdateDelivery.js +104 -0
  167. package/dist/web/threadReplyContentHeuristics.js +30 -0
  168. package/dist/web/threadRoots.js +61 -0
  169. package/dist/web/threadTaskBindings.js +365 -0
  170. package/dist/web/uiPanelPromptGuidance.js +27 -0
  171. package/dist/web/workspaceMemoryHints.js +143 -0
  172. package/dist/web/workspaceToolPromptGuidance.js +30 -0
  173. package/dist/web/wsHandler.js +397 -0
  174. package/dist/web/wsSink.js +116 -0
  175. package/package.json +54 -0
@@ -0,0 +1,961 @@
1
+ import { buildAgentSessionSystemPromptText } from '@bbigbang/memory';
2
+ import { log } from '@bbigbang/runtime-acp';
3
+ import { readFileSync } from 'node:fs';
4
+ import { iterateParsedNodeEvents } from '../nodeEventHistory.js';
5
+ const MAX_INLINE_TRANSCRIPT_BYTES = 2 * 1024 * 1024;
6
+ const HEURISTIC_TRANSCRIPT_LOOKBACK_MS = 24 * 60 * 60 * 1000;
7
+ const MAX_SESSION_MATCH_TRANSCRIPT_FILES = 8;
8
+ const MAX_HEURISTIC_TRANSCRIPT_FILES = 6;
9
+ const REDACTED_NOTIFICATION_PROMPT = '[notification delivery payload redacted]';
10
+ export const CODEX_DEBUG_UNSUPPORTED_RUNTIME_ERROR = 'Codex debug is only supported for codex_acp and codex_app_server conversations.';
11
+ export class CodexTranscriptService {
12
+ rolloutCache = new Map();
13
+ db;
14
+ broker;
15
+ getConversationById;
16
+ getAgentById;
17
+ getAcpSessionIdByConversationId;
18
+ getCodexContextWindowByModel;
19
+ constructor(params) {
20
+ this.db = params.db;
21
+ this.broker = params.broker;
22
+ this.getConversationById = params.getConversationById;
23
+ this.getAgentById = params.getAgentById;
24
+ this.getAcpSessionIdByConversationId = params.getAcpSessionIdByConversationId ?? (() => null);
25
+ this.getCodexContextWindowByModel = params.getCodexContextWindowByModel ?? defaultGetCodexContextWindowByModel;
26
+ }
27
+ async getConversationDebug(conversationId) {
28
+ const conversation = this.getConversationById(conversationId);
29
+ if (!conversation)
30
+ throw new Error('Conversation not found.');
31
+ if (conversation.agentType !== 'codex_acp' && conversation.agentType !== 'codex_app_server') {
32
+ throw new Error(CODEX_DEBUG_UNSUPPORTED_RUNTIME_ERROR);
33
+ }
34
+ if (!conversation.nodeId)
35
+ throw new Error('Conversation is not assigned to a remote node.');
36
+ if (!conversation.workspacePath)
37
+ throw new Error('Conversation has no workspace path.');
38
+ const replyTarget = (conversation.replyTarget ?? '').trim();
39
+ if (!replyTarget)
40
+ throw new Error('Conversation has no reply target.');
41
+ const acpSessionId = this.getAcpSessionIdByConversationId(conversationId)?.trim() || undefined;
42
+ const heuristicTranscriptCutoff = Date.now() - HEURISTIC_TRANSCRIPT_LOOKBACK_MS;
43
+ const listing = await this.broker.listFiles(conversation.nodeId).catch((error) => {
44
+ log.warn('[codex-debug] failed to list transcripts', {
45
+ conversationId,
46
+ nodeId: conversation.nodeId,
47
+ error: String(error?.message ?? error),
48
+ });
49
+ return {
50
+ rootPath: '',
51
+ files: [],
52
+ truncated: false,
53
+ };
54
+ });
55
+ const exactSessionRollouts = [];
56
+ const heuristicRollouts = [];
57
+ const workspaceCandidates = listing.files.filter((file) => {
58
+ if (!acpSessionId && file.modifiedAt < heuristicTranscriptCutoff) {
59
+ return false;
60
+ }
61
+ if (file.size > MAX_INLINE_TRANSCRIPT_BYTES) {
62
+ return false;
63
+ }
64
+ if (file.cwd && file.cwd.trim() !== conversation.workspacePath) {
65
+ return false;
66
+ }
67
+ return true;
68
+ });
69
+ const filesToScan = acpSessionId
70
+ ? selectTranscriptCandidatesForSession(workspaceCandidates, acpSessionId, heuristicTranscriptCutoff)
71
+ : workspaceCandidates.slice(0, MAX_HEURISTIC_TRANSCRIPT_FILES);
72
+ for (const file of filesToScan) {
73
+ const loaded = await this.loadParsedRollout(conversationId, conversation.nodeId, file);
74
+ if (loaded.timedOut)
75
+ break;
76
+ const parsed = loaded.rollout;
77
+ if (!parsed)
78
+ continue;
79
+ applyCodexContextWindowFallback(parsed, this.getCodexContextWindowByModel);
80
+ const sessionCwd = parsed.cwd?.trim();
81
+ if (!sessionCwd || sessionCwd !== conversation.workspacePath)
82
+ continue;
83
+ const exactSessionMatch = acpSessionId != null
84
+ && (parsed.sessionId === acpSessionId || file.path.includes(acpSessionId));
85
+ let matchedTurns = parsed.turns.filter((turn) => (turn.replyTarget ?? '').trim() === replyTarget);
86
+ if (matchedTurns.length === 0 && exactSessionMatch) {
87
+ matchedTurns = parsed.turns.filter((turn) => !(turn.replyTarget ?? '').trim());
88
+ if (matchedTurns.length === 0) {
89
+ matchedTurns = parsed.turns;
90
+ }
91
+ }
92
+ if (matchedTurns.length === 0)
93
+ continue;
94
+ const matchedRollout = {
95
+ ...parsed,
96
+ turns: matchedTurns,
97
+ };
98
+ if (exactSessionMatch) {
99
+ exactSessionRollouts.push(matchedRollout);
100
+ }
101
+ else {
102
+ heuristicRollouts.push(matchedRollout);
103
+ }
104
+ }
105
+ const sessionMatchMissed = Boolean(acpSessionId) && exactSessionRollouts.length === 0;
106
+ const usingExactSession = Boolean(acpSessionId) && exactSessionRollouts.length > 0;
107
+ const rollouts = usingExactSession ? exactSessionRollouts : heuristicRollouts;
108
+ rollouts.sort((a, b) => {
109
+ const aTime = getRolloutSortTime(a);
110
+ const bTime = getRolloutSortTime(b);
111
+ return usingExactSession ? aTime - bTime : bTime - aTime;
112
+ });
113
+ const platformInputs = listPlatformInputsForConversation(this.db, {
114
+ conversation,
115
+ getAgentById: this.getAgentById,
116
+ includePlatformTrace: false,
117
+ newestFirst: !usingExactSession,
118
+ });
119
+ const unmatchedPlatformInputs = attachPlatformInputsToTurns(rollouts, platformInputs);
120
+ const notificationDeliveries = listNotificationDeliveryEvidenceForConversation(this.db, conversation.id);
121
+ return {
122
+ provider: 'codex',
123
+ conversationId: conversation.id,
124
+ agentType: conversation.agentType,
125
+ workspacePath: conversation.workspacePath,
126
+ replyTarget,
127
+ acpSessionId,
128
+ matchMode: usingExactSession ? 'acp_session_id' : 'heuristic',
129
+ sessionMatchMissed,
130
+ truncated: listing.truncated,
131
+ rollouts,
132
+ unmatchedPlatformInputs,
133
+ notificationDeliveries,
134
+ };
135
+ }
136
+ async loadParsedRollout(conversationId, nodeId, file) {
137
+ const cacheKey = `${nodeId}:${file.path}:${file.modifiedAt}:${file.size}`;
138
+ const cached = this.rolloutCache.get(cacheKey);
139
+ if (cached) {
140
+ this.rolloutCache.delete(cacheKey);
141
+ this.rolloutCache.set(cacheKey, cached);
142
+ return { rollout: cached, timedOut: false };
143
+ }
144
+ let content;
145
+ try {
146
+ const result = await this.broker.readFile(nodeId, file.path);
147
+ content = result.content;
148
+ }
149
+ catch (error) {
150
+ const message = String(error?.message ?? error);
151
+ log.warn('[codex-debug] failed to read transcript', {
152
+ conversationId,
153
+ path: file.path,
154
+ error: message,
155
+ });
156
+ return { rollout: null, timedOut: isTranscriptTimeoutError(message) };
157
+ }
158
+ const parsed = parseCodexRollout(content, {
159
+ path: file.path,
160
+ modifiedAt: file.modifiedAt,
161
+ size: file.size,
162
+ });
163
+ setCachedRollout(this.rolloutCache, cacheKey, parsed);
164
+ return { rollout: parsed, timedOut: false };
165
+ }
166
+ }
167
+ let cachedCodexModelWindows = null;
168
+ function selectTranscriptCandidatesForSession(files, acpSessionId, heuristicCutoffMs) {
169
+ const exactCandidates = files.filter((file) => file.sessionId === acpSessionId);
170
+ if (exactCandidates.length > 0)
171
+ return exactCandidates.slice(0, MAX_SESSION_MATCH_TRANSCRIPT_FILES);
172
+ const hintedCandidates = files.filter((file) => file.path.includes(acpSessionId));
173
+ if (hintedCandidates.length > 0)
174
+ return hintedCandidates.slice(0, MAX_SESSION_MATCH_TRANSCRIPT_FILES);
175
+ const heuristicCandidates = files.filter((file) => file.modifiedAt >= heuristicCutoffMs);
176
+ if (heuristicCandidates.length > 0)
177
+ return heuristicCandidates.slice(0, MAX_HEURISTIC_TRANSCRIPT_FILES);
178
+ return files.slice(0, MAX_HEURISTIC_TRANSCRIPT_FILES);
179
+ }
180
+ function setCachedRollout(cache, key, rollout, maxEntries = 64) {
181
+ cache.set(key, rollout);
182
+ while (cache.size > maxEntries) {
183
+ const oldestKey = cache.keys().next().value;
184
+ if (!oldestKey)
185
+ break;
186
+ cache.delete(oldestKey);
187
+ }
188
+ }
189
+ function parseCodexRollout(content, meta) {
190
+ const lines = content.split(/\r?\n/).filter((line) => line.trim().length > 0);
191
+ let sessionId;
192
+ let cwd;
193
+ let model;
194
+ let baseInstructions;
195
+ const preludeDeveloperMessages = [];
196
+ const preludeUserMessages = [];
197
+ const turns = [];
198
+ let currentTurn = null;
199
+ const functionCallById = new Map();
200
+ const pushCurrentTurn = () => {
201
+ if (!currentTurn)
202
+ return;
203
+ turns.push(currentTurn);
204
+ currentTurn = null;
205
+ functionCallById.clear();
206
+ };
207
+ for (const line of lines) {
208
+ let parsedLine;
209
+ try {
210
+ parsedLine = JSON.parse(line);
211
+ }
212
+ catch {
213
+ continue;
214
+ }
215
+ const timestamp = typeof parsedLine.timestamp === 'string' ? parsedLine.timestamp : new Date(meta.modifiedAt).toISOString();
216
+ const payload = parsedLine.payload ?? {};
217
+ if (parsedLine.type === 'session_meta') {
218
+ sessionId = stringValue(payload.id);
219
+ cwd = stringValue(payload.cwd);
220
+ const base = payload.base_instructions;
221
+ baseInstructions = stringValue(base?.text);
222
+ continue;
223
+ }
224
+ if (parsedLine.type === 'turn_context') {
225
+ pushCurrentTurn();
226
+ model = model ?? stringValue(payload.model);
227
+ currentTurn = {
228
+ turnId: stringValue(payload.turn_id) ?? `turn-${turns.length + 1}`,
229
+ timestamp,
230
+ cwd: stringValue(payload.cwd),
231
+ inputBlocks: [],
232
+ functionCalls: [],
233
+ assistantOutputs: [],
234
+ reasoningSummaries: [],
235
+ hasEncryptedReasoning: false,
236
+ };
237
+ continue;
238
+ }
239
+ if (!currentTurn) {
240
+ const preludeText = extractMessageText(payload);
241
+ if (!preludeText)
242
+ continue;
243
+ if (payload.type === 'message' && payload.role === 'developer')
244
+ preludeDeveloperMessages.push(preludeText);
245
+ if (payload.type === 'message' && payload.role === 'user')
246
+ preludeUserMessages.push(preludeText);
247
+ continue;
248
+ }
249
+ if (parsedLine.type === 'response_item') {
250
+ const payloadType = stringValue(payload.type);
251
+ if (payloadType === 'message') {
252
+ const role = stringValue(payload.role);
253
+ const messageText = extractMessageText(payload);
254
+ if (role === 'user' && messageText) {
255
+ currentTurn.inputBlocks.push(...extractInputBlocks(payload));
256
+ currentTurn.replyTarget = currentTurn.replyTarget ?? parseReplyTarget(messageText);
257
+ currentTurn.triggerTarget = currentTurn.triggerTarget ?? parseTriggerTarget(messageText);
258
+ }
259
+ else if (role === 'assistant') {
260
+ for (const text of extractAssistantOutputs(payload)) {
261
+ currentTurn.assistantOutputs.push({
262
+ text,
263
+ phase: stringValue(payload.phase) ?? undefined,
264
+ timestamp,
265
+ });
266
+ }
267
+ }
268
+ }
269
+ else if (payloadType === 'function_call') {
270
+ recordCodexFunctionCall(currentTurn, functionCallById, payload, timestamp);
271
+ }
272
+ else if (payloadType === 'tool_search_call') {
273
+ recordCodexFunctionCall(currentTurn, functionCallById, payload, timestamp, {
274
+ fallbackName: 'tool_search',
275
+ });
276
+ }
277
+ else if (payloadType === 'function_call_output' || payloadType === 'tool_search_output') {
278
+ attachCodexFunctionCallOutput(functionCallById, payload, timestamp, {
279
+ fallbackName: payloadType === 'tool_search_output' ? 'tool_search' : 'unknown',
280
+ });
281
+ }
282
+ else if (payloadType === 'reasoning') {
283
+ const summary = Array.isArray(payload.summary) ? payload.summary.map((item) => {
284
+ if (!item || typeof item !== 'object')
285
+ return '';
286
+ return stringValue(item.text) ?? '';
287
+ }).filter(Boolean) : [];
288
+ currentTurn.reasoningSummaries.push(...summary);
289
+ if (typeof payload.encrypted_content === 'string' && payload.encrypted_content.trim()) {
290
+ currentTurn.hasEncryptedReasoning = true;
291
+ }
292
+ }
293
+ continue;
294
+ }
295
+ if (parsedLine.type === 'event_msg') {
296
+ const eventType = stringValue(payload.type);
297
+ if (eventType === 'user_message') {
298
+ const combined = stringValue(payload.message);
299
+ if (combined) {
300
+ currentTurn.combinedUserMessage = combined;
301
+ currentTurn.replyTarget = currentTurn.replyTarget ?? parseReplyTarget(combined);
302
+ currentTurn.triggerTarget = currentTurn.triggerTarget ?? parseTriggerTarget(combined);
303
+ }
304
+ }
305
+ else if (eventType === 'token_count') {
306
+ currentTurn.tokenUsage = parseTokenUsage(payload);
307
+ }
308
+ else if (eventType === 'agent_message') {
309
+ const message = stringValue(payload.message);
310
+ if (message) {
311
+ currentTurn.assistantOutputs.push({
312
+ text: message,
313
+ phase: stringValue(payload.phase) ?? undefined,
314
+ timestamp,
315
+ });
316
+ }
317
+ }
318
+ else if (eventType === 'mcp_tool_call_begin' || eventType === 'tool_search_call') {
319
+ recordCodexFunctionCall(currentTurn, functionCallById, payload, timestamp, {
320
+ fallbackName: eventType === 'tool_search_call' ? 'tool_search' : 'mcp_tool',
321
+ });
322
+ }
323
+ else if (eventType === 'mcp_tool_call_end'
324
+ || eventType === 'mcp_tool_call_result'
325
+ || eventType === 'tool_search_output') {
326
+ attachCodexFunctionCallOutput(functionCallById, payload, timestamp, {
327
+ fallbackName: eventType === 'tool_search_output' ? 'tool_search' : 'mcp_tool',
328
+ });
329
+ }
330
+ }
331
+ }
332
+ pushCurrentTurn();
333
+ return {
334
+ path: meta.path,
335
+ modifiedAt: meta.modifiedAt,
336
+ size: meta.size,
337
+ sessionId,
338
+ cwd,
339
+ model,
340
+ baseInstructions,
341
+ preludeDeveloperMessages,
342
+ preludeUserMessages,
343
+ turns,
344
+ };
345
+ }
346
+ function recordCodexFunctionCall(turn, functionCallById, payload, timestamp, options) {
347
+ const callId = getCodexCallId(payload) ?? `call-${turn.functionCalls.length + 1}`;
348
+ const existing = functionCallById.get(callId);
349
+ if (existing) {
350
+ existing.name = existing.name === 'unknown'
351
+ ? formatCodexFunctionCallName(payload, options?.fallbackName)
352
+ : existing.name;
353
+ if (!existing.arguments) {
354
+ existing.arguments = formatCodexFunctionCallArguments(payload);
355
+ }
356
+ return existing;
357
+ }
358
+ const call = {
359
+ callId,
360
+ name: formatCodexFunctionCallName(payload, options?.fallbackName),
361
+ arguments: formatCodexFunctionCallArguments(payload),
362
+ timestamp,
363
+ };
364
+ functionCallById.set(callId, call);
365
+ turn.functionCalls.push(call);
366
+ return call;
367
+ }
368
+ function attachCodexFunctionCallOutput(functionCallById, payload, timestamp, options) {
369
+ const callId = getCodexCallId(payload);
370
+ if (!callId)
371
+ return;
372
+ let existing = functionCallById.get(callId);
373
+ if (!existing) {
374
+ existing = {
375
+ callId,
376
+ name: formatCodexFunctionCallName(payload, options?.fallbackName),
377
+ arguments: formatCodexFunctionCallArguments(payload),
378
+ timestamp,
379
+ };
380
+ functionCallById.set(callId, existing);
381
+ }
382
+ const output = formatCodexFunctionCallOutput(payload);
383
+ if (output == null)
384
+ return;
385
+ existing.output = output;
386
+ existing.outputTimestamp = timestamp;
387
+ }
388
+ function getCodexCallId(payload) {
389
+ return stringValue(payload.call_id)
390
+ ?? stringValue(payload.callId)
391
+ ?? stringValue(payload.tool_call_id)
392
+ ?? stringValue(payload.toolCallId)
393
+ ?? stringValue(payload.item_id)
394
+ ?? stringValue(payload.itemId)
395
+ ?? stringValue(payload.id);
396
+ }
397
+ function formatCodexFunctionCallName(payload, fallbackName = 'unknown') {
398
+ const name = stringValue(payload.name)
399
+ ?? stringValue(payload.tool)
400
+ ?? stringValue(payload.tool_name)
401
+ ?? fallbackName;
402
+ const namespaceParts = getCodexNamespaceParts(payload.namespace ?? payload.server);
403
+ if (namespaceParts.length === 0)
404
+ return name;
405
+ const prefix = namespaceParts.join('__');
406
+ if (name === prefix || name.startsWith(`${prefix}__`))
407
+ return name;
408
+ return `${prefix}__${name}`;
409
+ }
410
+ function getCodexNamespaceParts(value) {
411
+ if (typeof value === 'string') {
412
+ return value
413
+ .split(/__|[./]/)
414
+ .map((part) => part.trim())
415
+ .filter(Boolean);
416
+ }
417
+ if (Array.isArray(value)) {
418
+ return value.flatMap((part) => getCodexNamespaceParts(part));
419
+ }
420
+ return [];
421
+ }
422
+ function formatCodexFunctionCallArguments(payload) {
423
+ const direct = stringValue(payload.arguments) ?? stringValue(payload.input);
424
+ if (direct != null)
425
+ return direct;
426
+ if (payload.arguments != null)
427
+ return formatPlatformEventValue(payload.arguments) ?? '';
428
+ if (payload.input != null)
429
+ return formatPlatformEventValue(payload.input) ?? '';
430
+ if (payload.query != null) {
431
+ return formatPlatformEventValue({
432
+ query: payload.query,
433
+ action: payload.action ?? undefined,
434
+ }) ?? '';
435
+ }
436
+ const fallbackPayload = omitDebugCallPayloadKeys(payload, [
437
+ 'output',
438
+ 'result',
439
+ 'content',
440
+ 'contentItems',
441
+ 'error',
442
+ 'message',
443
+ ]);
444
+ return fallbackPayload ? formatPlatformEventValue(fallbackPayload) ?? '' : '';
445
+ }
446
+ function formatCodexFunctionCallOutput(payload) {
447
+ const direct = stringValue(payload.output)
448
+ ?? stringValue(payload.result)
449
+ ?? stringValue(payload.message)
450
+ ?? stringValue(payload.error?.message);
451
+ if (direct != null)
452
+ return direct;
453
+ if (payload.output != null)
454
+ return formatPlatformEventValue(payload.output);
455
+ if (payload.result != null)
456
+ return formatPlatformEventValue(payload.result);
457
+ if (payload.contentItems != null)
458
+ return formatPlatformEventValue(payload.contentItems);
459
+ if (payload.content != null)
460
+ return formatPlatformEventValue(payload.content);
461
+ if (payload.error != null)
462
+ return formatPlatformEventValue(payload.error);
463
+ const fallbackPayload = omitDebugCallPayloadKeys(payload, [
464
+ 'arguments',
465
+ 'input',
466
+ 'query',
467
+ 'action',
468
+ ]);
469
+ return fallbackPayload ? formatPlatformEventValue(fallbackPayload) : undefined;
470
+ }
471
+ function omitDebugCallPayloadKeys(payload, extraKeys) {
472
+ const ignored = new Set([
473
+ 'type',
474
+ 'call_id',
475
+ 'callId',
476
+ 'tool_call_id',
477
+ 'toolCallId',
478
+ 'item_id',
479
+ 'itemId',
480
+ 'id',
481
+ 'name',
482
+ 'namespace',
483
+ 'server',
484
+ 'tool',
485
+ 'tool_name',
486
+ ...extraKeys,
487
+ ]);
488
+ const entries = Object.entries(payload).filter(([key, value]) => !ignored.has(key) && value !== undefined);
489
+ return entries.length > 0 ? Object.fromEntries(entries) : null;
490
+ }
491
+ export function getRolloutSortTime(rollout) {
492
+ const turnTimes = rollout.turns
493
+ .map((turn) => Date.parse(turn.timestamp))
494
+ .filter((value) => Number.isFinite(value));
495
+ if (turnTimes.length > 0) {
496
+ return Math.max(...turnTimes);
497
+ }
498
+ return rollout.modifiedAt;
499
+ }
500
+ export function attachPlatformInputsToTurns(rollouts, platformInputs) {
501
+ const turnRefs = [];
502
+ for (const rollout of rollouts) {
503
+ for (const turn of rollout.turns) {
504
+ turnRefs.push({
505
+ turn,
506
+ rolloutSessionId: rollout.sessionId,
507
+ });
508
+ }
509
+ }
510
+ const matchedTurnIndexes = new Set();
511
+ const matchedPlatformIndexes = new Set();
512
+ const chronologicalPlatformIndexes = platformInputs
513
+ .map((input, index) => ({ input, index }))
514
+ .sort((a, b) => a.input.startedAt - b.input.startedAt || a.index - b.index);
515
+ for (const { input, index } of chronologicalPlatformIndexes) {
516
+ if (!hasReliablePlatformTime(input))
517
+ continue;
518
+ const matchIndex = findBestTimedTurnMatch(turnRefs, matchedTurnIndexes, input);
519
+ if (matchIndex == null)
520
+ continue;
521
+ turnRefs[matchIndex].turn.platformInput = input;
522
+ matchedTurnIndexes.add(matchIndex);
523
+ matchedPlatformIndexes.add(index);
524
+ }
525
+ if (!hasReliableTimeMatchingData(turnRefs, platformInputs)) {
526
+ const remainingTurnIndexes = turnRefs
527
+ .map((_, index) => index)
528
+ .filter((index) => !matchedTurnIndexes.has(index));
529
+ const remainingPlatformIndexes = platformInputs
530
+ .map((_, index) => index)
531
+ .filter((index) => !matchedPlatformIndexes.has(index));
532
+ const pairCount = Math.min(remainingTurnIndexes.length, remainingPlatformIndexes.length);
533
+ for (let index = 0; index < pairCount; index += 1) {
534
+ const turnIndex = remainingTurnIndexes[index];
535
+ const platformIndex = remainingPlatformIndexes[index];
536
+ turnRefs[turnIndex].turn.platformInput = platformInputs[platformIndex];
537
+ matchedTurnIndexes.add(turnIndex);
538
+ matchedPlatformIndexes.add(platformIndex);
539
+ }
540
+ }
541
+ return platformInputs.filter((_, index) => !matchedPlatformIndexes.has(index));
542
+ }
543
+ function hasReliableTimeMatchingData(turnRefs, platformInputs) {
544
+ return turnRefs.some((ref) => hasReliableTimestamp(Date.parse(ref.turn.timestamp)))
545
+ && platformInputs.some(hasReliablePlatformTime);
546
+ }
547
+ function hasReliablePlatformTime(input) {
548
+ return hasReliableTimestamp(input.startedAt);
549
+ }
550
+ function hasReliableTimestamp(value) {
551
+ return Number.isFinite(value) && value >= Date.parse('2020-01-01T00:00:00.000Z');
552
+ }
553
+ function findBestTimedTurnMatch(turnRefs, matchedTurnIndexes, input) {
554
+ let bestIndex = null;
555
+ let bestDistance = Number.POSITIVE_INFINITY;
556
+ for (let index = 0; index < turnRefs.length; index += 1) {
557
+ if (matchedTurnIndexes.has(index))
558
+ continue;
559
+ const ref = turnRefs[index];
560
+ if (!isSessionCompatible(input, ref))
561
+ continue;
562
+ const turnTime = Date.parse(ref.turn.timestamp);
563
+ if (!hasReliableTimestamp(turnTime))
564
+ continue;
565
+ if (!isTurnTimeInsideRun(input, turnTime))
566
+ continue;
567
+ const distance = Math.abs(turnTime - input.startedAt);
568
+ if (distance < bestDistance) {
569
+ bestIndex = index;
570
+ bestDistance = distance;
571
+ }
572
+ }
573
+ return bestIndex;
574
+ }
575
+ function isSessionCompatible(input, ref) {
576
+ if (!input.acpSessionId || !ref.rolloutSessionId)
577
+ return true;
578
+ return input.acpSessionId === ref.rolloutSessionId;
579
+ }
580
+ function isTurnTimeInsideRun(input, turnTime) {
581
+ const beforeToleranceMs = 5_000;
582
+ const afterToleranceMs = 5_000;
583
+ if (turnTime < input.startedAt - beforeToleranceMs)
584
+ return false;
585
+ if (input.endedAt != null && turnTime > input.endedAt + afterToleranceMs)
586
+ return false;
587
+ return true;
588
+ }
589
+ export function listPlatformInputsForConversation(db, params) {
590
+ const rows = db.prepare(`SELECT r.run_id as runId,
591
+ r.prompt_text as runPromptText,
592
+ r.started_at as startedAt,
593
+ r.ended_at as endedAt,
594
+ r.stop_reason as stopReason,
595
+ r.error,
596
+ d.acp_session_id as acpSessionId,
597
+ d.is_fresh_session as isFreshSession,
598
+ d.is_exact as isExact,
599
+ d.dispatch_mode as dispatchMode,
600
+ d.system_prompt_text as snapshotSystemPromptText,
601
+ d.context_text as snapshotContextText,
602
+ d.prompt_text as snapshotPromptText,
603
+ d.dispatched_prompt_text as snapshotDispatchedPromptText
604
+ FROM conversations c
605
+ JOIN runs r ON r.session_key = c.session_key
606
+ LEFT JOIN run_debug_inputs d ON d.run_id = r.run_id
607
+ WHERE c.id = ?
608
+ ORDER BY r.started_at ${params.newestFirst ? 'DESC' : 'ASC'}`).all(params.conversation.id);
609
+ const agent = params.conversation.agentId ? params.getAgentById(params.conversation.agentId) : null;
610
+ const reconstructedSystemPrompt = agent && params.conversation.workspacePath
611
+ ? buildAgentSessionSystemPromptText({
612
+ agentName: agent.name,
613
+ agentBio: agent.description || undefined,
614
+ agentDescription: agent.systemPrompt || undefined,
615
+ workspacePath: params.conversation.workspacePath,
616
+ })
617
+ : undefined;
618
+ return rows.map((row) => {
619
+ const promptText = row.snapshotPromptText ?? row.runPromptText;
620
+ const dispatchedPromptText = row.snapshotDispatchedPromptText ?? undefined;
621
+ return {
622
+ runId: row.runId,
623
+ startedAt: row.startedAt,
624
+ endedAt: row.endedAt,
625
+ stopReason: row.stopReason ?? undefined,
626
+ error: row.error ?? undefined,
627
+ dispatchMode: row.dispatchMode ?? undefined,
628
+ acpSessionId: row.acpSessionId ?? undefined,
629
+ isFreshSession: row.isFreshSession == null ? undefined : Boolean(row.isFreshSession),
630
+ source: row.isExact ? 'exact_snapshot' : 'reconstructed',
631
+ systemPromptText: row.isExact
632
+ ? row.snapshotSystemPromptText ?? undefined
633
+ : row.snapshotSystemPromptText ?? reconstructedSystemPrompt,
634
+ contextText: row.snapshotContextText ?? undefined,
635
+ promptText,
636
+ dispatchedPromptText,
637
+ runtimeInputText: joinDebugText(row.snapshotContextText, dispatchedPromptText ?? promptText),
638
+ platformTrace: params.includePlatformTrace ? buildPlatformTraceForRun(db, row.runId) : undefined,
639
+ };
640
+ });
641
+ }
642
+ export function listNotificationDeliveryEvidenceForConversation(db, conversationId) {
643
+ const rows = db.prepare(`SELECT round_id as roundId,
644
+ target_agent_id as targetAgentId,
645
+ channel_id as channelId,
646
+ surface_thread_key as surfaceThreadKey,
647
+ conversation_id as conversationId,
648
+ status,
649
+ delivery_mode as deliveryMode,
650
+ from_seq_exclusive as fromSeqExclusive,
651
+ to_seq_inclusive as toSeqInclusive,
652
+ message_count as messageCount,
653
+ delivered_at as deliveredAt,
654
+ acked_at as ackedAt,
655
+ superseded_at as supersededAt,
656
+ failed_at as failedAt,
657
+ first_message_id as firstMessageId,
658
+ last_message_id as lastMessageId,
659
+ delivery_payload_json as deliveryPayloadJson
660
+ FROM agent_notification_rounds
661
+ WHERE conversation_id = ?
662
+ ORDER BY created_at ASC`).all(conversationId);
663
+ return rows.map((row) => {
664
+ let payload = {};
665
+ try {
666
+ payload = JSON.parse(row.deliveryPayloadJson);
667
+ }
668
+ catch {
669
+ payload = {};
670
+ }
671
+ const referencesDeletedMessage = notificationDeliveryReferencesDeletedMessage(db, row, payload.activationMetadata);
672
+ const promptText = typeof payload.promptText === 'string'
673
+ && payload.promptText !== REDACTED_NOTIFICATION_PROMPT
674
+ && !referencesDeletedMessage
675
+ ? payload.promptText
676
+ : undefined;
677
+ const activationMetadata = referencesDeletedMessage
678
+ ? redactNotificationActivationMetadata(payload.activationMetadata)
679
+ : payload.activationMetadata;
680
+ return {
681
+ roundId: row.roundId,
682
+ targetAgentId: row.targetAgentId,
683
+ channelId: row.channelId,
684
+ surfaceThreadKey: row.surfaceThreadKey,
685
+ conversationId: row.conversationId,
686
+ status: row.status,
687
+ deliveryMode: row.deliveryMode ?? undefined,
688
+ fromSeqExclusive: row.fromSeqExclusive,
689
+ toSeqInclusive: row.toSeqInclusive,
690
+ messageCount: row.messageCount,
691
+ deliveredAt: row.deliveredAt ?? undefined,
692
+ ackedAt: row.ackedAt ?? undefined,
693
+ supersededAt: row.supersededAt ?? undefined,
694
+ failedAt: row.failedAt ?? undefined,
695
+ promptText,
696
+ activationMetadata,
697
+ };
698
+ });
699
+ }
700
+ function redactNotificationActivationMetadata(value) {
701
+ if (!value || typeof value !== 'object' || Array.isArray(value))
702
+ return undefined;
703
+ const redacted = { ...value };
704
+ delete redacted.promptContextSections;
705
+ delete redacted.slashCommand;
706
+ delete redacted.attachments;
707
+ delete redacted.attachmentIds;
708
+ return Object.keys(redacted).length > 0 ? redacted : undefined;
709
+ }
710
+ function notificationDeliveryReferencesDeletedMessage(db, row, activationMetadata) {
711
+ const directDeletedRow = db.prepare(`SELECT 1
712
+ FROM channel_messages
713
+ WHERE channel_id = ?
714
+ AND COALESCE(thread_root_id, '') = ?
715
+ AND deleted_at IS NOT NULL
716
+ AND (
717
+ message_id = ?
718
+ OR message_id = ?
719
+ OR (seq > ? AND seq <= ?)
720
+ )
721
+ LIMIT 1`).get(row.channelId, row.surfaceThreadKey, row.firstMessageId, row.lastMessageId, row.fromSeqExclusive, row.toSeqInclusive);
722
+ if (directDeletedRow)
723
+ return true;
724
+ const referencedMessageIds = extractNotificationActivationMessageIds(activationMetadata);
725
+ if (referencedMessageIds.length === 0)
726
+ return false;
727
+ const deletedByMessageId = db.prepare(`SELECT 1
728
+ FROM channel_messages
729
+ WHERE message_id = ?
730
+ AND deleted_at IS NOT NULL
731
+ LIMIT 1`);
732
+ return referencedMessageIds.some((messageId) => Boolean(deletedByMessageId.get(messageId)));
733
+ }
734
+ function extractNotificationActivationMessageIds(value) {
735
+ if (!value || typeof value !== 'object')
736
+ return [];
737
+ const metadata = value;
738
+ const ids = new Set();
739
+ const triggerMessage = metadata.triggerMessage;
740
+ if (triggerMessage && typeof triggerMessage === 'object') {
741
+ const messageId = triggerMessage.messageId;
742
+ if (typeof messageId === 'string' && messageId.trim())
743
+ ids.add(messageId);
744
+ }
745
+ const taskPeerUpdate = metadata.taskPeerUpdate;
746
+ if (taskPeerUpdate && typeof taskPeerUpdate === 'object') {
747
+ const record = taskPeerUpdate;
748
+ for (const key of ['firstMessageId', 'lastMessageId']) {
749
+ const messageId = record[key];
750
+ if (typeof messageId === 'string' && messageId.trim())
751
+ ids.add(messageId);
752
+ }
753
+ }
754
+ const peerInboxAggregate = metadata.peerInboxAggregate;
755
+ if (peerInboxAggregate && typeof peerInboxAggregate === 'object') {
756
+ const itemMessageIds = peerInboxAggregate.itemMessageIds;
757
+ if (Array.isArray(itemMessageIds)) {
758
+ for (const messageId of itemMessageIds) {
759
+ if (typeof messageId === 'string' && messageId.trim())
760
+ ids.add(messageId);
761
+ }
762
+ }
763
+ }
764
+ const sharedCollaboration = metadata.sharedCollaboration;
765
+ if (sharedCollaboration && typeof sharedCollaboration === 'object') {
766
+ const messageId = sharedCollaboration.originMessageId;
767
+ if (typeof messageId === 'string' && messageId.trim())
768
+ ids.add(messageId);
769
+ }
770
+ return [...ids];
771
+ }
772
+ function buildPlatformTraceForRun(db, runId) {
773
+ let assistantText = '';
774
+ let thinkingText = '';
775
+ const toolCalls = [];
776
+ const toolCallById = new Map();
777
+ let sawReplayEvent = false;
778
+ for (const row of iterateParsedNodeEvents(db, {
779
+ runId,
780
+ types: ['content.delta', 'thinking.delta', 'tool.call', 'tool.result'],
781
+ })) {
782
+ const parsed = row.event;
783
+ if (!parsed)
784
+ continue;
785
+ sawReplayEvent = true;
786
+ if (parsed.type === 'content.delta') {
787
+ assistantText += parsed.text;
788
+ continue;
789
+ }
790
+ if (parsed.type === 'thinking.delta') {
791
+ thinkingText += parsed.text;
792
+ continue;
793
+ }
794
+ if (parsed.type === 'tool.call') {
795
+ const toolCall = upsertPlatformToolCall(toolCallById, toolCalls, parsed.toolCallId, {
796
+ name: parsed.name || 'tool',
797
+ });
798
+ toolCall.name = parsed.name || toolCall.name;
799
+ toolCall.inputText = formatPlatformEventValue(parsed.input) ?? toolCall.inputText;
800
+ toolCall.startedAt = parsed.startedAt ?? toolCall.startedAt ?? row.createdAt;
801
+ continue;
802
+ }
803
+ if (parsed.type === 'tool.result') {
804
+ const toolCall = upsertPlatformToolCall(toolCallById, toolCalls, parsed.toolCallId, {
805
+ name: 'tool',
806
+ });
807
+ toolCall.outputText = parsed.output;
808
+ toolCall.error = Boolean(parsed.error);
809
+ toolCall.status = parsed.status ?? (parsed.error ? 'failed' : 'completed');
810
+ toolCall.endedAt = parsed.endedAt ?? toolCall.endedAt ?? row.createdAt;
811
+ }
812
+ }
813
+ if (!sawReplayEvent)
814
+ return undefined;
815
+ if (!assistantText && !thinkingText && toolCalls.length === 0)
816
+ return undefined;
817
+ return {
818
+ assistantText: assistantText || undefined,
819
+ thinkingText: thinkingText || undefined,
820
+ toolCalls,
821
+ };
822
+ }
823
+ function upsertPlatformToolCall(toolCallById, toolCalls, toolCallId, seed) {
824
+ const existing = toolCallById.get(toolCallId);
825
+ if (existing)
826
+ return existing;
827
+ const created = {
828
+ callId: toolCallId,
829
+ name: seed.name,
830
+ };
831
+ toolCallById.set(toolCallId, created);
832
+ toolCalls.push(created);
833
+ return created;
834
+ }
835
+ function formatPlatformEventValue(value) {
836
+ if (value == null)
837
+ return undefined;
838
+ if (typeof value === 'string')
839
+ return value;
840
+ try {
841
+ return JSON.stringify(value, null, 2);
842
+ }
843
+ catch {
844
+ return String(value);
845
+ }
846
+ }
847
+ function joinDebugText(...parts) {
848
+ const visibleParts = parts
849
+ .map((part) => part?.trim())
850
+ .filter((part) => Boolean(part));
851
+ return visibleParts.length > 0 ? visibleParts.join('\n\n') : undefined;
852
+ }
853
+ function extractInputBlocks(payload) {
854
+ const content = Array.isArray(payload.content) ? payload.content : [];
855
+ return content.flatMap((block) => {
856
+ if (!block || typeof block !== 'object')
857
+ return [];
858
+ const typed = block;
859
+ if (typed.type !== 'input_text')
860
+ return [];
861
+ const text = stringValue(typed.text);
862
+ return text ? [text] : [];
863
+ });
864
+ }
865
+ function extractAssistantOutputs(payload) {
866
+ const content = Array.isArray(payload.content) ? payload.content : [];
867
+ return content.flatMap((block) => {
868
+ if (!block || typeof block !== 'object')
869
+ return [];
870
+ const typed = block;
871
+ if (typed.type !== 'output_text')
872
+ return [];
873
+ const text = stringValue(typed.text);
874
+ return text ? [text] : [];
875
+ });
876
+ }
877
+ function extractMessageText(payload) {
878
+ const blocks = extractInputBlocks(payload);
879
+ if (blocks.length > 0)
880
+ return blocks.join('\n\n');
881
+ const assistantOutputs = extractAssistantOutputs(payload);
882
+ if (assistantOutputs.length > 0)
883
+ return assistantOutputs.join('\n\n');
884
+ return undefined;
885
+ }
886
+ export function parseReplyTarget(text) {
887
+ const match = /\[(?:Current conversation target|Handoff metadata)\][\s\S]*?reply_target:\s*([^\n]+)/.exec(text);
888
+ return match?.[1]?.trim() || undefined;
889
+ }
890
+ export function parseTriggerTarget(text) {
891
+ const match = /\[Triggered message metadata\][\s\S]*?target:\s*([^\n]+)/.exec(text);
892
+ return match?.[1]?.trim() || undefined;
893
+ }
894
+ function parseTokenUsage(payload) {
895
+ const info = payload.info;
896
+ const total = info?.total_token_usage;
897
+ const last = info?.last_token_usage;
898
+ const contextWindow = numberValue(info?.model_context_window);
899
+ if (!total && !last && contextWindow === undefined)
900
+ return undefined;
901
+ return {
902
+ currentInputTokens: numberValue(last?.input_tokens) ?? numberValue(total?.input_tokens),
903
+ inputTokens: numberValue(total?.input_tokens),
904
+ cachedInputTokens: numberValue(total?.cached_input_tokens),
905
+ currentCachedInputTokens: numberValue(last?.cached_input_tokens) ?? numberValue(total?.cached_input_tokens),
906
+ outputTokens: numberValue(total?.output_tokens),
907
+ reasoningOutputTokens: numberValue(total?.reasoning_output_tokens),
908
+ totalTokens: numberValue(total?.total_tokens),
909
+ modelContextWindow: contextWindow,
910
+ };
911
+ }
912
+ function applyCodexContextWindowFallback(rollout, getCodexContextWindowByModel) {
913
+ const fallbackWindow = getCodexContextWindowByModel(rollout.model);
914
+ if (fallbackWindow == null)
915
+ return;
916
+ for (const turn of rollout.turns) {
917
+ if (!turn.tokenUsage)
918
+ continue;
919
+ turn.tokenUsage.modelContextWindow = turn.tokenUsage.modelContextWindow ?? fallbackWindow;
920
+ }
921
+ }
922
+ function defaultGetCodexContextWindowByModel(model) {
923
+ if (!model)
924
+ return undefined;
925
+ const windows = loadCodexModelWindows();
926
+ return windows.get(model);
927
+ }
928
+ function loadCodexModelWindows() {
929
+ if (cachedCodexModelWindows)
930
+ return cachedCodexModelWindows;
931
+ const windows = new Map();
932
+ try {
933
+ const raw = readFileSync('/root/.codex/models_cache.json', 'utf8');
934
+ const parsed = JSON.parse(raw);
935
+ for (const model of parsed.models ?? []) {
936
+ const slug = stringValue(model.slug);
937
+ const contextWindow = numberValue(model.context_window);
938
+ if (!slug || contextWindow == null)
939
+ continue;
940
+ const effectivePercent = numberValue(model.effective_context_window_percent);
941
+ const effectiveWindow = effectivePercent != null
942
+ ? Math.round(contextWindow * (effectivePercent / 100))
943
+ : contextWindow;
944
+ windows.set(slug, effectiveWindow);
945
+ }
946
+ }
947
+ catch {
948
+ // Best-effort fallback only.
949
+ }
950
+ cachedCodexModelWindows = windows;
951
+ return windows;
952
+ }
953
+ function stringValue(value) {
954
+ return typeof value === 'string' && value.trim() ? value : undefined;
955
+ }
956
+ function numberValue(value) {
957
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
958
+ }
959
+ function isTranscriptTimeoutError(message) {
960
+ return message.toLowerCase().includes('timed out');
961
+ }