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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { safeSliceForJson } from '../util/safe-slice.js';
|
|
2
|
+
import { normalizeToolIdentity } from '../shared/tool-identity.js';
|
|
1
3
|
// Codex CLI(codex 0.125)`exec --json` 事件流 → omk trace 抽取器。
|
|
2
4
|
// 跟 claude-sdk-trace.ts 不同源:
|
|
3
5
|
// Claude SDK 是 message[block] 嵌套结构,有 tool_use / tool_result 配对;
|
|
@@ -7,45 +9,134 @@
|
|
|
7
9
|
// schema(基于 codex 0.125 实测,fixture 测试锁住假设;漂移时 fixture 先红):
|
|
8
10
|
// - {type:'thread.started' | 'turn.started'} 划 turn 边界
|
|
9
11
|
// - {type:'turn.completed', usage} / {type:'turn.failed', error} 收尾
|
|
10
|
-
// - {type:'item.started', item:{type, status:'in_progress', ...}}
|
|
12
|
+
// - {type:'item.started', item:{type, status:'in_progress', ...}} 暂存;缺 completed 时保留为 unknown
|
|
11
13
|
// - {type:'item.completed', item:{type:'agent_message', text:'...'}} → assistant 文本
|
|
12
14
|
// - {type:'item.completed', item:{type:'command_execution', command, aggregated_output, exit_code, status}} → ToolCallInfo
|
|
13
|
-
// - 其他 item.type(file_read/file_write/web_search 等)best-effort 抽 input/output
|
|
14
|
-
//
|
|
15
|
+
// - 其他 item.type(file_read/file_write/web_search/agent tools 等)best-effort 抽 input/output
|
|
16
|
+
// 子代理统计只认事件流里的具体 spawn 工具事实;旧协议没有这类事件时自然为 0。
|
|
15
17
|
const TOOL_INPUT_LIMIT = 500;
|
|
16
18
|
const TOOL_OUTPUT_LIMIT = 500;
|
|
17
19
|
export function isCodexResultEvent(event) {
|
|
18
20
|
return event.type === 'turn.completed' || event.type === 'turn.failed';
|
|
19
21
|
}
|
|
20
22
|
function extractToolInput(item, itemType) {
|
|
21
|
-
if (itemType === 'command_execution')
|
|
22
|
-
return (item.command || ''
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
if (itemType === 'command_execution') {
|
|
24
|
+
return { command: safeSliceForJson(item.command || '', TOOL_INPUT_LIMIT, '') };
|
|
25
|
+
}
|
|
26
|
+
if (itemType === 'file_read' || itemType === 'file_write') {
|
|
27
|
+
return { file_path: item.path || null };
|
|
28
|
+
}
|
|
29
|
+
if (itemType === 'file_change') {
|
|
30
|
+
return item.changes?.slice(0, 50).map((change) => ({
|
|
31
|
+
path: safeSliceForJson(change.path || '', TOOL_INPUT_LIMIT, ''),
|
|
32
|
+
...(change.changeKind && { kind: change.changeKind }),
|
|
33
|
+
})) ?? [];
|
|
34
|
+
}
|
|
25
35
|
if (itemType === 'web_search')
|
|
26
|
-
return item.query || null;
|
|
36
|
+
return { query: item.query || null };
|
|
37
|
+
if (itemType === 'mcp_tool_call')
|
|
38
|
+
return item.arguments ?? null;
|
|
27
39
|
return null;
|
|
28
40
|
}
|
|
41
|
+
function stringifyToolValue(value) {
|
|
42
|
+
try {
|
|
43
|
+
const json = JSON.stringify(value);
|
|
44
|
+
return safeSliceForJson(json ?? String(value), TOOL_OUTPUT_LIMIT, '');
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return safeSliceForJson(String(value), TOOL_OUTPUT_LIMIT, '');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
29
50
|
function extractToolOutput(item, itemType) {
|
|
30
51
|
if (itemType === 'command_execution' && item.aggregated_output) {
|
|
31
|
-
return item.aggregated_output
|
|
52
|
+
return safeSliceForJson(item.aggregated_output, TOOL_OUTPUT_LIMIT, '');
|
|
32
53
|
}
|
|
33
54
|
if (itemType === 'file_read' && item.content)
|
|
34
|
-
return item.content
|
|
55
|
+
return safeSliceForJson(item.content, TOOL_OUTPUT_LIMIT, '');
|
|
56
|
+
if (itemType === 'file_change')
|
|
57
|
+
return item.status || '';
|
|
58
|
+
if (itemType === 'mcp_tool_call') {
|
|
59
|
+
if (item.error?.message)
|
|
60
|
+
return safeSliceForJson(item.error.message, TOOL_OUTPUT_LIMIT, '');
|
|
61
|
+
if (item.result !== undefined)
|
|
62
|
+
return stringifyToolValue(item.result);
|
|
63
|
+
}
|
|
64
|
+
if (itemType === 'error' && item.message)
|
|
65
|
+
return safeSliceForJson(item.message, TOOL_OUTPUT_LIMIT, '');
|
|
35
66
|
if (item.results)
|
|
36
|
-
return
|
|
67
|
+
return stringifyToolValue(item.results);
|
|
37
68
|
return '';
|
|
38
69
|
}
|
|
70
|
+
function codexItemStatus(event, item) {
|
|
71
|
+
if (event.error
|
|
72
|
+
|| item.error
|
|
73
|
+
|| item.type === 'error'
|
|
74
|
+
|| (typeof item.exit_code === 'number' && item.exit_code !== 0))
|
|
75
|
+
return 'failure';
|
|
76
|
+
const status = item.status?.toLowerCase();
|
|
77
|
+
if (status === 'failed' || status === 'error')
|
|
78
|
+
return 'failure';
|
|
79
|
+
if (status === 'cancelled' || status === 'canceled')
|
|
80
|
+
return 'cancelled';
|
|
81
|
+
if (status === 'completed'
|
|
82
|
+
|| status === 'complete'
|
|
83
|
+
|| status === 'success'
|
|
84
|
+
|| status === 'succeeded')
|
|
85
|
+
return 'success';
|
|
86
|
+
if (status)
|
|
87
|
+
return 'unknown';
|
|
88
|
+
if (typeof item.exit_code === 'number' && item.exit_code === 0)
|
|
89
|
+
return 'success';
|
|
90
|
+
// `item.completed` is the legacy success signal when older Codex events omit
|
|
91
|
+
// item.status. Keep that compatibility while preferring explicit status above.
|
|
92
|
+
return 'success';
|
|
93
|
+
}
|
|
39
94
|
export function extractCodexTrace(events) {
|
|
40
95
|
const turns = [];
|
|
41
96
|
const toolCalls = [];
|
|
42
97
|
let fullNumTurns = 0;
|
|
43
|
-
|
|
44
|
-
const numSubAgents = 0;
|
|
98
|
+
let numSubAgents = 0;
|
|
45
99
|
let currentTurnText = '';
|
|
46
100
|
let currentTurnTools = [];
|
|
47
101
|
let currentTurnHasContent = false;
|
|
48
102
|
let lastTurnTs;
|
|
103
|
+
const pendingItems = new Map();
|
|
104
|
+
const appendToolCall = (item, status, statusSource, callInstanceId) => {
|
|
105
|
+
const itemType = item.type;
|
|
106
|
+
if (!itemType || isNonToolItem(itemType))
|
|
107
|
+
return;
|
|
108
|
+
if (isAgentSpawnItem(item))
|
|
109
|
+
numSubAgents += 1;
|
|
110
|
+
const toolIdentity = normalizeToolIdentity({
|
|
111
|
+
sourceName: itemType,
|
|
112
|
+
provider: itemType === 'mcp_tool_call' ? item.server : undefined,
|
|
113
|
+
authoritativeName: itemType === 'mcp_tool_call' ? item.tool ?? item.name : undefined,
|
|
114
|
+
});
|
|
115
|
+
const tc = {
|
|
116
|
+
tool: toolIdentity.name,
|
|
117
|
+
...(toolIdentity.sourceName ? { sourceTool: toolIdentity.sourceName } : {}),
|
|
118
|
+
...(toolIdentity.namespace ? { toolNamespace: toolIdentity.namespace } : {}),
|
|
119
|
+
...(toolIdentity.provider ? { toolProvider: toolIdentity.provider } : {}),
|
|
120
|
+
input: extractToolInput(item, itemType),
|
|
121
|
+
output: extractToolOutput(item, itemType),
|
|
122
|
+
status,
|
|
123
|
+
statusSource,
|
|
124
|
+
success: status === 'success',
|
|
125
|
+
callInstanceId,
|
|
126
|
+
toolUseId: item.id,
|
|
127
|
+
};
|
|
128
|
+
currentTurnTools.push(tc);
|
|
129
|
+
toolCalls.push(tc);
|
|
130
|
+
currentTurnHasContent = true;
|
|
131
|
+
};
|
|
132
|
+
const materializePendingItems = () => {
|
|
133
|
+
for (const queue of pendingItems.values()) {
|
|
134
|
+
for (const pending of queue) {
|
|
135
|
+
appendToolCall(pending.item, 'unknown', 'unknown', pending.callInstanceId);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
pendingItems.clear();
|
|
139
|
+
};
|
|
49
140
|
const flushTurn = (closingEvent) => {
|
|
50
141
|
if (!currentTurnHasContent && currentTurnTools.length === 0)
|
|
51
142
|
return;
|
|
@@ -65,11 +156,13 @@ export function extractCodexTrace(events) {
|
|
|
65
156
|
if (closingEvent?.ts)
|
|
66
157
|
lastTurnTs = closingEvent.ts;
|
|
67
158
|
};
|
|
68
|
-
for (
|
|
159
|
+
for (let eventIndex = 0; eventIndex < events.length; eventIndex += 1) {
|
|
160
|
+
const event = events[eventIndex];
|
|
69
161
|
const t = event.type;
|
|
70
162
|
if (!t)
|
|
71
163
|
continue;
|
|
72
164
|
if (t === 'thread.started' || t === 'turn.started') {
|
|
165
|
+
materializePendingItems();
|
|
73
166
|
if (currentTurnHasContent || currentTurnTools.length > 0)
|
|
74
167
|
flushTurn();
|
|
75
168
|
if (event.ts)
|
|
@@ -77,25 +170,59 @@ export function extractCodexTrace(events) {
|
|
|
77
170
|
continue;
|
|
78
171
|
}
|
|
79
172
|
if (t === 'turn.completed') {
|
|
173
|
+
materializePendingItems();
|
|
80
174
|
flushTurn(event);
|
|
81
175
|
continue;
|
|
82
176
|
}
|
|
83
177
|
if (t === 'turn.failed') {
|
|
84
|
-
|
|
85
|
-
currentTurnTools[currentTurnTools.length - 1].success = false;
|
|
86
|
-
}
|
|
178
|
+
materializePendingItems();
|
|
87
179
|
flushTurn(event);
|
|
88
180
|
continue;
|
|
89
181
|
}
|
|
90
|
-
|
|
182
|
+
if (t === 'item.started') {
|
|
183
|
+
const item = event.item;
|
|
184
|
+
if (item?.id && item.type && item.type !== 'agent_message') {
|
|
185
|
+
const pending = pendingItems.get(item.id) ?? [];
|
|
186
|
+
pending.push({
|
|
187
|
+
item,
|
|
188
|
+
callInstanceId: `codex-exec:${eventIndex}:started`,
|
|
189
|
+
});
|
|
190
|
+
pendingItems.set(item.id, pending);
|
|
191
|
+
}
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (t === 'item.updated') {
|
|
195
|
+
const item = event.item;
|
|
196
|
+
if (item?.id && item.type && item.type !== 'agent_message') {
|
|
197
|
+
const pending = pendingItems.get(item.id);
|
|
198
|
+
const current = pending?.at(-1);
|
|
199
|
+
if (current) {
|
|
200
|
+
current.item = { ...current.item, ...item };
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
pendingItems.set(item.id, [{
|
|
204
|
+
item,
|
|
205
|
+
callInstanceId: `codex-exec:${eventIndex}:updated`,
|
|
206
|
+
}]);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
91
211
|
if (t !== 'item.completed')
|
|
92
212
|
continue;
|
|
93
213
|
const item = event.item;
|
|
94
214
|
if (!item || !item.type)
|
|
95
215
|
continue;
|
|
96
|
-
const
|
|
216
|
+
const pendingQueue = item.id ? pendingItems.get(item.id) : undefined;
|
|
217
|
+
const started = pendingQueue?.shift();
|
|
218
|
+
if (item.id && pendingQueue?.length === 0)
|
|
219
|
+
pendingItems.delete(item.id);
|
|
220
|
+
const completedItem = started
|
|
221
|
+
? { ...started.item, ...item, status: item.status }
|
|
222
|
+
: item;
|
|
223
|
+
const itemType = completedItem.type;
|
|
97
224
|
if (itemType === 'agent_message') {
|
|
98
|
-
const txt =
|
|
225
|
+
const txt = completedItem.text || '';
|
|
99
226
|
if (txt) {
|
|
100
227
|
if (currentTurnText)
|
|
101
228
|
currentTurnText += '\n';
|
|
@@ -104,19 +231,27 @@ export function extractCodexTrace(events) {
|
|
|
104
231
|
}
|
|
105
232
|
continue;
|
|
106
233
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const tc = {
|
|
110
|
-
tool: itemType,
|
|
111
|
-
input: extractToolInput(item, itemType),
|
|
112
|
-
output: extractToolOutput(item, itemType),
|
|
113
|
-
success: !isError,
|
|
114
|
-
};
|
|
115
|
-
currentTurnTools.push(tc);
|
|
116
|
-
toolCalls.push(tc);
|
|
117
|
-
currentTurnHasContent = true;
|
|
234
|
+
const status = codexItemStatus(event, completedItem);
|
|
235
|
+
appendToolCall(completedItem, status, 'runtime', started?.callInstanceId ?? `codex-exec:${eventIndex}:completed`);
|
|
118
236
|
}
|
|
119
237
|
// 流末尾 flush(防止最后一个 turn 没 turn.completed 收尾)
|
|
238
|
+
materializePendingItems();
|
|
120
239
|
flushTurn();
|
|
121
240
|
return { turns, toolCalls, fullNumTurns, numSubAgents };
|
|
122
241
|
}
|
|
242
|
+
function isAgentSpawnItem(item) {
|
|
243
|
+
const identity = [
|
|
244
|
+
item.type,
|
|
245
|
+
item.server,
|
|
246
|
+
item.tool,
|
|
247
|
+
item.name,
|
|
248
|
+
].filter((value) => typeof value === 'string').join(' ').toLowerCase();
|
|
249
|
+
return /(?:^|[^a-z0-9])(?:spawn|create|start)[_-]?(?:sub)?agent(?:$|[^a-z0-9])/.test(identity)
|
|
250
|
+
|| /^(?:agent|subagent|task)$/.test(item.type?.toLowerCase() ?? '');
|
|
251
|
+
}
|
|
252
|
+
function isNonToolItem(itemType) {
|
|
253
|
+
return itemType === 'agent_message'
|
|
254
|
+
|| itemType === 'reasoning'
|
|
255
|
+
|| itemType === 'todo_list'
|
|
256
|
+
|| itemType === 'error';
|
|
257
|
+
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import type { ExecResult, ExecutorInput } from '../types/index.js';
|
|
2
2
|
import type { CodexEvent } from './shared.js';
|
|
3
|
+
export { extractCodexFinalOutput, extractCodexProtocolError, extractCodexStopReason, extractCodexUsage, sumCodexElapsed, } from './codex-protocol.js';
|
|
3
4
|
export declare function isolateCodexCwd(allowedSkills: string[] | undefined, cwd: string | null | undefined, executorName?: string): void;
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export declare function extractCodexFinalOutput(events: CodexEvent[]): string;
|
|
10
|
-
export declare function extractCodexStopReason(events: CodexEvent[]): string;
|
|
11
|
-
export declare function sumCodexElapsed(resultEvents: CodexEvent[], wallClock: number): number;
|
|
5
|
+
export interface CodexJsonlParseResult {
|
|
6
|
+
events: CodexEvent[];
|
|
7
|
+
malformedLineCount: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function parseCodexJsonl(stdout: string): CodexJsonlParseResult;
|
|
12
10
|
export declare function buildCodexArgs({ model, cwd, prompt }: {
|
|
13
11
|
model: string;
|
|
14
12
|
cwd?: string | null;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { buildExecEnv, DEFAULT_TIMEOUT_MS, errorMessage, interruptedExecResult, MAX_BUFFER, spawnWithSigintPropagation, timeoutExecResult, } from './shared.js';
|
|
1
|
+
import { buildCodexResult } from './codex-protocol.js';
|
|
2
|
+
import { buildExecEnv, DEFAULT_TIMEOUT_MS, errorMessage, interruptedExecResult, MAX_BUFFER, normalizeCodexProtocolEvent, spawnWithSigintPropagation, timeoutExecResult, } from './shared.js';
|
|
3
|
+
export { extractCodexFinalOutput, extractCodexProtocolError, extractCodexStopReason, extractCodexUsage, sumCodexElapsed, } from './codex-protocol.js';
|
|
3
4
|
// codex CLI(0.125)隔离能力对比 claude-cli:
|
|
4
5
|
// Claude:三条 channel(SDK skills auto-discovery / subagent Skill 工具 /
|
|
5
6
|
// cwd 文件系统),`--disable-slash-commands` + `--disallowedTools Skill`
|
|
6
7
|
// 堵前两条,cwd 切空目录堵第三条
|
|
7
|
-
// Codex
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// Codex:CLI / SDK 的项目指令、skills 与可能的子线程都以 cwd 工作区为
|
|
9
|
+
// 发现边界;`-C/--cd` 是可验证的隔离控制面。因此把 cwd 切到
|
|
10
|
+
// 隔离空目录,统一阻断 AGENTS.md / .agents/skills/ 自动加载。
|
|
10
11
|
//
|
|
11
12
|
// undefined → 不传 -C(原行为,看 cwd 里有什么 codex 自己决定)
|
|
12
13
|
// [] → 必须提供 cwd 非空(否则 throw),caller 应传一个
|
|
@@ -19,11 +20,10 @@ export function isolateCodexCwd(allowedSkills, cwd, executorName = 'codex-cli')
|
|
|
19
20
|
throw new Error(`skill 白名单(allowedSkills=${JSON.stringify(allowedSkills)})不再支持:非空白名单无法真正隔离。\n`
|
|
20
21
|
+ ` 仅支持 [](强制 cwd 隔离,需提供 cwd 非空,全封死)或 undefined(不隔离)。`);
|
|
21
22
|
}
|
|
22
|
-
// allowedSkills === [] 时必须有 cwd
|
|
23
|
+
// allowedSkills === [] 时必须有 cwd;工作区隔离是 Codex 的控制面。
|
|
23
24
|
if (!cwd) {
|
|
24
|
-
throw new Error(`${executorName} executor allowedSkills=[] 需要提供 cwd
|
|
25
|
-
+ '
|
|
26
|
-
+ ' cwd 文件系统隔离是它唯一能堵 AGENTS.md / .agents/skills/ 自动加载的途径。\n'
|
|
25
|
+
throw new Error(`${executorName} executor allowedSkills=[] 需要提供 cwd 非空(工作区隔离)。\n`
|
|
26
|
+
+ ' 主线程与可能的子线程都会继承工作区上下文;必须用隔离 cwd 阻断 AGENTS.md / .agents/skills/ 自动加载。\n'
|
|
27
27
|
+ ' caller 应传一个 isolated 空目录(如 ~/.oh-my-knowledge/state/isolated-cwd/)。');
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -32,67 +32,37 @@ export function isolateCodexCwd(allowedSkills, cwd, executorName = 'codex-cli')
|
|
|
32
32
|
// 每次都打两行会让 stderr 被同样文本刷屏(140-280 行)。模块级 flag 一个 process 只打一次。
|
|
33
33
|
let hasWarnedSystem = false;
|
|
34
34
|
let hasWarnedCost = false;
|
|
35
|
-
function parseCodexJsonl(stdout) {
|
|
35
|
+
export function parseCodexJsonl(stdout) {
|
|
36
36
|
const events = [];
|
|
37
|
+
let malformedLineCount = 0;
|
|
37
38
|
for (const line of stdout.split('\n')) {
|
|
38
39
|
const trimmed = line.trim();
|
|
39
40
|
if (!trimmed)
|
|
40
41
|
continue;
|
|
41
42
|
try {
|
|
42
|
-
|
|
43
|
+
const value = JSON.parse(trimmed);
|
|
44
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
45
|
+
malformedLineCount += 1;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const event = normalizeCodexProtocolEvent(value);
|
|
49
|
+
if (!event
|
|
50
|
+
|| typeof event.type !== 'string'
|
|
51
|
+
|| event.type.trim() === ''
|
|
52
|
+
|| (event.type.startsWith('item.')
|
|
53
|
+
&& (!event.item
|
|
54
|
+
|| typeof event.item.type !== 'string'
|
|
55
|
+
|| event.item.type.trim() === ''))) {
|
|
56
|
+
malformedLineCount += 1;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
events.push(event);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
malformedLineCount += 1;
|
|
43
63
|
}
|
|
44
|
-
catch { /* skip non-JSON lines */ }
|
|
45
|
-
}
|
|
46
|
-
return events;
|
|
47
|
-
}
|
|
48
|
-
export function extractCodexUsage(events) {
|
|
49
|
-
let input = 0;
|
|
50
|
-
let cached = 0;
|
|
51
|
-
let output = 0;
|
|
52
|
-
for (const e of events) {
|
|
53
|
-
if (!isCodexResultEvent(e) || !e.usage)
|
|
54
|
-
continue;
|
|
55
|
-
input += e.usage.input_tokens || 0;
|
|
56
|
-
cached += e.usage.cached_input_tokens || 0;
|
|
57
|
-
output += e.usage.output_tokens || 0;
|
|
58
|
-
}
|
|
59
|
-
return { input, cached, output };
|
|
60
|
-
}
|
|
61
|
-
// exported for unit tests
|
|
62
|
-
export function extractCodexFinalOutput(events) {
|
|
63
|
-
// 抓 item.completed + item.type='agent_message' 的 text 全部拼起来。
|
|
64
|
-
// codex 同 turn 内可能 emit 多条 agent_message(如 "step 1" / "step 2" / "final answer"),
|
|
65
|
-
// 取最后一条会让 grader / assertion / LLM judge 只看到最后片段,跟 extractCodexTrace
|
|
66
|
-
// 把多条拼到 turn.content 的语义不一致(test/executors/codex-cli-trace.test.ts:120-130 锁住)。
|
|
67
|
-
// 一致化:final output 也拼,跟 trace UI 看到的内容对齐。
|
|
68
|
-
const allTexts = [];
|
|
69
|
-
for (const e of events) {
|
|
70
|
-
if (e.type !== 'item.completed')
|
|
71
|
-
continue;
|
|
72
|
-
if (e.item?.type !== 'agent_message')
|
|
73
|
-
continue;
|
|
74
|
-
const txt = e.item.text;
|
|
75
|
-
if (txt)
|
|
76
|
-
allTexts.push(txt);
|
|
77
64
|
}
|
|
78
|
-
return
|
|
79
|
-
}
|
|
80
|
-
export function extractCodexStopReason(events) {
|
|
81
|
-
const last = [...events].reverse().find((e) => isCodexResultEvent(e));
|
|
82
|
-
if (!last)
|
|
83
|
-
return 'unknown';
|
|
84
|
-
if (last.type === 'turn.failed')
|
|
85
|
-
return 'error';
|
|
86
|
-
return last.stop_reason || 'end_turn';
|
|
87
|
-
}
|
|
88
|
-
// codex 每个 turn.completed 携带本 turn 的 elapsed_ms(per-turn delta),
|
|
89
|
-
// 跟 extractCodexUsage 把 token 累加多 turn 是同一语义假设(events 是 per-turn delta,
|
|
90
|
-
// 不是 cumulative)。multi-turn 只取最后一条会漏算前面 turn 的耗时。fallback:
|
|
91
|
-
// 累加为 0 / 全部缺失时退到 wall-clock,避免 elapsed_ms===0 单 turn 抹平 duration。
|
|
92
|
-
// exported for unit tests
|
|
93
|
-
export function sumCodexElapsed(resultEvents, wallClock) {
|
|
94
|
-
const total = resultEvents.reduce((s, e) => s + (e.elapsed_ms ?? 0), 0);
|
|
95
|
-
return total > 0 ? total : wallClock;
|
|
65
|
+
return { events, malformedLineCount };
|
|
96
66
|
}
|
|
97
67
|
// exported for arg-shape regression tests
|
|
98
68
|
export function buildCodexArgs({ model, cwd, prompt }) {
|
|
@@ -104,6 +74,7 @@ export function buildCodexArgs({ model, cwd, prompt }) {
|
|
|
104
74
|
'--json',
|
|
105
75
|
'--ephemeral', // 不持久化 session 文件
|
|
106
76
|
'--ignore-user-config', // 不读 $CODEX_HOME/config.toml
|
|
77
|
+
'--ignore-rules', // 不让用户 / 项目 execpolicy 改写工具行为
|
|
107
78
|
'--skip-git-repo-check', // 允许 isolated cwd 不是 git 仓库
|
|
108
79
|
'--sandbox', 'read-only', // 评测场景不需要写文件
|
|
109
80
|
'-c', 'approval_policy="never"', // non-interactive 必须;TOML 字符串需要 quote
|
|
@@ -155,53 +126,13 @@ export async function codexCliExecutor({ model, system, prompt, cwd, skillDir, t
|
|
|
155
126
|
...(cwd && { cwd }),
|
|
156
127
|
});
|
|
157
128
|
const durationMs = Date.now() - start;
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
durationApiMs: 0,
|
|
166
|
-
inputTokens: 0,
|
|
167
|
-
outputTokens: 0,
|
|
168
|
-
cacheReadTokens: 0,
|
|
169
|
-
cacheCreationTokens: 0,
|
|
170
|
-
costUSD: 0,
|
|
171
|
-
costReportedByExecutor: false,
|
|
172
|
-
output: null,
|
|
173
|
-
stopReason: 'error',
|
|
174
|
-
numTurns: 0,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
const last = resultEvents[resultEvents.length - 1];
|
|
178
|
-
const usage = extractCodexUsage(events);
|
|
179
|
-
const trace = extractCodexTrace(events);
|
|
180
|
-
const stopReason = extractCodexStopReason(events);
|
|
181
|
-
const finalOutput = extractCodexFinalOutput(events);
|
|
182
|
-
const ok = last.type !== 'turn.failed' && !last.error;
|
|
183
|
-
return {
|
|
184
|
-
ok,
|
|
185
|
-
// multi-turn 累加 elapsed_ms(per-turn delta);全 0 fallback wall-clock。详见 sumCodexElapsed
|
|
186
|
-
durationMs: sumCodexElapsed(resultEvents, durationMs),
|
|
187
|
-
durationApiMs: 0, // codex 不报 API duration
|
|
188
|
-
inputTokens: usage.input,
|
|
189
|
-
outputTokens: usage.output,
|
|
190
|
-
cacheReadTokens: usage.cached,
|
|
191
|
-
cacheCreationTokens: 0, // codex 不报 cache creation
|
|
192
|
-
costUSD: 0, // 占位:codex 0.125 binary 不输出 cost
|
|
193
|
-
costReportedByExecutor: false, // renderer 据此显示「未报告」而非 $0.0000
|
|
194
|
-
output: finalOutput,
|
|
195
|
-
stopReason,
|
|
196
|
-
// success path:ok=false(turn.failed)时透传 last.error.message 给 caller 看到
|
|
197
|
-
// 失败原因。catch path 历史上已经透传(对称化)。
|
|
198
|
-
...(!ok && { error: last.error?.message || 'codex turn.failed' }),
|
|
199
|
-
numTurns: resultEvents.length,
|
|
200
|
-
fullNumTurns: trace.fullNumTurns,
|
|
201
|
-
numSubAgents: trace.numSubAgents,
|
|
202
|
-
...(trace.turns.length > 0 && { turns: trace.turns }),
|
|
203
|
-
...(trace.toolCalls.length > 0 && { toolCalls: trace.toolCalls }),
|
|
204
|
-
};
|
|
129
|
+
const parsed = parseCodexJsonl(stdout);
|
|
130
|
+
return buildCodexResult({
|
|
131
|
+
events: parsed.events,
|
|
132
|
+
wallClockDurationMs: durationMs,
|
|
133
|
+
source: 'codex --json',
|
|
134
|
+
malformedLineCount: parsed.malformedLineCount,
|
|
135
|
+
});
|
|
205
136
|
}
|
|
206
137
|
catch (err) {
|
|
207
138
|
const durationMs = Date.now() - start;
|
|
@@ -210,47 +141,14 @@ export async function codexCliExecutor({ model, system, prompt, cwd, skillDir, t
|
|
|
210
141
|
return { ...timeoutExecResult(timeoutMs, durationMs), costReportedByExecutor: false };
|
|
211
142
|
if (details.killedBySignal)
|
|
212
143
|
return { ...interruptedExecResult(durationMs), costReportedByExecutor: false };
|
|
213
|
-
//
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
error: last.error?.message || errorMessage(err),
|
|
223
|
-
durationMs: sumCodexElapsed(resultEvents, durationMs),
|
|
224
|
-
durationApiMs: 0,
|
|
225
|
-
inputTokens: usage.input,
|
|
226
|
-
outputTokens: usage.output,
|
|
227
|
-
cacheReadTokens: usage.cached,
|
|
228
|
-
cacheCreationTokens: 0,
|
|
229
|
-
costUSD: 0,
|
|
230
|
-
costReportedByExecutor: false,
|
|
231
|
-
output: extractCodexFinalOutput(events) || null,
|
|
232
|
-
stopReason: 'error',
|
|
233
|
-
numTurns: resultEvents.length,
|
|
234
|
-
fullNumTurns: trace.fullNumTurns,
|
|
235
|
-
numSubAgents: trace.numSubAgents,
|
|
236
|
-
...(trace.turns.length > 0 && { turns: trace.turns }),
|
|
237
|
-
...(trace.toolCalls.length > 0 && { toolCalls: trace.toolCalls }),
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
return {
|
|
241
|
-
ok: false,
|
|
242
|
-
error: errorMessage(err),
|
|
243
|
-
durationMs,
|
|
244
|
-
durationApiMs: 0,
|
|
245
|
-
inputTokens: 0,
|
|
246
|
-
outputTokens: 0,
|
|
247
|
-
cacheReadTokens: 0,
|
|
248
|
-
cacheCreationTokens: 0,
|
|
249
|
-
costUSD: 0,
|
|
250
|
-
costReportedByExecutor: false,
|
|
251
|
-
output: null,
|
|
252
|
-
stopReason: 'error',
|
|
253
|
-
numTurns: 0,
|
|
254
|
-
};
|
|
144
|
+
// 失败路径仍保留已完成的事件、用量与 trace,但不能反转为成功。
|
|
145
|
+
const parsed = parseCodexJsonl(details.stdout || '');
|
|
146
|
+
return buildCodexResult({
|
|
147
|
+
events: parsed.events,
|
|
148
|
+
wallClockDurationMs: durationMs,
|
|
149
|
+
source: 'codex --json',
|
|
150
|
+
malformedLineCount: parsed.malformedLineCount,
|
|
151
|
+
forcedError: details.stderr?.trim() || errorMessage(err),
|
|
152
|
+
});
|
|
255
153
|
}
|
|
256
154
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ExecResult } from '../types/index.js';
|
|
2
|
+
import type { CodexEvent } from './shared.js';
|
|
3
|
+
export declare function extractCodexUsage(events: CodexEvent[]): {
|
|
4
|
+
input: number;
|
|
5
|
+
cached: number;
|
|
6
|
+
output: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Match @openai/codex-sdk's `finalResponse`: the latest completed
|
|
10
|
+
* agent_message is the answer. Earlier messages remain available in `turns`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function extractCodexFinalOutput(events: CodexEvent[]): string;
|
|
13
|
+
export declare function extractCodexProtocolError(events: CodexEvent[]): string | undefined;
|
|
14
|
+
export declare function validateCodexProtocol(events: CodexEvent[]): string | undefined;
|
|
15
|
+
export declare function extractCodexStopReason(events: CodexEvent[]): string;
|
|
16
|
+
export declare function sumCodexElapsed(resultEvents: CodexEvent[], wallClock: number): number;
|
|
17
|
+
export interface BuildCodexResultOptions {
|
|
18
|
+
events: CodexEvent[];
|
|
19
|
+
wallClockDurationMs: number;
|
|
20
|
+
source: 'codex --json' | 'codex-sdk';
|
|
21
|
+
malformedLineCount?: number;
|
|
22
|
+
forcedError?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function buildCodexResult({ events, wallClockDurationMs, source, malformedLineCount, forcedError, }: BuildCodexResultOptions): ExecResult;
|