oh-my-knowledge 0.51.2 → 0.52.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/README.md +27 -6
- package/README.zh.md +27 -6
- package/dist/assets/agent-skills/omk/SKILL.md +8 -8
- package/dist/observability/codex-conversation-index.d.ts +88 -0
- package/dist/observability/codex-conversation-index.js +566 -0
- package/dist/observability/codex-protocol.d.ts +12 -0
- package/dist/observability/codex-protocol.js +78 -0
- package/dist/observability/codex-tool-status.d.ts +16 -0
- package/dist/observability/codex-tool-status.js +114 -0
- package/dist/observability/codex-trace-adapter.js +342 -119
- package/dist/observability/conversation-catalog.d.ts +38 -0
- package/dist/observability/conversation-catalog.js +573 -0
- package/dist/observability/conversation-index-process.d.ts +1 -0
- package/dist/observability/conversation-index-process.js +31 -0
- package/dist/observability/conversation-view-model.d.ts +5 -0
- package/dist/observability/conversation-view-model.js +100 -0
- package/dist/observability/experience.d.ts +3 -1
- package/dist/observability/experience.js +248 -7
- package/dist/observability/inbox.js +40 -2
- package/dist/observability/knowledge-debugger.d.ts +4 -0
- package/dist/observability/knowledge-debugger.js +364 -0
- package/dist/observability/polling-subscription-hub.d.ts +27 -0
- package/dist/observability/polling-subscription-hub.js +149 -0
- package/dist/observability/source-record-archive.d.ts +13 -0
- package/dist/observability/source-record-archive.js +324 -0
- package/dist/observability/task-window.d.ts +8 -0
- package/dist/observability/task-window.js +36 -0
- package/dist/observability/trace-ir.d.ts +66 -1
- package/dist/observability/trace-source.d.ts +1 -0
- package/dist/observability/trace-source.js +12 -5
- package/dist/observability/turn-index.d.ts +3 -0
- package/dist/observability/turn-index.js +251 -0
- package/dist/renderer/conversation-renderer.d.ts +9 -0
- package/dist/renderer/conversation-renderer.js +410 -0
- package/dist/renderer/icons.js +1 -0
- package/dist/renderer/inline-markdown.d.ts +6 -0
- package/dist/renderer/inline-markdown.js +158 -0
- package/dist/renderer/knowledge-debugger-renderer.d.ts +9 -0
- package/dist/renderer/knowledge-debugger-renderer.js +2280 -0
- package/dist/renderer/observation-inbox-renderer.js +9 -0
- package/dist/renderer/skill-list-renderer.js +2 -1
- package/dist/renderer/trajectory-live.d.ts +42 -0
- package/dist/renderer/trajectory-live.js +258 -0
- package/dist/renderer/trajectory-routing.d.ts +60 -0
- package/dist/renderer/trajectory-routing.js +351 -0
- package/dist/server/report-server.d.ts +4 -1
- package/dist/server/report-server.js +274 -6
- package/dist/types/observability.d.ts +225 -1
- package/package.json +8 -5
|
@@ -1,38 +1,11 @@
|
|
|
1
1
|
/** Codex rollout JSONL -> source-neutral Trace IR. */
|
|
2
2
|
import { basename } from 'node:path';
|
|
3
|
-
import { isToolResultFailureText } from './text-signals.js';
|
|
4
3
|
import { correlateTraceToolEvents, createTraceId, normalizeTraceTimestamp, traceTimestampBounds, } from './trace-ir.js';
|
|
5
4
|
import { nonNegativeMetric, optionalTokenCount, splitInclusiveInputTokens, tokenCount, } from '../shared/token-usage.js';
|
|
6
5
|
import { normalizeToolIdentity } from '../shared/tool-identity.js';
|
|
7
6
|
import { extractCodexExecCommands } from './codex-exec-command.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
'reasoning',
|
|
11
|
-
'tool_search_call',
|
|
12
|
-
'tool_search_output',
|
|
13
|
-
'web_search_call',
|
|
14
|
-
'image_generation_call',
|
|
15
|
-
'function_call',
|
|
16
|
-
'custom_tool_call',
|
|
17
|
-
'function_call_output',
|
|
18
|
-
'custom_tool_call_output',
|
|
19
|
-
]);
|
|
20
|
-
const CODEX_EVENT_MESSAGE_TYPES = new Set([
|
|
21
|
-
'token_count',
|
|
22
|
-
'task_started',
|
|
23
|
-
'task_complete',
|
|
24
|
-
'turn_aborted',
|
|
25
|
-
'user_message',
|
|
26
|
-
'agent_message',
|
|
27
|
-
'mcp_tool_call_end',
|
|
28
|
-
'patch_apply_end',
|
|
29
|
-
'agent_reasoning',
|
|
30
|
-
'thread_settings_applied',
|
|
31
|
-
'web_search_end',
|
|
32
|
-
'context_compacted',
|
|
33
|
-
'image_generation_end',
|
|
34
|
-
'thread_goal_updated',
|
|
35
|
-
]);
|
|
7
|
+
import { codexUserDisplayText, codexUserMessageOrigin, isCodexEventMessageType, isCodexRecordConsumedWithoutDirectEvent, isCodexResponseItemType, } from './codex-protocol.js';
|
|
8
|
+
import { codexRuntimeToolOutcome, codexToolOutputOutcome, codexToolStatusFromValue, } from './codex-tool-status.js';
|
|
36
9
|
export function isCodexJsonl(records) {
|
|
37
10
|
return records.some((record) => {
|
|
38
11
|
const raw = asCodexRecord(record);
|
|
@@ -48,11 +21,10 @@ export function isCodexJsonl(records) {
|
|
|
48
21
|
|| stringValue(raw.payload.model) !== undefined;
|
|
49
22
|
}
|
|
50
23
|
if (raw.type === 'response_item') {
|
|
51
|
-
return
|
|
24
|
+
return isCodexResponseItemType(payloadType);
|
|
52
25
|
}
|
|
53
26
|
return raw.type === 'event_msg'
|
|
54
|
-
&& payloadType
|
|
55
|
-
&& CODEX_EVENT_MESSAGE_TYPES.has(payloadType);
|
|
27
|
+
&& isCodexEventMessageType(payloadType);
|
|
56
28
|
});
|
|
57
29
|
}
|
|
58
30
|
export function isCodexGuardianRollout(records) {
|
|
@@ -118,8 +90,10 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
118
90
|
const representedPatchCalls = new Set();
|
|
119
91
|
const representedPatchResults = new Set();
|
|
120
92
|
const duplicateEventMessageIndexes = indexDuplicateEventMessages(rawRecords);
|
|
93
|
+
const duplicateAgentReasoningIndexes = indexDuplicateAgentReasoningMessages(rawRecords);
|
|
121
94
|
let activeModel;
|
|
122
95
|
let activeTurnId;
|
|
96
|
+
let emittedSessionContext = false;
|
|
123
97
|
let previousTotalUsageFingerprint;
|
|
124
98
|
rawRecords.forEach((value, sourceIndex) => {
|
|
125
99
|
const record = asCodexRecord(value);
|
|
@@ -136,14 +110,109 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
136
110
|
timestamp,
|
|
137
111
|
turnId: activeTurnId,
|
|
138
112
|
};
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
113
|
+
if (record.type === 'session_meta') {
|
|
114
|
+
// Desktop may repeat session_meta during a long rollout. One normalized
|
|
115
|
+
// context event is enough; source records retain every native copy.
|
|
116
|
+
if (emittedSessionContext)
|
|
117
|
+
return;
|
|
118
|
+
emittedSessionContext = true;
|
|
119
|
+
const runtimeName = stringValue(payload.originator);
|
|
120
|
+
const runtimeVersion = stringValue(payload.cli_version);
|
|
121
|
+
const availableTools = codexAvailableTools(payload.dynamic_tools);
|
|
122
|
+
const instructions = codexPlaintext(payload.base_instructions);
|
|
123
|
+
const memoryMode = stringValue(payload.memory_mode);
|
|
124
|
+
const historyMode = stringValue(payload.history_mode);
|
|
125
|
+
const contextWindowId = nestedString(payload.context_window, 'window_id');
|
|
126
|
+
const modelProvider = stringValue(payload.model_provider);
|
|
127
|
+
const multiAgentVersion = stringValue(payload.multi_agent_version);
|
|
128
|
+
if (runtimeName
|
|
129
|
+
|| runtimeVersion
|
|
130
|
+
|| availableTools
|
|
131
|
+
|| instructions
|
|
132
|
+
|| memoryMode
|
|
133
|
+
|| historyMode
|
|
134
|
+
|| contextWindowId
|
|
135
|
+
|| modelProvider
|
|
136
|
+
|| multiAgentVersion) {
|
|
137
|
+
events.push({
|
|
138
|
+
...base,
|
|
139
|
+
eventKind: 'runtime_context',
|
|
140
|
+
eventId: eventId('session-context'),
|
|
141
|
+
runtimeKind: 'session_context',
|
|
142
|
+
runtimeName,
|
|
143
|
+
runtimeVersion,
|
|
144
|
+
modelProvider,
|
|
145
|
+
multiAgentVersion,
|
|
146
|
+
memoryMode,
|
|
147
|
+
historyMode,
|
|
148
|
+
contextWindowId,
|
|
149
|
+
availableTools,
|
|
150
|
+
instructions: instructions || undefined,
|
|
151
|
+
summary: codexSessionContextSummary({
|
|
152
|
+
runtimeName,
|
|
153
|
+
runtimeVersion,
|
|
154
|
+
memoryMode,
|
|
155
|
+
historyMode,
|
|
156
|
+
availableTools,
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
143
160
|
return;
|
|
161
|
+
}
|
|
144
162
|
if (record.type === 'turn_context') {
|
|
145
163
|
activeModel = stringValue(payload.model) ?? activeModel;
|
|
146
164
|
activeTurnId = stringValue(payload.turn_id) ?? activeTurnId;
|
|
165
|
+
events.push({
|
|
166
|
+
...base,
|
|
167
|
+
turnId: activeTurnId,
|
|
168
|
+
eventKind: 'runtime_context',
|
|
169
|
+
eventId: eventId('runtime-context'),
|
|
170
|
+
runtimeKind: 'execution_context',
|
|
171
|
+
cwd: stringValue(payload.cwd),
|
|
172
|
+
workspaceRoots: stringArray(payload.workspace_roots),
|
|
173
|
+
currentDate: stringValue(payload.current_date),
|
|
174
|
+
timezone: stringValue(payload.timezone),
|
|
175
|
+
model: activeModel,
|
|
176
|
+
reasoningEffort: stringValue(payload.effort),
|
|
177
|
+
personality: stringValue(payload.personality),
|
|
178
|
+
approvalPolicy: stringValue(payload.approval_policy),
|
|
179
|
+
approvalReviewer: stringValue(payload.approvals_reviewer),
|
|
180
|
+
permissionProfile: nestedString(payload.permission_profile, 'type'),
|
|
181
|
+
sandboxMode: nestedString(payload.sandbox_policy, 'type'),
|
|
182
|
+
collaborationMode: stringValue(payload.collaboration_mode)
|
|
183
|
+
?? nestedString(payload.collaboration_mode, 'mode'),
|
|
184
|
+
realtimeActive: booleanValue(payload.realtime_active),
|
|
185
|
+
multiAgentMode: stringValue(payload.multi_agent_mode),
|
|
186
|
+
multiAgentVersion: stringValue(payload.multi_agent_version),
|
|
187
|
+
instructions: stringValue(payload.user_instructions),
|
|
188
|
+
summary: stringValue(payload.summary),
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (record.type === 'response_item' && payloadType === 'reasoning') {
|
|
193
|
+
const reasoning = codexReasoningPlaintext(payload);
|
|
194
|
+
if (reasoning) {
|
|
195
|
+
events.push({
|
|
196
|
+
...base,
|
|
197
|
+
eventKind: 'model_activity',
|
|
198
|
+
eventId: eventId('model-activity'),
|
|
199
|
+
activityKind: 'reasoning',
|
|
200
|
+
contentVisibility: 'plaintext',
|
|
201
|
+
text: reasoning.text,
|
|
202
|
+
contentSource: reasoning.contentSource,
|
|
203
|
+
model: activeModel,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
else if (stringValue(payload.encrypted_content)) {
|
|
207
|
+
events.push({
|
|
208
|
+
...base,
|
|
209
|
+
eventKind: 'model_activity',
|
|
210
|
+
eventId: eventId('model-activity'),
|
|
211
|
+
activityKind: 'reasoning',
|
|
212
|
+
contentVisibility: 'opaque',
|
|
213
|
+
model: activeModel,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
147
216
|
return;
|
|
148
217
|
}
|
|
149
218
|
if (record.type === 'response_item' && payloadType === 'message') {
|
|
@@ -161,11 +230,30 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
161
230
|
? codexUserMessageOrigin(text)
|
|
162
231
|
: normalizedRole === 'system' ? 'runtime' : 'synthetic',
|
|
163
232
|
text,
|
|
233
|
+
displayText: normalizedRole === 'user' ? codexUserDisplayText(text) : undefined,
|
|
164
234
|
model: normalizedRole === 'assistant' ? activeModel : undefined,
|
|
165
235
|
});
|
|
166
236
|
}
|
|
167
237
|
return;
|
|
168
238
|
}
|
|
239
|
+
if (record.type === 'response_item' && payloadType === 'agent_message') {
|
|
240
|
+
const text = codexContentText(payload.content);
|
|
241
|
+
const passthrough = isObject(payload.internal_chat_message_metadata_passthrough)
|
|
242
|
+
? payload.internal_chat_message_metadata_passthrough
|
|
243
|
+
: {};
|
|
244
|
+
events.push({
|
|
245
|
+
...base,
|
|
246
|
+
sourceEventId: stringValue(payload.id),
|
|
247
|
+
turnId: stringValue(passthrough.turn_id) ?? activeTurnId,
|
|
248
|
+
eventKind: 'agent_activity',
|
|
249
|
+
eventId: eventId('agent-communication'),
|
|
250
|
+
activityKind: 'communication',
|
|
251
|
+
author: stringValue(payload.author),
|
|
252
|
+
recipient: stringValue(payload.recipient),
|
|
253
|
+
text: text || undefined,
|
|
254
|
+
});
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
169
257
|
if (record.type === 'response_item' && payloadType === 'tool_search_call') {
|
|
170
258
|
const callId = stringValue(payload.call_id) ?? stringValue(payload.id) ?? `codex-tool-search-${sourceIndex}`;
|
|
171
259
|
events.push(toolCallEvent(eventId('tool-call'), base, callId, normalizeToolIdentity({ sourceName: 'tool_search' }), parseToolInput(payload.arguments), activeModel));
|
|
@@ -178,7 +266,7 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
178
266
|
execution: stringValue(payload.execution),
|
|
179
267
|
tools: summarizeDiscoveredTools(payload.tools),
|
|
180
268
|
});
|
|
181
|
-
events.push(toolResultEvent(eventId('tool-result'), base, callId, output,
|
|
269
|
+
events.push(toolResultEvent(eventId('tool-result'), base, callId, output, codexToolStatusFromValue(payload.status), stringValue(payload.status) ? 'runtime' : 'unknown'));
|
|
182
270
|
return;
|
|
183
271
|
}
|
|
184
272
|
if (record.type === 'response_item' && payloadType === 'web_search_call') {
|
|
@@ -187,7 +275,7 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
187
275
|
const resultOccurrence = takeOccurrence(externalResultOccurrences, callId);
|
|
188
276
|
const completed = externalEnds.byOccurrence.has(mcpCallOccurrenceKey(callId, resultOccurrence));
|
|
189
277
|
const payloadStatus = stringValue(payload.status);
|
|
190
|
-
const explicitStatus =
|
|
278
|
+
const explicitStatus = codexToolStatusFromValue(payloadStatus);
|
|
191
279
|
events.push(toolCallEvent(eventId('tool-call'), base, callId, normalizeToolIdentity({ sourceName: 'web_search' }), isObject(payload.action) ? payload.action : {}, activeModel));
|
|
192
280
|
events.push(toolResultEvent(eventId('tool-result'), base, callId, JSON.stringify({ status: stringValue(payload.status) }), explicitStatus !== 'unknown' ? explicitStatus : completed ? 'success' : 'unknown', explicitStatus !== 'unknown' ? 'runtime' : completed ? 'inferred' : 'unknown'));
|
|
193
281
|
return;
|
|
@@ -199,7 +287,7 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
199
287
|
const resultOccurrence = takeOccurrence(externalResultOccurrences, callId);
|
|
200
288
|
const completed = externalEnds.byOccurrence.has(mcpCallOccurrenceKey(callId, resultOccurrence));
|
|
201
289
|
const payloadStatus = stringValue(payload.status);
|
|
202
|
-
const explicitStatus =
|
|
290
|
+
const explicitStatus = codexToolStatusFromValue(payloadStatus);
|
|
203
291
|
const inferredSuccess = result.length > 0 || completed;
|
|
204
292
|
events.push(toolCallEvent(eventId('tool-call'), base, callId, normalizeToolIdentity({ sourceName: 'image_generation' }), { prompt: stringValue(payload.revised_prompt) }, activeModel));
|
|
205
293
|
events.push(toolResultEvent(eventId('tool-result'), base, callId, JSON.stringify({ status: stringValue(payload.status), resultBytes: result.length }), explicitStatus !== 'unknown' ? explicitStatus : inferredSuccess ? 'success' : 'unknown', explicitStatus !== 'unknown' ? 'runtime' : inferredSuccess ? 'inferred' : 'unknown'));
|
|
@@ -237,16 +325,15 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
237
325
|
if (patchEnd)
|
|
238
326
|
representedPatchResults.add(mcpEndKey);
|
|
239
327
|
const output = codexContentText(payload.output) || mcpEnd?.output || patchEnd?.output || '';
|
|
240
|
-
const runtimeOutcome =
|
|
328
|
+
const runtimeOutcome = codexRuntimeToolOutcome(mcpEnd ?? patchEnd);
|
|
241
329
|
const payloadStatus = stringValue(payload.status);
|
|
242
330
|
const hasRuntimeStatus = runtimeOutcome.present || payloadStatus !== undefined;
|
|
243
331
|
const explicitStatus = runtimeOutcome.present
|
|
244
332
|
? runtimeOutcome.status
|
|
245
|
-
:
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
const inferredSuccess = bridgeSuccess;
|
|
333
|
+
: codexToolStatusFromValue(payloadStatus);
|
|
334
|
+
const outputOutcome = codexToolOutputOutcome(output);
|
|
335
|
+
const inferredFailure = outputOutcome.status === 'failure';
|
|
336
|
+
const inferredSuccess = outputOutcome.status === 'success';
|
|
250
337
|
const completedExternalCall = externalEnds.byOccurrence.has(mcpCallOccurrenceKey(callId, externalOccurrence));
|
|
251
338
|
const inferredStatus = inferredFailure
|
|
252
339
|
? 'failure'
|
|
@@ -259,6 +346,12 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
259
346
|
if (record.type === 'event_msg' && payloadType === 'token_count') {
|
|
260
347
|
const info = isObject(payload.info) ? payload.info : {};
|
|
261
348
|
const usage = isObject(info.last_token_usage) ? info.last_token_usage : undefined;
|
|
349
|
+
// Codex also emits rate-limit-only snapshots under the token_count
|
|
350
|
+
// protocol name. They describe account capacity, not task token usage.
|
|
351
|
+
// Keep the source record for provenance without manufacturing a usage
|
|
352
|
+
// event or reporting a known protocol shape as unknown.
|
|
353
|
+
if (!usage && payload.info == null && isObject(payload.rate_limits))
|
|
354
|
+
return;
|
|
262
355
|
if (!isValidCodexTokenUsage(usage)) {
|
|
263
356
|
events.push({
|
|
264
357
|
...base,
|
|
@@ -325,6 +418,19 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
325
418
|
activeTurnId = undefined;
|
|
326
419
|
return;
|
|
327
420
|
}
|
|
421
|
+
if (record.type === 'event_msg' && payloadType === 'turn_interrupted') {
|
|
422
|
+
events.push({
|
|
423
|
+
...base,
|
|
424
|
+
turnId: stringValue(payload.turn_id) ?? activeTurnId,
|
|
425
|
+
eventKind: 'lifecycle',
|
|
426
|
+
eventId: eventId('lifecycle'),
|
|
427
|
+
phase: 'turn_interrupted',
|
|
428
|
+
reason: stringValue(payload.reason),
|
|
429
|
+
durationMs: nonNegativeMetric(payload.duration_ms),
|
|
430
|
+
});
|
|
431
|
+
activeTurnId = undefined;
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
328
434
|
if (record.type === 'event_msg' && payloadType === 'user_message') {
|
|
329
435
|
const text = stringValue(payload.message);
|
|
330
436
|
if (text) {
|
|
@@ -337,6 +443,7 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
337
443
|
role: 'user',
|
|
338
444
|
origin: codexUserMessageOrigin(text),
|
|
339
445
|
text,
|
|
446
|
+
displayText: codexUserDisplayText(text),
|
|
340
447
|
});
|
|
341
448
|
}
|
|
342
449
|
return;
|
|
@@ -358,6 +465,90 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
358
465
|
}
|
|
359
466
|
return;
|
|
360
467
|
}
|
|
468
|
+
if (record.type === 'event_msg' && payloadType === 'agent_reasoning') {
|
|
469
|
+
if (duplicateAgentReasoningIndexes.has(sourceIndex))
|
|
470
|
+
return;
|
|
471
|
+
const text = codexPlaintext(payload.text);
|
|
472
|
+
if (text) {
|
|
473
|
+
events.push({
|
|
474
|
+
...base,
|
|
475
|
+
eventKind: 'model_activity',
|
|
476
|
+
eventId: eventId('model-activity'),
|
|
477
|
+
activityKind: 'reasoning',
|
|
478
|
+
contentVisibility: 'plaintext',
|
|
479
|
+
text,
|
|
480
|
+
contentSource: 'text',
|
|
481
|
+
model: activeModel,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (record.type === 'event_msg' && payloadType === 'thread_settings_applied') {
|
|
487
|
+
const settings = isObject(payload.thread_settings) ? payload.thread_settings : payload;
|
|
488
|
+
activeModel = stringValue(settings.model) ?? activeModel;
|
|
489
|
+
events.push({
|
|
490
|
+
...base,
|
|
491
|
+
eventKind: 'runtime_context',
|
|
492
|
+
eventId: eventId('runtime-settings'),
|
|
493
|
+
runtimeKind: 'settings',
|
|
494
|
+
model: activeModel,
|
|
495
|
+
modelProvider: stringValue(settings.model_provider_id),
|
|
496
|
+
serviceTier: stringValue(settings.service_tier),
|
|
497
|
+
reasoningEffort: stringValue(settings.reasoning_effort),
|
|
498
|
+
reasoningSummary: stringValue(settings.reasoning_summary),
|
|
499
|
+
personality: stringValue(settings.personality),
|
|
500
|
+
cwd: stringValue(settings.cwd),
|
|
501
|
+
approvalPolicy: stringValue(settings.approval_policy),
|
|
502
|
+
approvalReviewer: stringValue(settings.approvals_reviewer),
|
|
503
|
+
permissionProfile: nestedString(settings.permission_profile, 'type'),
|
|
504
|
+
sandboxMode: nestedString(settings.sandbox_policy, 'type'),
|
|
505
|
+
collaborationMode: stringValue(settings.collaboration_mode)
|
|
506
|
+
?? nestedString(settings.collaboration_mode, 'mode'),
|
|
507
|
+
summary: stringValue(settings.summary),
|
|
508
|
+
});
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (record.type === 'event_msg' && payloadType === 'thread_goal_updated') {
|
|
512
|
+
const goal = isObject(payload.goal) ? payload.goal : {};
|
|
513
|
+
events.push({
|
|
514
|
+
...base,
|
|
515
|
+
eventKind: 'runtime_context',
|
|
516
|
+
eventId: eventId('runtime-goal'),
|
|
517
|
+
runtimeKind: 'goal',
|
|
518
|
+
goal: stringValue(payload.goal)
|
|
519
|
+
?? stringValue(goal.objective)
|
|
520
|
+
?? stringValue(goal.text),
|
|
521
|
+
goalStatus: stringValue(goal.status),
|
|
522
|
+
summary: stringValue(payload.summary),
|
|
523
|
+
});
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
if (record.type === 'compacted' || (record.type === 'event_msg' && payloadType === 'context_compacted')) {
|
|
527
|
+
const replacementHistory = Array.isArray(payload.replacement_history)
|
|
528
|
+
? payload.replacement_history
|
|
529
|
+
: undefined;
|
|
530
|
+
events.push({
|
|
531
|
+
...base,
|
|
532
|
+
eventKind: 'context_compaction',
|
|
533
|
+
eventId: eventId('context-compaction'),
|
|
534
|
+
summary: stringValue(payload.summary) ?? stringValue(payload.message),
|
|
535
|
+
replacementItemCount: replacementHistory?.length,
|
|
536
|
+
});
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (record.type === 'event_msg' && payloadType === 'sub_agent_activity') {
|
|
540
|
+
events.push({
|
|
541
|
+
...base,
|
|
542
|
+
sourceEventId: stringValue(payload.event_id),
|
|
543
|
+
eventKind: 'agent_activity',
|
|
544
|
+
eventId: eventId('agent-status'),
|
|
545
|
+
activityKind: 'status',
|
|
546
|
+
agentId: stringValue(payload.agent_thread_id),
|
|
547
|
+
agentPath: stringValue(payload.agent_path),
|
|
548
|
+
activity: stringValue(payload.kind),
|
|
549
|
+
});
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
361
552
|
if (payloadType === 'mcp_tool_call_end') {
|
|
362
553
|
const end = mcpEnds.bySourceIndex.get(sourceIndex);
|
|
363
554
|
if (end) {
|
|
@@ -374,7 +565,7 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
374
565
|
}
|
|
375
566
|
return;
|
|
376
567
|
}
|
|
377
|
-
if (
|
|
568
|
+
if (isCodexRecordConsumedWithoutDirectEvent(record.type, payloadType))
|
|
378
569
|
return;
|
|
379
570
|
events.push({
|
|
380
571
|
...base,
|
|
@@ -385,7 +576,7 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
385
576
|
});
|
|
386
577
|
for (const end of mcpEnds.ordered) {
|
|
387
578
|
const endKey = mcpCallOccurrenceKey(end.callId, end.occurrence);
|
|
388
|
-
const outcome =
|
|
579
|
+
const outcome = codexRuntimeToolOutcome(end);
|
|
389
580
|
if (!representedMcpCalls.has(endKey)) {
|
|
390
581
|
events.push({
|
|
391
582
|
eventKind: 'tool_call',
|
|
@@ -419,7 +610,7 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
419
610
|
}
|
|
420
611
|
for (const end of patchEnds.ordered) {
|
|
421
612
|
const endKey = mcpCallOccurrenceKey(end.callId, end.occurrence);
|
|
422
|
-
const outcome =
|
|
613
|
+
const outcome = codexRuntimeToolOutcome(end);
|
|
423
614
|
if (!representedPatchCalls.has(endKey)) {
|
|
424
615
|
events.push({
|
|
425
616
|
eventKind: 'tool_call',
|
|
@@ -452,26 +643,6 @@ function convertCodexRecords(rawRecords, runId) {
|
|
|
452
643
|
events.sort((a, b) => a.sourceIndex - b.sourceIndex);
|
|
453
644
|
return events;
|
|
454
645
|
}
|
|
455
|
-
/**
|
|
456
|
-
* Codex persists protocol and UI state alongside behavioral evidence. These
|
|
457
|
-
* records are understood by this adapter but intentionally do not become Trace
|
|
458
|
-
* IR events. Keeping them out of `unknown` means ingestion warnings remain a
|
|
459
|
-
* forward-compatibility signal instead of firing on every normal rollout.
|
|
460
|
-
*/
|
|
461
|
-
function isKnownNonMeasurementCodexRecord(recordType, payloadType) {
|
|
462
|
-
if (recordType === 'compacted' || recordType === 'world_state')
|
|
463
|
-
return true;
|
|
464
|
-
if (recordType === 'response_item' && payloadType === 'reasoning')
|
|
465
|
-
return true;
|
|
466
|
-
if (recordType !== 'event_msg')
|
|
467
|
-
return false;
|
|
468
|
-
return payloadType === 'agent_reasoning'
|
|
469
|
-
|| payloadType === 'thread_settings_applied'
|
|
470
|
-
|| payloadType === 'web_search_end'
|
|
471
|
-
|| payloadType === 'context_compacted'
|
|
472
|
-
|| payloadType === 'image_generation_end'
|
|
473
|
-
|| payloadType === 'thread_goal_updated';
|
|
474
|
-
}
|
|
475
646
|
function mcpToolRefFromEnd(end) {
|
|
476
647
|
return normalizeToolIdentity({
|
|
477
648
|
sourceName: 'mcp_tool_call',
|
|
@@ -590,26 +761,6 @@ function takeOccurrence(counts, callId) {
|
|
|
590
761
|
function mcpCallOccurrenceKey(callId, occurrence) {
|
|
591
762
|
return `${callId}\u0000${occurrence}`;
|
|
592
763
|
}
|
|
593
|
-
function runtimeToolEndOutcome(end) {
|
|
594
|
-
if (!end)
|
|
595
|
-
return { status: 'unknown', present: false };
|
|
596
|
-
if (end.status !== undefined) {
|
|
597
|
-
const status = statusFromCodex(end.status);
|
|
598
|
-
if (status === 'cancelled')
|
|
599
|
-
return { status, present: true };
|
|
600
|
-
if (status === 'failure' || end.isError === true) {
|
|
601
|
-
return { status: 'failure', present: true };
|
|
602
|
-
}
|
|
603
|
-
if (status === 'success')
|
|
604
|
-
return { status, present: true };
|
|
605
|
-
return { status: 'unknown', present: true };
|
|
606
|
-
}
|
|
607
|
-
if (end.isError === true)
|
|
608
|
-
return { status: 'failure', present: true };
|
|
609
|
-
if (end.isError === false)
|
|
610
|
-
return { status: 'success', present: true };
|
|
611
|
-
return { status: 'unknown', present: false };
|
|
612
|
-
}
|
|
613
764
|
function normalizeCodexTool(sourceName, rawInput, mcpEnd, sourceNamespace) {
|
|
614
765
|
const input = parseToolInput(rawInput);
|
|
615
766
|
const sourceInput = stringValue(input.input);
|
|
@@ -654,14 +805,6 @@ function normalizeCodexTool(sourceName, rawInput, mcpEnd, sourceNamespace) {
|
|
|
654
805
|
}
|
|
655
806
|
return { tool, input };
|
|
656
807
|
}
|
|
657
|
-
function codexUserMessageOrigin(text) {
|
|
658
|
-
const trimmed = text.trimStart();
|
|
659
|
-
if (/^# AGENTS\.md instructions\b/i.test(trimmed))
|
|
660
|
-
return 'runtime';
|
|
661
|
-
if (/^<(?:app-context|environment_context|permissions instructions|collaboration_mode|apps_instructions|plugins_instructions|skills_instructions|recommended_plugins)>/i.test(trimmed))
|
|
662
|
-
return 'runtime';
|
|
663
|
-
return 'human';
|
|
664
|
-
}
|
|
665
808
|
function summarizeDiscoveredTools(value) {
|
|
666
809
|
if (!Array.isArray(value))
|
|
667
810
|
return [];
|
|
@@ -678,15 +821,29 @@ function summarizeDiscoveredTools(value) {
|
|
|
678
821
|
}];
|
|
679
822
|
});
|
|
680
823
|
}
|
|
681
|
-
function
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
824
|
+
function codexAvailableTools(value) {
|
|
825
|
+
if (!Array.isArray(value))
|
|
826
|
+
return undefined;
|
|
827
|
+
const names = value.flatMap((entry) => {
|
|
828
|
+
if (!isObject(entry))
|
|
829
|
+
return [];
|
|
830
|
+
const name = stringValue(entry.name);
|
|
831
|
+
if (!name)
|
|
832
|
+
return [];
|
|
833
|
+
const namespace = stringValue(entry.namespace);
|
|
834
|
+
return [namespace ? `${namespace}.${name}` : name];
|
|
835
|
+
});
|
|
836
|
+
return names.length > 0 ? Array.from(new Set(names)) : undefined;
|
|
837
|
+
}
|
|
838
|
+
function codexSessionContextSummary(input) {
|
|
839
|
+
const runtime = [input.runtimeName, input.runtimeVersion].filter(Boolean).join(' ');
|
|
840
|
+
const parts = [
|
|
841
|
+
runtime,
|
|
842
|
+
input.memoryMode ? `memory ${input.memoryMode}` : '',
|
|
843
|
+
input.historyMode ? `history ${input.historyMode}` : '',
|
|
844
|
+
input.availableTools ? `${input.availableTools.length} tools` : '',
|
|
845
|
+
].filter(Boolean);
|
|
846
|
+
return parts.length > 0 ? parts.join(' · ') : undefined;
|
|
690
847
|
}
|
|
691
848
|
function parseToolInput(value) {
|
|
692
849
|
if (isObject(value))
|
|
@@ -717,20 +874,36 @@ function codexContentText(value) {
|
|
|
717
874
|
return part.type === 'input_image' ? '[image]' : '';
|
|
718
875
|
}).filter(Boolean).join('\n');
|
|
719
876
|
}
|
|
720
|
-
function
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
877
|
+
function codexReasoningPlaintext(payload) {
|
|
878
|
+
const summary = codexPlaintext(payload.summary);
|
|
879
|
+
if (summary)
|
|
880
|
+
return { text: summary, contentSource: 'summary' };
|
|
881
|
+
const content = codexPlaintext(payload.content);
|
|
882
|
+
if (content)
|
|
883
|
+
return { text: content, contentSource: 'content' };
|
|
884
|
+
const text = codexPlaintext(payload.text ?? payload.reasoning_text ?? payload.reasoningText);
|
|
885
|
+
return text ? { text, contentSource: 'text' } : undefined;
|
|
886
|
+
}
|
|
887
|
+
function codexPlaintext(value, depth = 0) {
|
|
888
|
+
if (depth > 3)
|
|
889
|
+
return '';
|
|
890
|
+
if (typeof value === 'string')
|
|
891
|
+
return value.trim();
|
|
892
|
+
if (Array.isArray(value)) {
|
|
893
|
+
return value
|
|
894
|
+
.map((part) => codexPlaintext(part, depth + 1))
|
|
895
|
+
.filter(Boolean)
|
|
896
|
+
.join('\n\n')
|
|
897
|
+
.trim();
|
|
898
|
+
}
|
|
899
|
+
if (!isObject(value))
|
|
900
|
+
return '';
|
|
901
|
+
for (const key of ['text', 'input_text', 'output_text', 'summary_text']) {
|
|
902
|
+
const text = stringValue(value[key]);
|
|
903
|
+
if (text)
|
|
904
|
+
return text;
|
|
905
|
+
}
|
|
906
|
+
return codexPlaintext(value.content, depth + 1);
|
|
734
907
|
}
|
|
735
908
|
function codexSourceMetadata(records, metaPayload) {
|
|
736
909
|
const models = Array.from(new Set(records.flatMap((value) => {
|
|
@@ -846,6 +1019,47 @@ function indexDuplicateEventMessages(records) {
|
|
|
846
1019
|
});
|
|
847
1020
|
return duplicateIndexes;
|
|
848
1021
|
}
|
|
1022
|
+
function indexDuplicateAgentReasoningMessages(records) {
|
|
1023
|
+
const reasoningItems = records.flatMap((value, sourceIndex) => {
|
|
1024
|
+
const record = asCodexRecord(value);
|
|
1025
|
+
const payload = isObject(record?.payload) ? record.payload : {};
|
|
1026
|
+
if (record?.type !== 'response_item' || payload.type !== 'reasoning')
|
|
1027
|
+
return [];
|
|
1028
|
+
const reasoning = codexReasoningPlaintext(payload);
|
|
1029
|
+
if (!reasoning)
|
|
1030
|
+
return [];
|
|
1031
|
+
return [{
|
|
1032
|
+
sourceIndex,
|
|
1033
|
+
timestamp: normalizeTraceTimestamp(record.timestamp),
|
|
1034
|
+
text: normalizeReasoningMirrorText(reasoning.text),
|
|
1035
|
+
}];
|
|
1036
|
+
});
|
|
1037
|
+
const duplicateIndexes = new Set();
|
|
1038
|
+
records.forEach((value, sourceIndex) => {
|
|
1039
|
+
const record = asCodexRecord(value);
|
|
1040
|
+
const payload = isObject(record?.payload) ? record.payload : {};
|
|
1041
|
+
if (record?.type !== 'event_msg' || payload.type !== 'agent_reasoning')
|
|
1042
|
+
return;
|
|
1043
|
+
const text = normalizeReasoningMirrorText(codexPlaintext(payload.text));
|
|
1044
|
+
if (!text)
|
|
1045
|
+
return;
|
|
1046
|
+
const timestamp = normalizeTraceTimestamp(record.timestamp);
|
|
1047
|
+
const mirrored = reasoningItems.some((item) => {
|
|
1048
|
+
if (Math.abs(item.sourceIndex - sourceIndex) > 8)
|
|
1049
|
+
return false;
|
|
1050
|
+
const timeDistance = timestamp && item.timestamp
|
|
1051
|
+
? Math.abs(Date.parse(timestamp) - Date.parse(item.timestamp))
|
|
1052
|
+
: 0;
|
|
1053
|
+
return timeDistance <= 1_000 && item.text.includes(text);
|
|
1054
|
+
});
|
|
1055
|
+
if (mirrored)
|
|
1056
|
+
duplicateIndexes.add(sourceIndex);
|
|
1057
|
+
});
|
|
1058
|
+
return duplicateIndexes;
|
|
1059
|
+
}
|
|
1060
|
+
function normalizeReasoningMirrorText(value) {
|
|
1061
|
+
return value.replace(/\s+/g, ' ').trim();
|
|
1062
|
+
}
|
|
849
1063
|
function asCodexRecord(value) {
|
|
850
1064
|
return isObject(value) ? value : undefined;
|
|
851
1065
|
}
|
|
@@ -855,6 +1069,15 @@ function stringValue(value) {
|
|
|
855
1069
|
function booleanValue(value) {
|
|
856
1070
|
return typeof value === 'boolean' ? value : undefined;
|
|
857
1071
|
}
|
|
1072
|
+
function stringArray(value) {
|
|
1073
|
+
if (!Array.isArray(value))
|
|
1074
|
+
return undefined;
|
|
1075
|
+
const strings = value.filter((item) => typeof item === 'string' && item.trim().length > 0);
|
|
1076
|
+
return strings.length > 0 ? strings : undefined;
|
|
1077
|
+
}
|
|
1078
|
+
function nestedString(value, key) {
|
|
1079
|
+
return isObject(value) ? stringValue(value[key]) : undefined;
|
|
1080
|
+
}
|
|
858
1081
|
function isObject(value) {
|
|
859
1082
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
860
1083
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ConversationIndexViewModel, ConversationListItem, ExperienceTurnStatus, ObservationSourceRecordArchiveView, TaskTrajectorySession, TraceIngestionSummary } from '../types/index.js';
|
|
2
|
+
export interface ConversationTaskTrajectory {
|
|
3
|
+
revision: string;
|
|
4
|
+
status: ExperienceTurnStatus;
|
|
5
|
+
/** The source task is still the final unclosed turn and may append after a quiet period. */
|
|
6
|
+
liveObservable: boolean;
|
|
7
|
+
session: TaskTrajectorySession;
|
|
8
|
+
ingestion: TraceIngestionSummary;
|
|
9
|
+
sourceRecords: ObservationSourceRecordArchiveView;
|
|
10
|
+
}
|
|
11
|
+
export interface ConversationTaskTrajectoryObserver {
|
|
12
|
+
next(trajectory: ConversationTaskTrajectory): void;
|
|
13
|
+
complete?(): void;
|
|
14
|
+
error?(cause: unknown): void;
|
|
15
|
+
}
|
|
16
|
+
export interface ConversationTaskTrajectorySubscriptionOptions {
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
}
|
|
19
|
+
export interface ConversationCatalog {
|
|
20
|
+
listConversations(): Promise<ConversationIndexViewModel>;
|
|
21
|
+
getConversation(threadId: string): Promise<ConversationListItem | undefined>;
|
|
22
|
+
loadTaskTrajectory(threadId: string, turnId: string): Promise<ConversationTaskTrajectory | undefined>;
|
|
23
|
+
/** Optional live capability. Static catalogs do not need to implement it. */
|
|
24
|
+
observeTaskTrajectory?(threadId: string, turnId: string, observer: ConversationTaskTrajectoryObserver, options?: ConversationTaskTrajectorySubscriptionOptions): Promise<() => void>;
|
|
25
|
+
}
|
|
26
|
+
export interface CodexConversationCatalogOptions {
|
|
27
|
+
codexHome?: string;
|
|
28
|
+
cacheDir?: string;
|
|
29
|
+
/** Tests can keep synthetic fixtures in-process. */
|
|
30
|
+
useBackgroundProcess?: boolean;
|
|
31
|
+
backgroundProcessThresholdBytes?: number;
|
|
32
|
+
livePollIntervalMs?: number;
|
|
33
|
+
/** An unclosed final task is only live while its rollout is still recent. */
|
|
34
|
+
liveActivityWindowMs?: number;
|
|
35
|
+
now?: () => number;
|
|
36
|
+
}
|
|
37
|
+
export declare function createCodexConversationCatalog(options?: CodexConversationCatalogOptions): ConversationCatalog;
|
|
38
|
+
export declare function conversationSourceLabel(path: string): string;
|