oh-my-knowledge 0.48.0 → 0.49.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 +50 -18
- package/README.zh.md +55 -23
- package/dist/analysis/coverage-analyzer.d.ts +1 -0
- package/dist/analysis/coverage-analyzer.js +125 -62
- package/dist/analysis/failure-clusterer.js +2 -1
- package/dist/analysis/gap-analyzer.d.ts +2 -2
- package/dist/analysis/gap-analyzer.js +13 -3
- package/dist/analysis/hedging-classifier.d.ts +2 -2
- package/dist/analysis/hedging-classifier.js +3 -4
- package/dist/analysis/report-diagnostics.js +9 -7
- package/dist/analysis/sample-diagnostics.js +6 -6
- package/dist/artifact-graph/doctor.js +15 -7
- package/dist/assets/agent-skills/omk/SKILL.md +27 -7
- package/dist/assets/agent-skills/omk/references/commands.md +18 -17
- package/dist/authoring/evolver.d.ts +10 -6
- package/dist/authoring/evolver.js +496 -83
- package/dist/authoring/generator.d.ts +3 -3
- package/dist/authoring/generator.js +5 -10
- package/dist/authoring/sample-fixer.d.ts +8 -6
- package/dist/authoring/sample-fixer.js +76 -5
- package/dist/cli/commands/doctor.js +31 -14
- package/dist/cli/commands/eval/index.d.ts +3 -0
- package/dist/cli/commands/eval/index.js +163 -17
- package/dist/cli/commands/evolve.d.ts +4 -4
- package/dist/cli/commands/evolve.js +27 -13
- package/dist/cli/commands/init.js +16 -3
- package/dist/cli/commands/observe/inbox.js +28 -21
- package/dist/cli/commands/observe/index.js +20 -11
- package/dist/cli/commands/observe/ingest.d.ts +3 -0
- package/dist/cli/commands/observe/ingest.js +30 -2
- package/dist/cli/commands/sample.d.ts +6 -3
- package/dist/cli/commands/sample.js +72 -68
- package/dist/cli/lib/codex-model-hint.d.ts +9 -0
- package/dist/cli/lib/codex-model-hint.js +45 -0
- package/dist/cli/lib/generation-failure-hint.d.ts +2 -0
- package/dist/cli/lib/generation-failure-hint.js +61 -0
- package/dist/cli/lib/i18n-dict/common.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/common.js +4 -0
- package/dist/cli/lib/i18n-dict/gen.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/gen.js +38 -6
- package/dist/cli/lib/i18n-dict/help.js +6 -6
- package/dist/cli/lib/i18n-dict/init.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/init.js +13 -9
- package/dist/cli/lib/i18n-dict/run.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/run.js +34 -2
- package/dist/cli/lib/llm-failure-classifier.d.ts +2 -0
- package/dist/cli/lib/llm-failure-classifier.js +8 -0
- package/dist/cli/lib/parse-run-config.d.ts +6 -5
- package/dist/cli/lib/parse-run-config.js +16 -9
- package/dist/cli/lib/runtime-defaults.d.ts +21 -0
- package/dist/cli/lib/runtime-defaults.js +79 -0
- package/dist/diagnosis/observe-mapper.js +14 -15
- package/dist/diagnosis/observe-producer.js +3 -1
- package/dist/diagnosis/studio-projection.js +14 -7
- package/dist/diagnosis/types.d.ts +2 -0
- package/dist/diagnosis/types.js +12 -0
- package/dist/doctor/endpoint-rule.js +2 -1
- package/dist/eval-core/artifact-file-names.js +18 -1
- package/dist/eval-core/artifact-index.d.ts +7 -11
- package/dist/eval-core/artifact-index.js +139 -80
- package/dist/eval-core/cache.d.ts +12 -3
- package/dist/eval-core/cache.js +89 -29
- package/dist/eval-core/comparability.js +10 -6
- package/dist/eval-core/evaluation-execution.d.ts +2 -1
- package/dist/eval-core/evaluation-execution.js +122 -37
- package/dist/eval-core/evaluation-job.d.ts +4 -1
- package/dist/eval-core/evaluation-job.js +4 -1
- package/dist/eval-core/evaluation-reporting.d.ts +15 -13
- package/dist/eval-core/evaluation-reporting.js +54 -52
- package/dist/eval-core/execution-strategy.d.ts +2 -0
- package/dist/eval-core/execution-strategy.js +11 -9
- package/dist/eval-core/fact-checker.js +15 -7
- package/dist/eval-core/holdout.js +3 -2
- package/dist/eval-core/judge-independence.d.ts +2 -2
- package/dist/eval-core/mock-hook.cjs +23 -6
- package/dist/eval-core/mocks-runtime.js +30 -8
- package/dist/eval-core/report-document.d.ts +12 -0
- package/dist/eval-core/report-document.js +1151 -0
- package/dist/eval-core/report-extensions.d.ts +4 -0
- package/dist/eval-core/report-extensions.js +500 -0
- package/dist/eval-core/report-file-migration.js +7 -2
- package/dist/eval-core/resume-compatibility.d.ts +31 -0
- package/dist/eval-core/resume-compatibility.js +141 -0
- package/dist/eval-core/sample-fingerprint.d.ts +12 -0
- package/dist/eval-core/sample-fingerprint.js +193 -0
- package/dist/eval-core/schema.js +86 -31
- package/dist/eval-core/verdict.d.ts +8 -4
- package/dist/eval-core/verdict.js +24 -10
- package/dist/eval-workflows/batch-evaluation-workflow.d.ts +2 -1
- package/dist/eval-workflows/batch-evaluation-workflow.js +25 -12
- package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.d.ts +10 -5
- package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.js +58 -21
- package/dist/eval-workflows/evaluation-pipeline/report-finalize.js +3 -1
- package/dist/eval-workflows/evaluation-pipeline/run-state.d.ts +4 -1
- package/dist/eval-workflows/evaluation-pipeline/run-state.js +4 -1
- package/dist/eval-workflows/evaluation-pipeline/test-set-hash.d.ts +6 -5
- package/dist/eval-workflows/evaluation-pipeline/test-set-hash.js +17 -10
- package/dist/eval-workflows/evaluation-pipeline.js +12 -7
- package/dist/eval-workflows/run-evaluation.d.ts +9 -7
- package/dist/eval-workflows/run-evaluation.js +79 -51
- package/dist/executors/anthropic-api.js +65 -9
- package/dist/executors/claude-cli.js +16 -79
- package/dist/executors/claude-protocol.d.ts +28 -0
- package/dist/executors/claude-protocol.js +180 -0
- package/dist/executors/claude-sdk-trace.js +56 -28
- package/dist/executors/claude-sdk.d.ts +1 -0
- package/dist/executors/claude-sdk.js +39 -93
- package/dist/executors/codex-cli-trace.js +166 -31
- package/dist/executors/codex-cli.d.ts +6 -8
- package/dist/executors/codex-cli.js +49 -151
- package/dist/executors/codex-protocol.d.ts +24 -0
- package/dist/executors/codex-protocol.js +234 -0
- package/dist/executors/codex-sdk.js +68 -120
- package/dist/executors/gemini.js +88 -13
- package/dist/executors/index.d.ts +2 -3
- package/dist/executors/index.js +5 -3
- package/dist/executors/openai-api.js +70 -9
- package/dist/executors/runtime-fingerprint.js +88 -11
- package/dist/executors/script-command.d.ts +8 -0
- package/dist/executors/script-command.js +87 -0
- package/dist/executors/script.js +202 -29
- package/dist/executors/shared.d.ts +35 -3
- package/dist/executors/shared.js +113 -15
- package/dist/grading/assertions.d.ts +1 -1
- package/dist/grading/assertions.js +19 -9
- package/dist/grading/diagnostic.d.ts +9 -2
- package/dist/grading/diagnostic.js +25 -2
- package/dist/grading/index.js +10 -4
- package/dist/grading/judge.js +19 -6
- package/dist/grading/layered-scores.d.ts +2 -3
- package/dist/grading/layered-scores.js +2 -3
- package/dist/inputs/load-samples.d.ts +1 -2
- package/dist/inputs/load-samples.js +23 -1
- package/dist/inputs/mcp-resolver.js +6 -3
- package/dist/inputs/sample-document.d.ts +11 -0
- package/dist/inputs/sample-document.js +96 -0
- package/dist/managed/evidence.d.ts +1 -0
- package/dist/managed/evidence.js +1 -1
- package/dist/managed/store.js +200 -91
- package/dist/observability/codex-trace-adapter.d.ts +5 -0
- package/dist/observability/codex-trace-adapter.js +850 -0
- package/dist/observability/experience.d.ts +32 -6
- package/dist/observability/experience.js +2695 -459
- package/dist/observability/feedback-matchers.js +16 -1
- package/dist/observability/inbox-view-model.d.ts +1 -1
- package/dist/observability/inbox-view-model.js +19 -14
- package/dist/observability/inbox.d.ts +7 -1
- package/dist/observability/inbox.js +632 -124
- package/dist/observability/problem-patterns.js +2 -0
- package/dist/observability/review-state.d.ts +6 -0
- package/dist/observability/review-state.js +235 -63
- package/dist/observability/skill-chain-advisories.js +1 -1
- package/dist/observability/skill-chain.js +17 -4
- package/dist/observability/skill-health-analyzer.d.ts +32 -7
- package/dist/observability/skill-health-analyzer.js +194 -121
- package/dist/observability/skill-health-report.d.ts +10 -0
- package/dist/observability/skill-health-report.js +620 -0
- package/dist/observability/soft-standards/constants.d.ts +0 -1
- package/dist/observability/soft-standards/constants.js +0 -1
- package/dist/observability/soft-standards/index.d.ts +1 -1
- package/dist/observability/soft-standards/index.js +1 -1
- package/dist/observability/soft-standards/llm-extractor.js +8 -10
- package/dist/observability/soft-standards/skill-standards-store.d.ts +2 -1
- package/dist/observability/soft-standards/skill-standards-store.js +59 -18
- package/dist/observability/soft-standards/types.d.ts +2 -2
- package/dist/observability/trace-adapter.d.ts +12 -7
- package/dist/observability/trace-adapter.js +11 -9
- package/dist/observability/trace-attribution.d.ts +13 -5
- package/dist/observability/trace-attribution.js +315 -21
- package/dist/observability/trace-ingestion.d.ts +9 -0
- package/dist/observability/trace-ingestion.js +80 -0
- package/dist/observability/trace-ir.d.ts +113 -0
- package/dist/observability/trace-ir.js +87 -0
- package/dist/observability/trace-segmenter.d.ts +19 -6
- package/dist/observability/trace-segmenter.js +377 -196
- package/dist/observability/trace-session-index.d.ts +19 -0
- package/dist/observability/trace-session-index.js +68 -0
- package/dist/observability/trace-source.d.ts +12 -4
- package/dist/observability/trace-source.js +939 -215
- package/dist/renderer/html-renderer.js +37 -6
- package/dist/renderer/icons.js +3 -0
- package/dist/renderer/observation-inbox-renderer.js +208 -90
- package/dist/renderer/skill-detail-renderer.js +452 -109
- package/dist/renderer/skill-health-renderer.js +69 -12
- package/dist/renderer/summary.js +28 -7
- package/dist/renderer/table.js +21 -4
- package/dist/renderer/test-view.d.ts +1 -0
- package/dist/renderer/test-view.js +44 -9
- package/dist/server/indexed-report-store.js +14 -18
- package/dist/server/job-store.js +64 -26
- package/dist/server/report-server.js +190 -78
- package/dist/server/report-store.js +57 -80
- package/dist/server/skill-index.js +143 -49
- package/dist/server/skill-insights.js +44 -5
- package/dist/shared/artifact-graph.d.ts +3 -0
- package/dist/shared/artifact-graph.js +224 -0
- package/dist/shared/assertion-types.d.ts +8 -0
- package/dist/shared/assertion-types.js +46 -0
- package/dist/shared/atomic-json.d.ts +8 -0
- package/dist/shared/atomic-json.js +33 -0
- package/dist/shared/diagnosis-schema.d.ts +9 -0
- package/dist/shared/diagnosis-schema.js +181 -0
- package/dist/shared/doctor-report.d.ts +3 -0
- package/dist/shared/doctor-report.js +103 -0
- package/dist/shared/evaluation-job.d.ts +6 -0
- package/dist/shared/evaluation-job.js +217 -0
- package/dist/shared/executor-result.d.ts +17 -0
- package/dist/shared/executor-result.js +221 -0
- package/dist/shared/file-lock.d.ts +12 -0
- package/dist/shared/file-lock.js +129 -0
- package/dist/shared/json-value.d.ts +5 -0
- package/dist/shared/json-value.js +36 -0
- package/dist/shared/keyed-mutex.d.ts +7 -0
- package/dist/shared/keyed-mutex.js +24 -0
- package/dist/shared/record-count.d.ts +8 -0
- package/dist/shared/record-count.js +43 -0
- package/dist/shared/sample-contract.d.ts +3 -0
- package/dist/shared/sample-contract.js +332 -0
- package/dist/shared/timestamp.d.ts +6 -0
- package/dist/shared/timestamp.js +64 -0
- package/dist/shared/token-usage.d.ts +19 -0
- package/dist/shared/token-usage.js +50 -0
- package/dist/shared/tool-call-status.d.ts +8 -0
- package/dist/shared/tool-call-status.js +28 -0
- package/dist/shared/tool-identity.d.ts +21 -0
- package/dist/shared/tool-identity.js +84 -0
- package/dist/shared/tool-search.js +73 -16
- package/dist/shared/trace-projection.d.ts +5 -0
- package/dist/shared/trace-projection.js +20 -0
- package/dist/shared/trace-source-kind.d.ts +3 -0
- package/dist/shared/trace-source-kind.js +12 -0
- package/dist/types/diagnosis.d.ts +2 -0
- package/dist/types/eval.d.ts +4 -0
- package/dist/types/executor.d.ts +32 -5
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/judge.d.ts +2 -0
- package/dist/types/observability.d.ts +116 -9
- package/dist/types/report.d.ts +58 -6
- package/dist/types/skill-index.d.ts +7 -0
- package/dist/types/trace.d.ts +2 -0
- package/dist/types/trace.js +1 -0
- package/package.json +9 -5
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
/** Codex rollout JSONL -> source-neutral Trace IR. */
|
|
2
|
+
import { basename } from 'node:path';
|
|
3
|
+
import { isToolResultFailureText } from './text-signals.js';
|
|
4
|
+
import { correlateTraceToolEvents, createTraceId, normalizeTraceTimestamp, traceTimestampBounds, } from './trace-ir.js';
|
|
5
|
+
import { nonNegativeMetric, optionalTokenCount, splitInclusiveInputTokens, tokenCount, } from '../shared/token-usage.js';
|
|
6
|
+
import { normalizeToolIdentity } from '../shared/tool-identity.js';
|
|
7
|
+
const CODEX_RESPONSE_ITEM_TYPES = new Set([
|
|
8
|
+
'message',
|
|
9
|
+
'reasoning',
|
|
10
|
+
'tool_search_call',
|
|
11
|
+
'tool_search_output',
|
|
12
|
+
'web_search_call',
|
|
13
|
+
'image_generation_call',
|
|
14
|
+
'function_call',
|
|
15
|
+
'custom_tool_call',
|
|
16
|
+
'function_call_output',
|
|
17
|
+
'custom_tool_call_output',
|
|
18
|
+
]);
|
|
19
|
+
const CODEX_EVENT_MESSAGE_TYPES = new Set([
|
|
20
|
+
'token_count',
|
|
21
|
+
'task_started',
|
|
22
|
+
'task_complete',
|
|
23
|
+
'turn_aborted',
|
|
24
|
+
'user_message',
|
|
25
|
+
'agent_message',
|
|
26
|
+
'mcp_tool_call_end',
|
|
27
|
+
'patch_apply_end',
|
|
28
|
+
'agent_reasoning',
|
|
29
|
+
'thread_settings_applied',
|
|
30
|
+
'web_search_end',
|
|
31
|
+
'context_compacted',
|
|
32
|
+
'image_generation_end',
|
|
33
|
+
'thread_goal_updated',
|
|
34
|
+
]);
|
|
35
|
+
export function isCodexJsonl(records) {
|
|
36
|
+
return records.some((record) => {
|
|
37
|
+
const raw = asCodexRecord(record);
|
|
38
|
+
if (!isObject(raw?.payload))
|
|
39
|
+
return false;
|
|
40
|
+
const payloadType = stringValue(raw.payload.type);
|
|
41
|
+
if (raw.type === 'session_meta') {
|
|
42
|
+
return stringValue(raw.payload.id) !== undefined
|
|
43
|
+
|| stringValue(raw.payload.session_id) !== undefined;
|
|
44
|
+
}
|
|
45
|
+
if (raw.type === 'turn_context') {
|
|
46
|
+
return stringValue(raw.payload.turn_id) !== undefined
|
|
47
|
+
|| stringValue(raw.payload.model) !== undefined;
|
|
48
|
+
}
|
|
49
|
+
if (raw.type === 'response_item') {
|
|
50
|
+
return payloadType !== undefined && CODEX_RESPONSE_ITEM_TYPES.has(payloadType);
|
|
51
|
+
}
|
|
52
|
+
return raw.type === 'event_msg'
|
|
53
|
+
&& payloadType !== undefined
|
|
54
|
+
&& CODEX_EVENT_MESSAGE_TYPES.has(payloadType);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
export function isCodexGuardianRollout(records) {
|
|
58
|
+
return records.some((record) => {
|
|
59
|
+
const raw = asCodexRecord(record);
|
|
60
|
+
if (raw?.type !== 'session_meta')
|
|
61
|
+
return false;
|
|
62
|
+
const payload = isObject(raw.payload) ? raw.payload : {};
|
|
63
|
+
const source = isObject(payload.source) ? payload.source : {};
|
|
64
|
+
const subagent = source.subagent;
|
|
65
|
+
return (typeof subagent === 'string' && subagent === 'guardian')
|
|
66
|
+
|| (isObject(subagent) && stringValue(subagent.other) === 'guardian');
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export function parseCodexSessionFile(filePath, rawRecords) {
|
|
70
|
+
const meta = rawRecords.map(asCodexRecord).find((record) => record?.type === 'session_meta');
|
|
71
|
+
const metaPayload = isObject(meta?.payload) ? meta.payload : {};
|
|
72
|
+
const runId = stringValue(metaPayload.id)
|
|
73
|
+
?? stringValue(metaPayload.session_id)
|
|
74
|
+
?? basename(filePath).replace(/\.jsonl$/, '');
|
|
75
|
+
const parentRunId = stringValue(metaPayload.parent_thread_id);
|
|
76
|
+
const subagentKind = codexSubagentKind(metaPayload);
|
|
77
|
+
const cwd = stringValue(metaPayload.cwd);
|
|
78
|
+
const entrypoint = codexEntrypoint(metaPayload);
|
|
79
|
+
const role = parentRunId || metaPayload.thread_source === 'subagent' || subagentKind
|
|
80
|
+
? 'subagent'
|
|
81
|
+
: 'main';
|
|
82
|
+
const events = correlateTraceToolEvents(convertCodexRecords(rawRecords, runId));
|
|
83
|
+
const bounds = traceTimestampBounds([
|
|
84
|
+
...events.map((event) => event.timestamp),
|
|
85
|
+
meta?.timestamp,
|
|
86
|
+
metaPayload.timestamp,
|
|
87
|
+
]);
|
|
88
|
+
return {
|
|
89
|
+
runId,
|
|
90
|
+
rootRunId: parentRunId ?? runId,
|
|
91
|
+
parentRunId,
|
|
92
|
+
traceId: createTraceId({ sourceKind: 'codex', runId, sourcePath: filePath }),
|
|
93
|
+
groupPath: `codex:${parentRunId ?? runId}`,
|
|
94
|
+
role,
|
|
95
|
+
label: role === 'subagent' ? `subagent/${runId}` : `main/${basename(filePath)}`,
|
|
96
|
+
sourcePath: filePath,
|
|
97
|
+
sourceKind: 'codex',
|
|
98
|
+
events,
|
|
99
|
+
cwd,
|
|
100
|
+
gitBranch: isObject(metaPayload.git) ? stringValue(metaPayload.git.branch) : undefined,
|
|
101
|
+
entrypoint,
|
|
102
|
+
sourceMetadata: codexSourceMetadata(rawRecords, metaPayload),
|
|
103
|
+
...bounds,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function convertCodexRecords(rawRecords, runId) {
|
|
107
|
+
const events = [];
|
|
108
|
+
const mcpEnds = indexMcpCallEnds(rawRecords);
|
|
109
|
+
const patchEnds = indexPatchApplyEnds(rawRecords);
|
|
110
|
+
const externalEnds = indexExternalToolEnds(rawRecords);
|
|
111
|
+
const callOccurrences = new Map();
|
|
112
|
+
const resultOccurrences = new Map();
|
|
113
|
+
const externalCallOccurrences = new Map();
|
|
114
|
+
const externalResultOccurrences = new Map();
|
|
115
|
+
const representedMcpCalls = new Set();
|
|
116
|
+
const representedMcpResults = new Set();
|
|
117
|
+
const representedPatchCalls = new Set();
|
|
118
|
+
const representedPatchResults = new Set();
|
|
119
|
+
const duplicateEventMessageIndexes = indexDuplicateEventMessages(rawRecords);
|
|
120
|
+
let activeModel;
|
|
121
|
+
let activeTurnId;
|
|
122
|
+
let previousTotalUsageFingerprint;
|
|
123
|
+
rawRecords.forEach((value, sourceIndex) => {
|
|
124
|
+
const record = asCodexRecord(value);
|
|
125
|
+
if (!record)
|
|
126
|
+
return;
|
|
127
|
+
const timestamp = normalizeTraceTimestamp(record.timestamp);
|
|
128
|
+
const payload = isObject(record.payload) ? record.payload : {};
|
|
129
|
+
const payloadType = stringValue(payload.type);
|
|
130
|
+
const eventId = (suffix) => `${runId}:${sourceIndex}:${suffix}`;
|
|
131
|
+
const base = {
|
|
132
|
+
sourceEventId: stringValue(payload.id),
|
|
133
|
+
sourceIndex,
|
|
134
|
+
sourceType: `${String(record.type ?? 'unknown')}:${payloadType ?? ''}`,
|
|
135
|
+
timestamp,
|
|
136
|
+
turnId: activeTurnId,
|
|
137
|
+
};
|
|
138
|
+
// Desktop may repeat session_meta during a long rollout. Metadata is read
|
|
139
|
+
// once above and is transparent to the event stream; retaining later copies
|
|
140
|
+
// as unknown events also breaks otherwise adjacent mirrored-message pairs.
|
|
141
|
+
if (record.type === 'session_meta')
|
|
142
|
+
return;
|
|
143
|
+
if (record.type === 'turn_context') {
|
|
144
|
+
activeModel = stringValue(payload.model) ?? activeModel;
|
|
145
|
+
activeTurnId = stringValue(payload.turn_id) ?? activeTurnId;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (record.type === 'response_item' && payloadType === 'message') {
|
|
149
|
+
const role = stringValue(payload.role);
|
|
150
|
+
const text = codexContentText(payload.content);
|
|
151
|
+
if ((role === 'user' || role === 'assistant' || role === 'system' || role === 'developer')
|
|
152
|
+
&& text) {
|
|
153
|
+
const normalizedRole = role === 'developer' ? 'system' : role;
|
|
154
|
+
events.push({
|
|
155
|
+
...base,
|
|
156
|
+
eventKind: 'message',
|
|
157
|
+
eventId: eventId('message'),
|
|
158
|
+
role: normalizedRole,
|
|
159
|
+
origin: normalizedRole === 'user'
|
|
160
|
+
? codexUserMessageOrigin(text)
|
|
161
|
+
: normalizedRole === 'system' ? 'runtime' : 'synthetic',
|
|
162
|
+
text,
|
|
163
|
+
model: normalizedRole === 'assistant' ? activeModel : undefined,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (record.type === 'response_item' && payloadType === 'tool_search_call') {
|
|
169
|
+
const callId = stringValue(payload.call_id) ?? stringValue(payload.id) ?? `codex-tool-search-${sourceIndex}`;
|
|
170
|
+
events.push(toolCallEvent(eventId('tool-call'), base, callId, normalizeToolIdentity({ sourceName: 'tool_search' }), parseToolInput(payload.arguments), activeModel));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (record.type === 'response_item' && payloadType === 'tool_search_output') {
|
|
174
|
+
const callId = stringValue(payload.call_id) ?? `codex-tool-search-${sourceIndex}`;
|
|
175
|
+
const output = JSON.stringify({
|
|
176
|
+
status: stringValue(payload.status),
|
|
177
|
+
execution: stringValue(payload.execution),
|
|
178
|
+
tools: summarizeDiscoveredTools(payload.tools),
|
|
179
|
+
});
|
|
180
|
+
events.push(toolResultEvent(eventId('tool-result'), base, callId, output, statusFromCodex(payload.status), stringValue(payload.status) ? 'runtime' : 'unknown'));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (record.type === 'response_item' && payloadType === 'web_search_call') {
|
|
184
|
+
const callId = stringValue(payload.id) ?? `codex-web-search-${sourceIndex}`;
|
|
185
|
+
takeOccurrence(externalCallOccurrences, callId);
|
|
186
|
+
const resultOccurrence = takeOccurrence(externalResultOccurrences, callId);
|
|
187
|
+
const completed = externalEnds.byOccurrence.has(mcpCallOccurrenceKey(callId, resultOccurrence));
|
|
188
|
+
const payloadStatus = stringValue(payload.status);
|
|
189
|
+
const explicitStatus = statusFromCodex(payloadStatus);
|
|
190
|
+
events.push(toolCallEvent(eventId('tool-call'), base, callId, normalizeToolIdentity({ sourceName: 'web_search' }), isObject(payload.action) ? payload.action : {}, activeModel));
|
|
191
|
+
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'));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (record.type === 'response_item' && payloadType === 'image_generation_call') {
|
|
195
|
+
const callId = stringValue(payload.id) ?? `codex-image-generation-${sourceIndex}`;
|
|
196
|
+
const result = typeof payload.result === 'string' ? payload.result : '';
|
|
197
|
+
takeOccurrence(externalCallOccurrences, callId);
|
|
198
|
+
const resultOccurrence = takeOccurrence(externalResultOccurrences, callId);
|
|
199
|
+
const completed = externalEnds.byOccurrence.has(mcpCallOccurrenceKey(callId, resultOccurrence));
|
|
200
|
+
const payloadStatus = stringValue(payload.status);
|
|
201
|
+
const explicitStatus = statusFromCodex(payloadStatus);
|
|
202
|
+
const inferredSuccess = result.length > 0 || completed;
|
|
203
|
+
events.push(toolCallEvent(eventId('tool-call'), base, callId, normalizeToolIdentity({ sourceName: 'image_generation' }), { prompt: stringValue(payload.revised_prompt) }, activeModel));
|
|
204
|
+
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'));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (record.type === 'response_item'
|
|
208
|
+
&& (payloadType === 'function_call' || payloadType === 'custom_tool_call')) {
|
|
209
|
+
const callId = stringValue(payload.call_id) ?? stringValue(payload.id) ?? `codex-call-${sourceIndex}`;
|
|
210
|
+
const occurrence = takeOccurrence(callOccurrences, callId);
|
|
211
|
+
takeOccurrence(externalCallOccurrences, callId);
|
|
212
|
+
const mcpEndKey = mcpCallOccurrenceKey(callId, occurrence);
|
|
213
|
+
const mcpEnd = mcpEnds.byOccurrence.get(mcpEndKey);
|
|
214
|
+
if (mcpEnd)
|
|
215
|
+
representedMcpCalls.add(mcpEndKey);
|
|
216
|
+
const sourceName = stringValue(payload.name) ?? 'unknown';
|
|
217
|
+
const patchEnd = sourceName.toLowerCase() === 'apply_patch'
|
|
218
|
+
? patchEnds.byOccurrence.get(mcpEndKey)
|
|
219
|
+
: undefined;
|
|
220
|
+
if (patchEnd)
|
|
221
|
+
representedPatchCalls.add(mcpEndKey);
|
|
222
|
+
const normalized = normalizeCodexTool(sourceName, payload.arguments ?? payload.input, mcpEnd, stringValue(payload.namespace));
|
|
223
|
+
events.push(toolCallEvent(eventId('tool-call'), base, callId, normalized.tool, normalized.input, activeModel));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (record.type === 'response_item'
|
|
227
|
+
&& (payloadType === 'function_call_output' || payloadType === 'custom_tool_call_output')) {
|
|
228
|
+
const callId = stringValue(payload.call_id) ?? `codex-call-${sourceIndex}`;
|
|
229
|
+
const occurrence = takeOccurrence(resultOccurrences, callId);
|
|
230
|
+
const externalOccurrence = takeOccurrence(externalResultOccurrences, callId);
|
|
231
|
+
const mcpEndKey = mcpCallOccurrenceKey(callId, occurrence);
|
|
232
|
+
const mcpEnd = mcpEnds.byOccurrence.get(mcpEndKey);
|
|
233
|
+
if (mcpEnd)
|
|
234
|
+
representedMcpResults.add(mcpEndKey);
|
|
235
|
+
const patchEnd = patchEnds.byOccurrence.get(mcpEndKey);
|
|
236
|
+
if (patchEnd)
|
|
237
|
+
representedPatchResults.add(mcpEndKey);
|
|
238
|
+
const output = codexContentText(payload.output) || mcpEnd?.output || patchEnd?.output || '';
|
|
239
|
+
const runtimeOutcome = runtimeToolEndOutcome(mcpEnd ?? patchEnd);
|
|
240
|
+
const payloadStatus = stringValue(payload.status);
|
|
241
|
+
const hasRuntimeStatus = runtimeOutcome.present || payloadStatus !== undefined;
|
|
242
|
+
const explicitStatus = runtimeOutcome.present
|
|
243
|
+
? runtimeOutcome.status
|
|
244
|
+
: statusFromCodex(payloadStatus);
|
|
245
|
+
const inferredFailure = isToolResultFailureText(output) || codexToolOutputFailed(output);
|
|
246
|
+
const inferredSuccess = !inferredFailure && codexToolOutputSucceeded(output);
|
|
247
|
+
const completedExternalCall = externalEnds.byOccurrence.has(mcpCallOccurrenceKey(callId, externalOccurrence));
|
|
248
|
+
const inferredStatus = inferredFailure
|
|
249
|
+
? 'failure'
|
|
250
|
+
: inferredSuccess || completedExternalCall ? 'success' : 'unknown';
|
|
251
|
+
events.push(toolResultEvent(eventId('tool-result'), base, callId, output, hasRuntimeStatus ? explicitStatus : inferredStatus, hasRuntimeStatus
|
|
252
|
+
? 'runtime'
|
|
253
|
+
: inferredStatus === 'unknown' ? 'unknown' : 'inferred'));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (record.type === 'event_msg' && payloadType === 'token_count') {
|
|
257
|
+
const info = isObject(payload.info) ? payload.info : {};
|
|
258
|
+
const usage = isObject(info.last_token_usage) ? info.last_token_usage : undefined;
|
|
259
|
+
if (!isValidCodexTokenUsage(usage)) {
|
|
260
|
+
events.push({
|
|
261
|
+
...base,
|
|
262
|
+
eventKind: 'unknown',
|
|
263
|
+
eventId: eventId('invalid-usage'),
|
|
264
|
+
raw: value,
|
|
265
|
+
});
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const totalUsage = isObject(info.total_token_usage) ? info.total_token_usage : undefined;
|
|
269
|
+
const fingerprint = tokenUsageFingerprint(totalUsage);
|
|
270
|
+
if (totalUsage && !fingerprint) {
|
|
271
|
+
events.push({
|
|
272
|
+
...base,
|
|
273
|
+
eventKind: 'unknown',
|
|
274
|
+
eventId: eventId('invalid-total-usage'),
|
|
275
|
+
raw: value,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
if (fingerprint && fingerprint === previousTotalUsageFingerprint)
|
|
279
|
+
return;
|
|
280
|
+
const normalized = normalizeCodexTokenUsage(usage);
|
|
281
|
+
previousTotalUsageFingerprint = fingerprint;
|
|
282
|
+
events.push({
|
|
283
|
+
...base,
|
|
284
|
+
eventKind: 'usage',
|
|
285
|
+
eventId: eventId('usage'),
|
|
286
|
+
model: activeModel,
|
|
287
|
+
...normalized,
|
|
288
|
+
});
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (record.type === 'event_msg' && payloadType === 'task_started') {
|
|
292
|
+
activeTurnId = stringValue(payload.turn_id) ?? activeTurnId;
|
|
293
|
+
events.push({
|
|
294
|
+
...base,
|
|
295
|
+
turnId: activeTurnId,
|
|
296
|
+
eventKind: 'lifecycle',
|
|
297
|
+
eventId: eventId('lifecycle'),
|
|
298
|
+
phase: 'turn_started',
|
|
299
|
+
});
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (record.type === 'event_msg' && payloadType === 'task_complete') {
|
|
303
|
+
events.push({
|
|
304
|
+
...base,
|
|
305
|
+
eventKind: 'lifecycle',
|
|
306
|
+
eventId: eventId('lifecycle'),
|
|
307
|
+
phase: 'turn_completed',
|
|
308
|
+
});
|
|
309
|
+
activeTurnId = undefined;
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (record.type === 'event_msg' && payloadType === 'turn_aborted') {
|
|
313
|
+
events.push({
|
|
314
|
+
...base,
|
|
315
|
+
turnId: stringValue(payload.turn_id) ?? activeTurnId,
|
|
316
|
+
eventKind: 'lifecycle',
|
|
317
|
+
eventId: eventId('lifecycle'),
|
|
318
|
+
phase: 'turn_aborted',
|
|
319
|
+
reason: stringValue(payload.reason),
|
|
320
|
+
durationMs: nonNegativeMetric(payload.duration_ms),
|
|
321
|
+
});
|
|
322
|
+
activeTurnId = undefined;
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (record.type === 'event_msg' && payloadType === 'user_message') {
|
|
326
|
+
const text = stringValue(payload.message);
|
|
327
|
+
if (text) {
|
|
328
|
+
if (duplicateEventMessageIndexes.has(sourceIndex))
|
|
329
|
+
return;
|
|
330
|
+
events.push({
|
|
331
|
+
...base,
|
|
332
|
+
eventKind: 'message',
|
|
333
|
+
eventId: eventId('message'),
|
|
334
|
+
role: 'user',
|
|
335
|
+
origin: codexUserMessageOrigin(text),
|
|
336
|
+
text,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (record.type === 'event_msg' && payloadType === 'agent_message') {
|
|
342
|
+
const text = stringValue(payload.message);
|
|
343
|
+
if (text) {
|
|
344
|
+
if (duplicateEventMessageIndexes.has(sourceIndex))
|
|
345
|
+
return;
|
|
346
|
+
events.push({
|
|
347
|
+
...base,
|
|
348
|
+
eventKind: 'message',
|
|
349
|
+
eventId: eventId('message'),
|
|
350
|
+
role: 'assistant',
|
|
351
|
+
origin: 'synthetic',
|
|
352
|
+
text,
|
|
353
|
+
model: activeModel,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (payloadType === 'mcp_tool_call_end') {
|
|
359
|
+
const end = mcpEnds.bySourceIndex.get(sourceIndex);
|
|
360
|
+
if (end) {
|
|
361
|
+
end.turnId = activeTurnId;
|
|
362
|
+
end.model = activeModel;
|
|
363
|
+
}
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (payloadType === 'patch_apply_end') {
|
|
367
|
+
const end = patchEnds.bySourceIndex.get(sourceIndex);
|
|
368
|
+
if (end) {
|
|
369
|
+
end.turnId = activeTurnId;
|
|
370
|
+
end.model = activeModel;
|
|
371
|
+
}
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (isKnownNonMeasurementCodexRecord(record.type, payloadType))
|
|
375
|
+
return;
|
|
376
|
+
events.push({
|
|
377
|
+
...base,
|
|
378
|
+
eventKind: 'unknown',
|
|
379
|
+
eventId: eventId('unknown'),
|
|
380
|
+
raw: value,
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
for (const end of mcpEnds.ordered) {
|
|
384
|
+
const endKey = mcpCallOccurrenceKey(end.callId, end.occurrence);
|
|
385
|
+
const outcome = runtimeToolEndOutcome(end);
|
|
386
|
+
if (!representedMcpCalls.has(endKey)) {
|
|
387
|
+
events.push({
|
|
388
|
+
eventKind: 'tool_call',
|
|
389
|
+
eventId: `${runId}:${end.sourceIndex}:mcp-tool-call`,
|
|
390
|
+
sourceEventId: end.sourceEventId,
|
|
391
|
+
sourceIndex: end.sourceIndex,
|
|
392
|
+
sourceType: end.sourceType,
|
|
393
|
+
timestamp: end.timestamp,
|
|
394
|
+
turnId: end.turnId,
|
|
395
|
+
callId: end.callId,
|
|
396
|
+
tool: mcpToolRefFromEnd(end),
|
|
397
|
+
input: end.input ?? {},
|
|
398
|
+
model: end.model,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
if (representedMcpResults.has(endKey))
|
|
402
|
+
continue;
|
|
403
|
+
events.push({
|
|
404
|
+
eventKind: 'tool_result',
|
|
405
|
+
eventId: `${runId}:${end.sourceIndex}:mcp-tool-result`,
|
|
406
|
+
sourceEventId: end.sourceEventId,
|
|
407
|
+
sourceIndex: end.sourceIndex,
|
|
408
|
+
sourceType: end.sourceType,
|
|
409
|
+
timestamp: end.timestamp,
|
|
410
|
+
turnId: end.turnId,
|
|
411
|
+
callId: end.callId,
|
|
412
|
+
output: end.output ?? '',
|
|
413
|
+
status: outcome.status,
|
|
414
|
+
statusSource: outcome.present ? 'runtime' : 'unknown',
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
for (const end of patchEnds.ordered) {
|
|
418
|
+
const endKey = mcpCallOccurrenceKey(end.callId, end.occurrence);
|
|
419
|
+
const outcome = runtimeToolEndOutcome(end);
|
|
420
|
+
if (!representedPatchCalls.has(endKey)) {
|
|
421
|
+
events.push({
|
|
422
|
+
eventKind: 'tool_call',
|
|
423
|
+
eventId: `${runId}:${end.sourceIndex}:patch-tool-call`,
|
|
424
|
+
sourceIndex: end.sourceIndex,
|
|
425
|
+
sourceType: end.sourceType,
|
|
426
|
+
timestamp: end.timestamp,
|
|
427
|
+
turnId: end.turnId,
|
|
428
|
+
callId: end.callId,
|
|
429
|
+
tool: { name: 'Edit', sourceName: 'apply_patch' },
|
|
430
|
+
input: {},
|
|
431
|
+
model: end.model,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
if (representedPatchResults.has(endKey))
|
|
435
|
+
continue;
|
|
436
|
+
events.push({
|
|
437
|
+
eventKind: 'tool_result',
|
|
438
|
+
eventId: `${runId}:${end.sourceIndex}:patch-tool-result`,
|
|
439
|
+
sourceIndex: end.sourceIndex,
|
|
440
|
+
sourceType: end.sourceType,
|
|
441
|
+
timestamp: end.timestamp,
|
|
442
|
+
turnId: end.turnId,
|
|
443
|
+
callId: end.callId,
|
|
444
|
+
output: end.output ?? '',
|
|
445
|
+
status: outcome.status,
|
|
446
|
+
statusSource: outcome.present ? 'runtime' : 'unknown',
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
events.sort((a, b) => a.sourceIndex - b.sourceIndex);
|
|
450
|
+
return events;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Codex persists protocol and UI state alongside behavioral evidence. These
|
|
454
|
+
* records are understood by this adapter but intentionally do not become Trace
|
|
455
|
+
* IR events. Keeping them out of `unknown` means ingestion warnings remain a
|
|
456
|
+
* forward-compatibility signal instead of firing on every normal rollout.
|
|
457
|
+
*/
|
|
458
|
+
function isKnownNonMeasurementCodexRecord(recordType, payloadType) {
|
|
459
|
+
if (recordType === 'compacted' || recordType === 'world_state')
|
|
460
|
+
return true;
|
|
461
|
+
if (recordType === 'response_item' && payloadType === 'reasoning')
|
|
462
|
+
return true;
|
|
463
|
+
if (recordType !== 'event_msg')
|
|
464
|
+
return false;
|
|
465
|
+
return payloadType === 'agent_reasoning'
|
|
466
|
+
|| payloadType === 'thread_settings_applied'
|
|
467
|
+
|| payloadType === 'web_search_end'
|
|
468
|
+
|| payloadType === 'context_compacted'
|
|
469
|
+
|| payloadType === 'image_generation_end'
|
|
470
|
+
|| payloadType === 'thread_goal_updated';
|
|
471
|
+
}
|
|
472
|
+
function mcpToolRefFromEnd(end) {
|
|
473
|
+
return normalizeToolIdentity({
|
|
474
|
+
sourceName: 'mcp_tool_call',
|
|
475
|
+
provider: end.server,
|
|
476
|
+
authoritativeName: end.tool ?? 'unknown',
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
function toolCallEvent(eventId, base, callId, tool, input, model) {
|
|
480
|
+
return { ...base, eventKind: 'tool_call', eventId, callId, tool, input, model };
|
|
481
|
+
}
|
|
482
|
+
function toolResultEvent(eventId, base, callId, output, status, statusSource) {
|
|
483
|
+
return { ...base, eventKind: 'tool_result', eventId, callId, output, status, statusSource };
|
|
484
|
+
}
|
|
485
|
+
function indexMcpCallEnds(records) {
|
|
486
|
+
const ordered = [];
|
|
487
|
+
const byOccurrence = new Map();
|
|
488
|
+
const bySourceIndex = new Map();
|
|
489
|
+
const occurrences = new Map();
|
|
490
|
+
records.forEach((value, sourceIndex) => {
|
|
491
|
+
const record = asCodexRecord(value);
|
|
492
|
+
const payload = isObject(record?.payload) ? record.payload : {};
|
|
493
|
+
if (payload.type !== 'mcp_tool_call_end')
|
|
494
|
+
return;
|
|
495
|
+
const callId = stringValue(payload.call_id) ?? stringValue(payload.id);
|
|
496
|
+
if (!callId)
|
|
497
|
+
return;
|
|
498
|
+
const occurrence = takeOccurrence(occurrences, callId);
|
|
499
|
+
const invocation = isObject(payload.invocation) ? payload.invocation : {};
|
|
500
|
+
const result = isObject(payload.result) ? payload.result : {};
|
|
501
|
+
const hasOk = Object.prototype.hasOwnProperty.call(result, 'Ok');
|
|
502
|
+
const ok = isObject(result.Ok) ? result.Ok : undefined;
|
|
503
|
+
const hasErr = Object.prototype.hasOwnProperty.call(result, 'Err');
|
|
504
|
+
const err = hasErr ? result.Err : undefined;
|
|
505
|
+
const end = {
|
|
506
|
+
callId,
|
|
507
|
+
occurrence,
|
|
508
|
+
sourceIndex,
|
|
509
|
+
sourceEventId: stringValue(payload.id),
|
|
510
|
+
sourceType: `${String(record?.type ?? 'unknown')}:${String(payload.type)}`,
|
|
511
|
+
timestamp: normalizeTraceTimestamp(record?.timestamp),
|
|
512
|
+
isError: booleanValue(payload.isError)
|
|
513
|
+
?? booleanValue(payload.is_error)
|
|
514
|
+
?? booleanValue(result.isError)
|
|
515
|
+
?? booleanValue(result.is_error)
|
|
516
|
+
?? booleanValue(ok?.isError)
|
|
517
|
+
?? booleanValue(ok?.is_error)
|
|
518
|
+
?? (hasErr ? true : hasOk ? false : undefined),
|
|
519
|
+
status: stringValue(payload.status) ?? stringValue(result.status) ?? stringValue(ok?.status),
|
|
520
|
+
tool: stringValue(invocation.tool) ?? stringValue(payload.tool),
|
|
521
|
+
server: stringValue(invocation.server) ?? stringValue(invocation.provider) ?? stringValue(payload.server),
|
|
522
|
+
input: parseToolInput(invocation.arguments ?? invocation.input ?? payload.arguments ?? payload.input),
|
|
523
|
+
output: codexContentText(payload.output
|
|
524
|
+
?? result.output
|
|
525
|
+
?? result.content
|
|
526
|
+
?? ok?.content
|
|
527
|
+
?? ok?.structuredContent
|
|
528
|
+
?? err),
|
|
529
|
+
};
|
|
530
|
+
ordered.push(end);
|
|
531
|
+
byOccurrence.set(mcpCallOccurrenceKey(callId, occurrence), end);
|
|
532
|
+
bySourceIndex.set(sourceIndex, end);
|
|
533
|
+
});
|
|
534
|
+
return { ordered, byOccurrence, bySourceIndex };
|
|
535
|
+
}
|
|
536
|
+
function indexPatchApplyEnds(records) {
|
|
537
|
+
const ordered = [];
|
|
538
|
+
const byOccurrence = new Map();
|
|
539
|
+
const bySourceIndex = new Map();
|
|
540
|
+
const occurrences = new Map();
|
|
541
|
+
records.forEach((value, sourceIndex) => {
|
|
542
|
+
const record = asCodexRecord(value);
|
|
543
|
+
const payload = isObject(record?.payload) ? record.payload : {};
|
|
544
|
+
if (payload.type !== 'patch_apply_end')
|
|
545
|
+
return;
|
|
546
|
+
const callId = stringValue(payload.call_id) ?? stringValue(payload.id);
|
|
547
|
+
if (!callId)
|
|
548
|
+
return;
|
|
549
|
+
const occurrence = takeOccurrence(occurrences, callId);
|
|
550
|
+
const success = booleanValue(payload.success);
|
|
551
|
+
const end = {
|
|
552
|
+
callId,
|
|
553
|
+
occurrence,
|
|
554
|
+
sourceIndex,
|
|
555
|
+
sourceType: `${String(record?.type ?? 'unknown')}:${String(payload.type)}`,
|
|
556
|
+
timestamp: normalizeTraceTimestamp(record?.timestamp),
|
|
557
|
+
isError: success === undefined ? undefined : !success,
|
|
558
|
+
status: stringValue(payload.status),
|
|
559
|
+
output: [stringValue(payload.stdout), stringValue(payload.stderr)].filter(Boolean).join('\n'),
|
|
560
|
+
};
|
|
561
|
+
ordered.push(end);
|
|
562
|
+
byOccurrence.set(mcpCallOccurrenceKey(callId, occurrence), end);
|
|
563
|
+
bySourceIndex.set(sourceIndex, end);
|
|
564
|
+
});
|
|
565
|
+
return { ordered, byOccurrence, bySourceIndex };
|
|
566
|
+
}
|
|
567
|
+
function indexExternalToolEnds(records) {
|
|
568
|
+
const byOccurrence = new Set();
|
|
569
|
+
const occurrences = new Map();
|
|
570
|
+
for (const value of records) {
|
|
571
|
+
const record = asCodexRecord(value);
|
|
572
|
+
const payload = isObject(record?.payload) ? record.payload : {};
|
|
573
|
+
if (payload.type !== 'web_search_end' && payload.type !== 'image_generation_end')
|
|
574
|
+
continue;
|
|
575
|
+
const callId = stringValue(payload.call_id) ?? stringValue(payload.id);
|
|
576
|
+
if (!callId)
|
|
577
|
+
continue;
|
|
578
|
+
byOccurrence.add(mcpCallOccurrenceKey(callId, takeOccurrence(occurrences, callId)));
|
|
579
|
+
}
|
|
580
|
+
return { byOccurrence };
|
|
581
|
+
}
|
|
582
|
+
function takeOccurrence(counts, callId) {
|
|
583
|
+
const occurrence = counts.get(callId) ?? 0;
|
|
584
|
+
counts.set(callId, occurrence + 1);
|
|
585
|
+
return occurrence;
|
|
586
|
+
}
|
|
587
|
+
function mcpCallOccurrenceKey(callId, occurrence) {
|
|
588
|
+
return `${callId}\u0000${occurrence}`;
|
|
589
|
+
}
|
|
590
|
+
function runtimeToolEndOutcome(end) {
|
|
591
|
+
if (!end)
|
|
592
|
+
return { status: 'unknown', present: false };
|
|
593
|
+
if (end.status !== undefined) {
|
|
594
|
+
const status = statusFromCodex(end.status);
|
|
595
|
+
if (status === 'cancelled')
|
|
596
|
+
return { status, present: true };
|
|
597
|
+
if (status === 'failure' || end.isError === true) {
|
|
598
|
+
return { status: 'failure', present: true };
|
|
599
|
+
}
|
|
600
|
+
if (status === 'success')
|
|
601
|
+
return { status, present: true };
|
|
602
|
+
return { status: 'unknown', present: true };
|
|
603
|
+
}
|
|
604
|
+
if (end.isError === true)
|
|
605
|
+
return { status: 'failure', present: true };
|
|
606
|
+
if (end.isError === false)
|
|
607
|
+
return { status: 'success', present: true };
|
|
608
|
+
return { status: 'unknown', present: false };
|
|
609
|
+
}
|
|
610
|
+
function normalizeCodexTool(sourceName, rawInput, mcpEnd, sourceNamespace) {
|
|
611
|
+
const input = parseToolInput(rawInput);
|
|
612
|
+
// Codex desktop's orchestration wrapper names its JavaScript command bridge
|
|
613
|
+
// `exec`. This mapping is source-specific: a generic tool named `exec` must not
|
|
614
|
+
// become shell execution outside the Codex adapter.
|
|
615
|
+
const identitySourceName = sourceName.toLowerCase() === 'exec'
|
|
616
|
+
? 'command_execution'
|
|
617
|
+
: sourceName;
|
|
618
|
+
const normalizedTool = normalizeToolIdentity({
|
|
619
|
+
sourceName: identitySourceName,
|
|
620
|
+
namespace: sourceNamespace,
|
|
621
|
+
provider: mcpEnd?.server,
|
|
622
|
+
authoritativeName: mcpEnd?.tool,
|
|
623
|
+
});
|
|
624
|
+
const tool = identitySourceName === sourceName
|
|
625
|
+
? normalizedTool
|
|
626
|
+
: { ...normalizedTool, sourceName };
|
|
627
|
+
if (tool.name === 'Bash') {
|
|
628
|
+
return {
|
|
629
|
+
tool,
|
|
630
|
+
input: {
|
|
631
|
+
...input,
|
|
632
|
+
command: stringValue(input.command)
|
|
633
|
+
?? stringValue(input.cmd)
|
|
634
|
+
?? stringValue(input.input)
|
|
635
|
+
?? '',
|
|
636
|
+
},
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
if (tool.name === 'ViewImage') {
|
|
640
|
+
return {
|
|
641
|
+
tool,
|
|
642
|
+
input: { ...input, file_path: stringValue(input.file_path) ?? stringValue(input.path) },
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
return { tool, input };
|
|
646
|
+
}
|
|
647
|
+
function codexUserMessageOrigin(text) {
|
|
648
|
+
const trimmed = text.trimStart();
|
|
649
|
+
if (/^# AGENTS\.md instructions\b/i.test(trimmed))
|
|
650
|
+
return 'runtime';
|
|
651
|
+
if (/^<(?:app-context|environment_context|permissions instructions|collaboration_mode|apps_instructions|plugins_instructions|skills_instructions|recommended_plugins)>/i.test(trimmed))
|
|
652
|
+
return 'runtime';
|
|
653
|
+
return 'human';
|
|
654
|
+
}
|
|
655
|
+
function summarizeDiscoveredTools(value) {
|
|
656
|
+
if (!Array.isArray(value))
|
|
657
|
+
return [];
|
|
658
|
+
return value.flatMap((entry) => {
|
|
659
|
+
if (!isObject(entry))
|
|
660
|
+
return [];
|
|
661
|
+
const nested = Array.isArray(entry.tools)
|
|
662
|
+
? entry.tools.flatMap((tool) => isObject(tool) && stringValue(tool.name) ? [stringValue(tool.name)] : [])
|
|
663
|
+
: undefined;
|
|
664
|
+
return [{
|
|
665
|
+
type: stringValue(entry.type),
|
|
666
|
+
name: stringValue(entry.name),
|
|
667
|
+
...(nested && nested.length > 0 ? { tools: nested } : {}),
|
|
668
|
+
}];
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
function statusFromCodex(value) {
|
|
672
|
+
const status = stringValue(value)?.toLowerCase();
|
|
673
|
+
if (status === 'failed' || status === 'error')
|
|
674
|
+
return 'failure';
|
|
675
|
+
if (status === 'cancelled' || status === 'canceled')
|
|
676
|
+
return 'cancelled';
|
|
677
|
+
if (status === 'success' || status === 'succeeded' || status === 'completed' || status === 'complete')
|
|
678
|
+
return 'success';
|
|
679
|
+
return 'unknown';
|
|
680
|
+
}
|
|
681
|
+
function parseToolInput(value) {
|
|
682
|
+
if (isObject(value))
|
|
683
|
+
return value;
|
|
684
|
+
if (typeof value !== 'string')
|
|
685
|
+
return {};
|
|
686
|
+
try {
|
|
687
|
+
const parsed = JSON.parse(value);
|
|
688
|
+
return isObject(parsed) ? parsed : { input: value };
|
|
689
|
+
}
|
|
690
|
+
catch {
|
|
691
|
+
return { input: value };
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
function codexContentText(value) {
|
|
695
|
+
if (typeof value === 'string')
|
|
696
|
+
return value;
|
|
697
|
+
if (!Array.isArray(value))
|
|
698
|
+
return value == null ? '' : JSON.stringify(value);
|
|
699
|
+
return value.map((part) => {
|
|
700
|
+
if (typeof part === 'string')
|
|
701
|
+
return part;
|
|
702
|
+
if (!isObject(part))
|
|
703
|
+
return '';
|
|
704
|
+
const text = stringValue(part.text) ?? stringValue(part.output_text);
|
|
705
|
+
if (text)
|
|
706
|
+
return text;
|
|
707
|
+
return part.type === 'input_image' ? '[image]' : '';
|
|
708
|
+
}).filter(Boolean).join('\n');
|
|
709
|
+
}
|
|
710
|
+
function codexToolOutputFailed(output) {
|
|
711
|
+
return /\b(?:process|script)\s+(?:exited|failed)\s+with\s+(?:exit\s+)?code\s+[1-9]\d*\b/i.test(output)
|
|
712
|
+
|| /\bexit[_\s-]?code\s*[:=]\s*[1-9]\d*\b/i.test(output)
|
|
713
|
+
|| /\bapply_patch verification failed\b/i.test(output);
|
|
714
|
+
}
|
|
715
|
+
function codexToolOutputSucceeded(output) {
|
|
716
|
+
const trimmed = output.trim();
|
|
717
|
+
return /\bprocess exited with code 0\b/i.test(trimmed)
|
|
718
|
+
|| /\bexit code:\s*0\b/i.test(trimmed)
|
|
719
|
+
|| /^script completed\b/i.test(trimmed)
|
|
720
|
+
|| /^success\.\s+(?:updated|added|deleted|moved)\b/i.test(trimmed)
|
|
721
|
+
|| /^plan updated\b/i.test(trimmed)
|
|
722
|
+
|| /^workspace dependencies are available\b/i.test(trimmed)
|
|
723
|
+
|| /^\[image\]$/i.test(trimmed);
|
|
724
|
+
}
|
|
725
|
+
function codexSourceMetadata(records, metaPayload) {
|
|
726
|
+
const models = Array.from(new Set(records.flatMap((value) => {
|
|
727
|
+
const record = asCodexRecord(value);
|
|
728
|
+
if (record?.type !== 'turn_context')
|
|
729
|
+
return [];
|
|
730
|
+
const payload = isObject(record.payload) ? record.payload : {};
|
|
731
|
+
const model = stringValue(payload.model);
|
|
732
|
+
return model ? [model] : [];
|
|
733
|
+
})));
|
|
734
|
+
return {
|
|
735
|
+
provider: stringValue(metaPayload.model_provider) ?? 'openai',
|
|
736
|
+
model: models.length > 0 ? models.join(', ') : undefined,
|
|
737
|
+
modelApi: 'codex',
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
function tokenUsageFingerprint(usage) {
|
|
741
|
+
if (!isValidCodexTokenUsage(usage))
|
|
742
|
+
return undefined;
|
|
743
|
+
const keys = [
|
|
744
|
+
'input_tokens',
|
|
745
|
+
'cached_input_tokens',
|
|
746
|
+
'cache_write_input_tokens',
|
|
747
|
+
'output_tokens',
|
|
748
|
+
'reasoning_output_tokens',
|
|
749
|
+
'total_tokens',
|
|
750
|
+
];
|
|
751
|
+
const values = keys.map((key) => optionalTokenCount(usage[key]));
|
|
752
|
+
return values.map((value) => value ?? 0).join(':');
|
|
753
|
+
}
|
|
754
|
+
function isValidCodexTokenUsage(usage) {
|
|
755
|
+
if (!usage)
|
|
756
|
+
return false;
|
|
757
|
+
if (optionalTokenCount(usage.input_tokens) === undefined
|
|
758
|
+
|| optionalTokenCount(usage.output_tokens) === undefined) {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
const optionalKeys = [
|
|
762
|
+
'cached_input_tokens',
|
|
763
|
+
'cache_write_input_tokens',
|
|
764
|
+
'reasoning_output_tokens',
|
|
765
|
+
'total_tokens',
|
|
766
|
+
];
|
|
767
|
+
return optionalKeys.every((key) => (usage[key] === undefined || optionalTokenCount(usage[key]) !== undefined));
|
|
768
|
+
}
|
|
769
|
+
function normalizeCodexTokenUsage(usage) {
|
|
770
|
+
const input = splitInclusiveInputTokens(usage.input_tokens, usage.cached_input_tokens, usage.cache_write_input_tokens);
|
|
771
|
+
return {
|
|
772
|
+
...input,
|
|
773
|
+
outputTokens: tokenCount(usage.output_tokens),
|
|
774
|
+
reasoningTokens: optionalTokenCount(usage.reasoning_output_tokens),
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
function codexEntrypoint(metaPayload) {
|
|
778
|
+
const originator = stringValue(metaPayload.originator)?.toLowerCase().trim() ?? '';
|
|
779
|
+
if (!originator)
|
|
780
|
+
return undefined;
|
|
781
|
+
if (originator.includes('desktop'))
|
|
782
|
+
return 'codex-desktop';
|
|
783
|
+
if (originator.includes('vscode'))
|
|
784
|
+
return 'codex-vscode';
|
|
785
|
+
if (originator.includes('sdk'))
|
|
786
|
+
return 'codex-sdk';
|
|
787
|
+
if (originator === 'claudian')
|
|
788
|
+
return 'claudian';
|
|
789
|
+
if (/(?:^|[-_ ])(?:cli|tui|exec)(?:$|[-_ ])/.test(originator))
|
|
790
|
+
return 'codex-cli';
|
|
791
|
+
const normalized = originator.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
792
|
+
return normalized ? `codex-${normalized}` : undefined;
|
|
793
|
+
}
|
|
794
|
+
function codexSubagentKind(metaPayload) {
|
|
795
|
+
const source = isObject(metaPayload.source) ? metaPayload.source : {};
|
|
796
|
+
const subagent = source.subagent;
|
|
797
|
+
if (typeof subagent === 'string')
|
|
798
|
+
return stringValue(subagent);
|
|
799
|
+
return isObject(subagent) ? stringValue(subagent.other) : undefined;
|
|
800
|
+
}
|
|
801
|
+
function indexDuplicateEventMessages(records) {
|
|
802
|
+
const duplicateIndexes = new Set();
|
|
803
|
+
records.forEach((value, sourceIndex) => {
|
|
804
|
+
const record = asCodexRecord(value);
|
|
805
|
+
if (record?.type !== 'event_msg')
|
|
806
|
+
return;
|
|
807
|
+
const payload = isObject(record.payload) ? record.payload : {};
|
|
808
|
+
const payloadType = stringValue(payload.type);
|
|
809
|
+
const role = payloadType === 'user_message'
|
|
810
|
+
? 'user'
|
|
811
|
+
: payloadType === 'agent_message' ? 'assistant' : undefined;
|
|
812
|
+
const text = stringValue(payload.message);
|
|
813
|
+
if (!role || !text)
|
|
814
|
+
return;
|
|
815
|
+
const nearbyRecords = [-1, 1].flatMap((direction) => {
|
|
816
|
+
let candidateIndex = sourceIndex + direction;
|
|
817
|
+
while (candidateIndex >= 0 && candidateIndex < records.length) {
|
|
818
|
+
const candidate = asCodexRecord(records[candidateIndex]);
|
|
819
|
+
if (candidate?.type !== 'session_meta')
|
|
820
|
+
return [records[candidateIndex]];
|
|
821
|
+
candidateIndex += direction;
|
|
822
|
+
}
|
|
823
|
+
return [];
|
|
824
|
+
});
|
|
825
|
+
const mirrored = nearbyRecords.some((candidate) => {
|
|
826
|
+
const adjacent = asCodexRecord(candidate);
|
|
827
|
+
if (adjacent?.type !== 'response_item')
|
|
828
|
+
return false;
|
|
829
|
+
const adjacentPayload = isObject(adjacent.payload) ? adjacent.payload : {};
|
|
830
|
+
return adjacentPayload.type === 'message'
|
|
831
|
+
&& adjacentPayload.role === role
|
|
832
|
+
&& codexContentText(adjacentPayload.content) === text;
|
|
833
|
+
});
|
|
834
|
+
if (mirrored)
|
|
835
|
+
duplicateIndexes.add(sourceIndex);
|
|
836
|
+
});
|
|
837
|
+
return duplicateIndexes;
|
|
838
|
+
}
|
|
839
|
+
function asCodexRecord(value) {
|
|
840
|
+
return isObject(value) ? value : undefined;
|
|
841
|
+
}
|
|
842
|
+
function stringValue(value) {
|
|
843
|
+
return typeof value === 'string' && value.trim() ? value : undefined;
|
|
844
|
+
}
|
|
845
|
+
function booleanValue(value) {
|
|
846
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
847
|
+
}
|
|
848
|
+
function isObject(value) {
|
|
849
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
850
|
+
}
|