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.
Files changed (243) hide show
  1. package/README.md +50 -18
  2. package/README.zh.md +55 -23
  3. package/dist/analysis/coverage-analyzer.d.ts +1 -0
  4. package/dist/analysis/coverage-analyzer.js +125 -62
  5. package/dist/analysis/failure-clusterer.js +2 -1
  6. package/dist/analysis/gap-analyzer.d.ts +2 -2
  7. package/dist/analysis/gap-analyzer.js +13 -3
  8. package/dist/analysis/hedging-classifier.d.ts +2 -2
  9. package/dist/analysis/hedging-classifier.js +3 -4
  10. package/dist/analysis/report-diagnostics.js +9 -7
  11. package/dist/analysis/sample-diagnostics.js +6 -6
  12. package/dist/artifact-graph/doctor.js +15 -7
  13. package/dist/assets/agent-skills/omk/SKILL.md +27 -7
  14. package/dist/assets/agent-skills/omk/references/commands.md +18 -17
  15. package/dist/authoring/evolver.d.ts +10 -6
  16. package/dist/authoring/evolver.js +496 -83
  17. package/dist/authoring/generator.d.ts +3 -3
  18. package/dist/authoring/generator.js +5 -10
  19. package/dist/authoring/sample-fixer.d.ts +8 -6
  20. package/dist/authoring/sample-fixer.js +76 -5
  21. package/dist/cli/commands/doctor.js +31 -14
  22. package/dist/cli/commands/eval/index.d.ts +3 -0
  23. package/dist/cli/commands/eval/index.js +163 -17
  24. package/dist/cli/commands/evolve.d.ts +4 -4
  25. package/dist/cli/commands/evolve.js +27 -13
  26. package/dist/cli/commands/init.js +16 -3
  27. package/dist/cli/commands/observe/inbox.js +28 -21
  28. package/dist/cli/commands/observe/index.js +20 -11
  29. package/dist/cli/commands/observe/ingest.d.ts +3 -0
  30. package/dist/cli/commands/observe/ingest.js +30 -2
  31. package/dist/cli/commands/sample.d.ts +6 -3
  32. package/dist/cli/commands/sample.js +72 -68
  33. package/dist/cli/lib/codex-model-hint.d.ts +9 -0
  34. package/dist/cli/lib/codex-model-hint.js +45 -0
  35. package/dist/cli/lib/generation-failure-hint.d.ts +2 -0
  36. package/dist/cli/lib/generation-failure-hint.js +61 -0
  37. package/dist/cli/lib/i18n-dict/common.d.ts +1 -1
  38. package/dist/cli/lib/i18n-dict/common.js +4 -0
  39. package/dist/cli/lib/i18n-dict/gen.d.ts +1 -1
  40. package/dist/cli/lib/i18n-dict/gen.js +38 -6
  41. package/dist/cli/lib/i18n-dict/help.js +6 -6
  42. package/dist/cli/lib/i18n-dict/init.d.ts +1 -1
  43. package/dist/cli/lib/i18n-dict/init.js +13 -9
  44. package/dist/cli/lib/i18n-dict/run.d.ts +1 -1
  45. package/dist/cli/lib/i18n-dict/run.js +34 -2
  46. package/dist/cli/lib/llm-failure-classifier.d.ts +2 -0
  47. package/dist/cli/lib/llm-failure-classifier.js +8 -0
  48. package/dist/cli/lib/parse-run-config.d.ts +6 -5
  49. package/dist/cli/lib/parse-run-config.js +16 -9
  50. package/dist/cli/lib/runtime-defaults.d.ts +21 -0
  51. package/dist/cli/lib/runtime-defaults.js +79 -0
  52. package/dist/diagnosis/observe-mapper.js +14 -15
  53. package/dist/diagnosis/observe-producer.js +3 -1
  54. package/dist/diagnosis/studio-projection.js +14 -7
  55. package/dist/diagnosis/types.d.ts +2 -0
  56. package/dist/diagnosis/types.js +12 -0
  57. package/dist/doctor/endpoint-rule.js +2 -1
  58. package/dist/eval-core/artifact-file-names.js +18 -1
  59. package/dist/eval-core/artifact-index.d.ts +7 -11
  60. package/dist/eval-core/artifact-index.js +139 -80
  61. package/dist/eval-core/cache.d.ts +12 -3
  62. package/dist/eval-core/cache.js +89 -29
  63. package/dist/eval-core/comparability.js +10 -6
  64. package/dist/eval-core/evaluation-execution.d.ts +2 -1
  65. package/dist/eval-core/evaluation-execution.js +122 -37
  66. package/dist/eval-core/evaluation-job.d.ts +4 -1
  67. package/dist/eval-core/evaluation-job.js +4 -1
  68. package/dist/eval-core/evaluation-reporting.d.ts +15 -13
  69. package/dist/eval-core/evaluation-reporting.js +54 -52
  70. package/dist/eval-core/execution-strategy.d.ts +2 -0
  71. package/dist/eval-core/execution-strategy.js +11 -9
  72. package/dist/eval-core/fact-checker.js +15 -7
  73. package/dist/eval-core/holdout.js +3 -2
  74. package/dist/eval-core/judge-independence.d.ts +2 -2
  75. package/dist/eval-core/mock-hook.cjs +23 -6
  76. package/dist/eval-core/mocks-runtime.js +30 -8
  77. package/dist/eval-core/report-document.d.ts +12 -0
  78. package/dist/eval-core/report-document.js +1151 -0
  79. package/dist/eval-core/report-extensions.d.ts +4 -0
  80. package/dist/eval-core/report-extensions.js +500 -0
  81. package/dist/eval-core/report-file-migration.js +7 -2
  82. package/dist/eval-core/resume-compatibility.d.ts +31 -0
  83. package/dist/eval-core/resume-compatibility.js +141 -0
  84. package/dist/eval-core/sample-fingerprint.d.ts +12 -0
  85. package/dist/eval-core/sample-fingerprint.js +193 -0
  86. package/dist/eval-core/schema.js +86 -31
  87. package/dist/eval-core/verdict.d.ts +8 -4
  88. package/dist/eval-core/verdict.js +24 -10
  89. package/dist/eval-workflows/batch-evaluation-workflow.d.ts +2 -1
  90. package/dist/eval-workflows/batch-evaluation-workflow.js +25 -12
  91. package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.d.ts +10 -5
  92. package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.js +58 -21
  93. package/dist/eval-workflows/evaluation-pipeline/report-finalize.js +3 -1
  94. package/dist/eval-workflows/evaluation-pipeline/run-state.d.ts +4 -1
  95. package/dist/eval-workflows/evaluation-pipeline/run-state.js +4 -1
  96. package/dist/eval-workflows/evaluation-pipeline/test-set-hash.d.ts +6 -5
  97. package/dist/eval-workflows/evaluation-pipeline/test-set-hash.js +17 -10
  98. package/dist/eval-workflows/evaluation-pipeline.js +12 -7
  99. package/dist/eval-workflows/run-evaluation.d.ts +9 -7
  100. package/dist/eval-workflows/run-evaluation.js +79 -51
  101. package/dist/executors/anthropic-api.js +65 -9
  102. package/dist/executors/claude-cli.js +16 -79
  103. package/dist/executors/claude-protocol.d.ts +28 -0
  104. package/dist/executors/claude-protocol.js +180 -0
  105. package/dist/executors/claude-sdk-trace.js +56 -28
  106. package/dist/executors/claude-sdk.d.ts +1 -0
  107. package/dist/executors/claude-sdk.js +39 -93
  108. package/dist/executors/codex-cli-trace.js +166 -31
  109. package/dist/executors/codex-cli.d.ts +6 -8
  110. package/dist/executors/codex-cli.js +49 -151
  111. package/dist/executors/codex-protocol.d.ts +24 -0
  112. package/dist/executors/codex-protocol.js +234 -0
  113. package/dist/executors/codex-sdk.js +68 -120
  114. package/dist/executors/gemini.js +88 -13
  115. package/dist/executors/index.d.ts +2 -3
  116. package/dist/executors/index.js +5 -3
  117. package/dist/executors/openai-api.js +70 -9
  118. package/dist/executors/runtime-fingerprint.js +88 -11
  119. package/dist/executors/script-command.d.ts +8 -0
  120. package/dist/executors/script-command.js +87 -0
  121. package/dist/executors/script.js +202 -29
  122. package/dist/executors/shared.d.ts +35 -3
  123. package/dist/executors/shared.js +113 -15
  124. package/dist/grading/assertions.d.ts +1 -1
  125. package/dist/grading/assertions.js +19 -9
  126. package/dist/grading/diagnostic.d.ts +9 -2
  127. package/dist/grading/diagnostic.js +25 -2
  128. package/dist/grading/index.js +10 -4
  129. package/dist/grading/judge.js +19 -6
  130. package/dist/grading/layered-scores.d.ts +2 -3
  131. package/dist/grading/layered-scores.js +2 -3
  132. package/dist/inputs/load-samples.d.ts +1 -2
  133. package/dist/inputs/load-samples.js +23 -1
  134. package/dist/inputs/mcp-resolver.js +6 -3
  135. package/dist/inputs/sample-document.d.ts +11 -0
  136. package/dist/inputs/sample-document.js +96 -0
  137. package/dist/managed/evidence.d.ts +1 -0
  138. package/dist/managed/evidence.js +1 -1
  139. package/dist/managed/store.js +200 -91
  140. package/dist/observability/codex-trace-adapter.d.ts +5 -0
  141. package/dist/observability/codex-trace-adapter.js +850 -0
  142. package/dist/observability/experience.d.ts +32 -6
  143. package/dist/observability/experience.js +2695 -459
  144. package/dist/observability/feedback-matchers.js +16 -1
  145. package/dist/observability/inbox-view-model.d.ts +1 -1
  146. package/dist/observability/inbox-view-model.js +19 -14
  147. package/dist/observability/inbox.d.ts +7 -1
  148. package/dist/observability/inbox.js +632 -124
  149. package/dist/observability/problem-patterns.js +2 -0
  150. package/dist/observability/review-state.d.ts +6 -0
  151. package/dist/observability/review-state.js +235 -63
  152. package/dist/observability/skill-chain-advisories.js +1 -1
  153. package/dist/observability/skill-chain.js +17 -4
  154. package/dist/observability/skill-health-analyzer.d.ts +32 -7
  155. package/dist/observability/skill-health-analyzer.js +194 -121
  156. package/dist/observability/skill-health-report.d.ts +10 -0
  157. package/dist/observability/skill-health-report.js +620 -0
  158. package/dist/observability/soft-standards/constants.d.ts +0 -1
  159. package/dist/observability/soft-standards/constants.js +0 -1
  160. package/dist/observability/soft-standards/index.d.ts +1 -1
  161. package/dist/observability/soft-standards/index.js +1 -1
  162. package/dist/observability/soft-standards/llm-extractor.js +8 -10
  163. package/dist/observability/soft-standards/skill-standards-store.d.ts +2 -1
  164. package/dist/observability/soft-standards/skill-standards-store.js +59 -18
  165. package/dist/observability/soft-standards/types.d.ts +2 -2
  166. package/dist/observability/trace-adapter.d.ts +12 -7
  167. package/dist/observability/trace-adapter.js +11 -9
  168. package/dist/observability/trace-attribution.d.ts +13 -5
  169. package/dist/observability/trace-attribution.js +315 -21
  170. package/dist/observability/trace-ingestion.d.ts +9 -0
  171. package/dist/observability/trace-ingestion.js +80 -0
  172. package/dist/observability/trace-ir.d.ts +113 -0
  173. package/dist/observability/trace-ir.js +87 -0
  174. package/dist/observability/trace-segmenter.d.ts +19 -6
  175. package/dist/observability/trace-segmenter.js +377 -196
  176. package/dist/observability/trace-session-index.d.ts +19 -0
  177. package/dist/observability/trace-session-index.js +68 -0
  178. package/dist/observability/trace-source.d.ts +12 -4
  179. package/dist/observability/trace-source.js +939 -215
  180. package/dist/renderer/html-renderer.js +37 -6
  181. package/dist/renderer/icons.js +3 -0
  182. package/dist/renderer/observation-inbox-renderer.js +208 -90
  183. package/dist/renderer/skill-detail-renderer.js +452 -109
  184. package/dist/renderer/skill-health-renderer.js +69 -12
  185. package/dist/renderer/summary.js +28 -7
  186. package/dist/renderer/table.js +21 -4
  187. package/dist/renderer/test-view.d.ts +1 -0
  188. package/dist/renderer/test-view.js +44 -9
  189. package/dist/server/indexed-report-store.js +14 -18
  190. package/dist/server/job-store.js +64 -26
  191. package/dist/server/report-server.js +190 -78
  192. package/dist/server/report-store.js +57 -80
  193. package/dist/server/skill-index.js +143 -49
  194. package/dist/server/skill-insights.js +44 -5
  195. package/dist/shared/artifact-graph.d.ts +3 -0
  196. package/dist/shared/artifact-graph.js +224 -0
  197. package/dist/shared/assertion-types.d.ts +8 -0
  198. package/dist/shared/assertion-types.js +46 -0
  199. package/dist/shared/atomic-json.d.ts +8 -0
  200. package/dist/shared/atomic-json.js +33 -0
  201. package/dist/shared/diagnosis-schema.d.ts +9 -0
  202. package/dist/shared/diagnosis-schema.js +181 -0
  203. package/dist/shared/doctor-report.d.ts +3 -0
  204. package/dist/shared/doctor-report.js +103 -0
  205. package/dist/shared/evaluation-job.d.ts +6 -0
  206. package/dist/shared/evaluation-job.js +217 -0
  207. package/dist/shared/executor-result.d.ts +17 -0
  208. package/dist/shared/executor-result.js +221 -0
  209. package/dist/shared/file-lock.d.ts +12 -0
  210. package/dist/shared/file-lock.js +129 -0
  211. package/dist/shared/json-value.d.ts +5 -0
  212. package/dist/shared/json-value.js +36 -0
  213. package/dist/shared/keyed-mutex.d.ts +7 -0
  214. package/dist/shared/keyed-mutex.js +24 -0
  215. package/dist/shared/record-count.d.ts +8 -0
  216. package/dist/shared/record-count.js +43 -0
  217. package/dist/shared/sample-contract.d.ts +3 -0
  218. package/dist/shared/sample-contract.js +332 -0
  219. package/dist/shared/timestamp.d.ts +6 -0
  220. package/dist/shared/timestamp.js +64 -0
  221. package/dist/shared/token-usage.d.ts +19 -0
  222. package/dist/shared/token-usage.js +50 -0
  223. package/dist/shared/tool-call-status.d.ts +8 -0
  224. package/dist/shared/tool-call-status.js +28 -0
  225. package/dist/shared/tool-identity.d.ts +21 -0
  226. package/dist/shared/tool-identity.js +84 -0
  227. package/dist/shared/tool-search.js +73 -16
  228. package/dist/shared/trace-projection.d.ts +5 -0
  229. package/dist/shared/trace-projection.js +20 -0
  230. package/dist/shared/trace-source-kind.d.ts +3 -0
  231. package/dist/shared/trace-source-kind.js +12 -0
  232. package/dist/types/diagnosis.d.ts +2 -0
  233. package/dist/types/eval.d.ts +4 -0
  234. package/dist/types/executor.d.ts +32 -5
  235. package/dist/types/index.d.ts +1 -0
  236. package/dist/types/index.js +1 -0
  237. package/dist/types/judge.d.ts +2 -0
  238. package/dist/types/observability.d.ts +116 -9
  239. package/dist/types/report.d.ts +58 -6
  240. package/dist/types/skill-index.d.ts +7 -0
  241. package/dist/types/trace.d.ts +2 -0
  242. package/dist/types/trace.js +1 -0
  243. package/package.json +9 -5
@@ -0,0 +1,180 @@
1
+ import { checkedSumTokenCounts, nonNegativeMetric, optionalTokenCount, } from '../shared/token-usage.js';
2
+ import { extractAgentTrace, isClaudeSdkResultMessage } from './claude-sdk-trace.js';
3
+ export function normalizeClaudeSdkMeasurements(result) {
4
+ const durationMs = optionalTokenCount(result.duration_ms);
5
+ const durationApiMs = optionalTokenCount(result.duration_api_ms);
6
+ const numTurns = optionalTokenCount(result.num_turns);
7
+ const costUSD = nonNegativeMetric(result.total_cost_usd);
8
+ if (durationMs === undefined
9
+ || durationApiMs === undefined
10
+ || numTurns === undefined
11
+ || costUSD === undefined
12
+ || costUSD > Number.MAX_SAFE_INTEGER) {
13
+ return { error: 'claude result contained invalid duration, turn, or cost metrics' };
14
+ }
15
+ const rawModelUsage = result.modelUsage;
16
+ const modelUsageEntries = rawModelUsage
17
+ && typeof rawModelUsage === 'object'
18
+ && !Array.isArray(rawModelUsage)
19
+ ? Object.values(rawModelUsage)
20
+ : [];
21
+ const buckets = [[], [], [], []];
22
+ if (modelUsageEntries.length > 0) {
23
+ for (const entry of modelUsageEntries) {
24
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
25
+ return { error: 'claude result contained invalid modelUsage' };
26
+ }
27
+ const counts = [
28
+ optionalTokenCount(entry.inputTokens),
29
+ optionalTokenCount(entry.outputTokens),
30
+ optionalTokenCount(entry.cacheReadInputTokens),
31
+ optionalTokenCount(entry.cacheCreationInputTokens),
32
+ ];
33
+ if (counts.some((count) => count === undefined)) {
34
+ return { error: 'claude result contained invalid modelUsage token counters' };
35
+ }
36
+ buckets.forEach((bucket, index) => bucket.push(counts[index]));
37
+ }
38
+ }
39
+ else {
40
+ const usage = result.usage;
41
+ const counts = [
42
+ optionalTokenCount(usage?.input_tokens),
43
+ optionalTokenCount(usage?.output_tokens),
44
+ usage?.cache_read_input_tokens === undefined
45
+ ? 0
46
+ : optionalTokenCount(usage.cache_read_input_tokens),
47
+ usage?.cache_creation_input_tokens === undefined
48
+ ? 0
49
+ : optionalTokenCount(usage.cache_creation_input_tokens),
50
+ ];
51
+ if (counts.some((count) => count === undefined)) {
52
+ return { error: 'claude result contained missing or invalid token usage' };
53
+ }
54
+ buckets.forEach((bucket, index) => bucket.push(counts[index]));
55
+ }
56
+ const totals = buckets.map((bucket) => checkedSumTokenCounts(...bucket));
57
+ if (totals.some((total) => total === undefined)
58
+ || checkedSumTokenCounts(...totals) === undefined) {
59
+ return { error: 'claude token usage aggregate exceeds safe integer' };
60
+ }
61
+ return {
62
+ durationMs,
63
+ durationApiMs,
64
+ inputTokens: totals[0],
65
+ outputTokens: totals[1],
66
+ cacheReadTokens: totals[2],
67
+ cacheCreationTokens: totals[3],
68
+ costUSD,
69
+ numTurns,
70
+ };
71
+ }
72
+ export function parseClaudeStreamJson(stdout) {
73
+ const messages = [];
74
+ let malformedLineCount = 0;
75
+ for (const line of stdout.split('\n')) {
76
+ const trimmed = line.trim();
77
+ if (!trimmed)
78
+ continue;
79
+ try {
80
+ const value = JSON.parse(trimmed);
81
+ if (!value
82
+ || typeof value !== 'object'
83
+ || Array.isArray(value)
84
+ || typeof value.type !== 'string') {
85
+ malformedLineCount += 1;
86
+ }
87
+ else {
88
+ messages.push(value);
89
+ }
90
+ }
91
+ catch {
92
+ malformedLineCount += 1;
93
+ }
94
+ }
95
+ return { messages, malformedLineCount };
96
+ }
97
+ export function buildClaudeResult(options) {
98
+ const { messages, wallClockDurationMs, source, malformedLineCount = 0, forcedError, messageTimestamps, } = options;
99
+ const resultMessages = messages.filter(isClaudeSdkResultMessage);
100
+ const trace = extractAgentTrace(messages, messageTimestamps);
101
+ const traceFields = {
102
+ fullNumTurns: trace.fullNumTurns,
103
+ numSubAgents: trace.numSubAgents,
104
+ ...(trace.turns.length > 0 && { turns: trace.turns }),
105
+ ...(trace.toolCalls.length > 0 && { toolCalls: trace.toolCalls }),
106
+ };
107
+ const errors = [];
108
+ if (forcedError)
109
+ errors.push(forcedError);
110
+ if (malformedLineCount > 0) {
111
+ errors.push(`${source} output contained ${malformedLineCount} malformed line(s)`);
112
+ }
113
+ if (resultMessages.length !== 1) {
114
+ errors.push(resultMessages.length === 0
115
+ ? `no result message in ${source} output`
116
+ : `expected exactly one result message in ${source} output, received ${resultMessages.length}`);
117
+ return {
118
+ ok: false,
119
+ error: [...new Set(errors)].join('; '),
120
+ durationMs: wallClockDurationMs,
121
+ durationApiMs: 0,
122
+ inputTokens: 0,
123
+ outputTokens: 0,
124
+ cacheReadTokens: 0,
125
+ cacheCreationTokens: 0,
126
+ tokenUsageReportedByExecutor: false,
127
+ costUSD: 0,
128
+ costReportedByExecutor: false,
129
+ output: null,
130
+ stopReason: 'error',
131
+ numTurns: 0,
132
+ ...traceFields,
133
+ };
134
+ }
135
+ const result = resultMessages[0];
136
+ const measurements = normalizeClaudeSdkMeasurements(result);
137
+ if ('error' in measurements) {
138
+ errors.push(measurements.error);
139
+ return {
140
+ ok: false,
141
+ error: [...new Set(errors)].join('; '),
142
+ durationMs: wallClockDurationMs,
143
+ durationApiMs: 0,
144
+ inputTokens: 0,
145
+ outputTokens: 0,
146
+ cacheReadTokens: 0,
147
+ cacheCreationTokens: 0,
148
+ tokenUsageReportedByExecutor: false,
149
+ costUSD: 0,
150
+ costReportedByExecutor: false,
151
+ output: typeof result.result === 'string' && result.result ? result.result : null,
152
+ stopReason: 'error',
153
+ numTurns: 0,
154
+ ...traceFields,
155
+ };
156
+ }
157
+ if (result.subtype !== 'success') {
158
+ errors.push(result.errors?.filter(Boolean).join('; ') || result.subtype || `${source} failed`);
159
+ }
160
+ else if (result.errors?.length) {
161
+ errors.push(result.errors.filter(Boolean).join('; '));
162
+ }
163
+ const output = typeof result.result === 'string' && result.result.trim()
164
+ ? result.result
165
+ : null;
166
+ if (!output)
167
+ errors.push(`${source} completed without an assistant response`);
168
+ const uniqueErrors = [...new Set(errors.filter(Boolean))];
169
+ return {
170
+ ok: uniqueErrors.length === 0,
171
+ ...(uniqueErrors.length > 0 && { error: uniqueErrors.join('; ') }),
172
+ ...measurements,
173
+ ...((forcedError || malformedLineCount > 0) && {
174
+ tokenUsageReportedByExecutor: false,
175
+ }),
176
+ output,
177
+ stopReason: uniqueErrors.length === 0 ? result.stop_reason || 'end_turn' : 'error',
178
+ ...traceFields,
179
+ };
180
+ }
@@ -1,4 +1,6 @@
1
1
  import { safeSliceForJson } from '../util/safe-slice.js';
2
+ import { isToolResultFailureText } from '../observability/text-signals.js';
3
+ import { normalizeToolIdentity } from '../shared/tool-identity.js';
2
4
  export function isClaudeSdkResultMessage(message) {
3
5
  return message.type === 'result';
4
6
  }
@@ -21,16 +23,36 @@ export function extractAgentTrace(messages, timestamps) {
21
23
  const textParts = [];
22
24
  const turnToolCalls = [];
23
25
  let hasNonThinking = false;
24
- for (const block of content) {
26
+ for (let blockIndex = 0; blockIndex < content.length; blockIndex += 1) {
27
+ const block = content[blockIndex];
25
28
  if (block.type === 'text' && block.text) {
26
29
  textParts.push(block.text);
27
30
  hasNonThinking = true;
28
31
  }
29
32
  else if (block.type === 'tool_use' && block.name) {
30
- pendingToolUse.set(block.id || '', { tool: block.name, input: block.input });
31
- turnToolCalls.push({ tool: block.name, input: block.input, output: null, success: true });
33
+ const toolIdentity = normalizeToolIdentity({ sourceName: block.name });
34
+ const toolCall = {
35
+ tool: toolIdentity.name,
36
+ ...(toolIdentity.sourceName ? { sourceTool: toolIdentity.sourceName } : {}),
37
+ ...(toolIdentity.namespace ? { toolNamespace: toolIdentity.namespace } : {}),
38
+ ...(toolIdentity.provider ? { toolProvider: toolIdentity.provider } : {}),
39
+ input: block.input,
40
+ output: null,
41
+ status: 'unknown',
42
+ statusSource: 'unknown',
43
+ success: false,
44
+ callInstanceId: `claude-sdk:${msgIdx}:${blockIndex}`,
45
+ toolUseId: block.id,
46
+ };
47
+ if (block.id) {
48
+ const pending = pendingToolUse.get(block.id) ?? [];
49
+ pending.push(toolCall);
50
+ pendingToolUse.set(block.id, pending);
51
+ }
52
+ turnToolCalls.push(toolCall);
53
+ toolCalls.push(toolCall);
32
54
  hasNonThinking = true;
33
- if (block.name === 'Agent')
55
+ if (toolIdentity.name === 'Agent')
34
56
  numSubAgents++;
35
57
  }
36
58
  else if (block.type !== 'thinking') {
@@ -52,12 +74,17 @@ export function extractAgentTrace(messages, timestamps) {
52
74
  }
53
75
  }
54
76
  if (msg.type === 'user') {
55
- for (const block of content) {
77
+ for (let blockIndex = 0; blockIndex < content.length; blockIndex += 1) {
78
+ const block = content[blockIndex];
56
79
  if (block.type !== 'tool_result')
57
80
  continue;
58
81
  const toolUseId = block.tool_use_id || '';
59
- const pending = pendingToolUse.get(toolUseId);
60
- const isError = block.is_error || false;
82
+ const pendingQueue = pendingToolUse.get(toolUseId);
83
+ const pending = pendingQueue?.shift();
84
+ if (pendingQueue?.length === 0)
85
+ pendingToolUse.delete(toolUseId);
86
+ const runtimeError = block.is_error;
87
+ const hasRuntimeStatus = typeof runtimeError === 'boolean';
61
88
  const resultContent = block.content;
62
89
  const outputText = typeof resultContent === 'string'
63
90
  ? resultContent
@@ -68,27 +95,29 @@ export function extractAgentTrace(messages, timestamps) {
68
95
  // is_error 因此为 true,但语义上是"mock 成功返回"。识别前缀 → 覆盖 success=true。
69
96
  // 见 src/eval-core/mocks-runtime.ts 的 wrappedReason 前缀。
70
97
  const isMockHit = outputText.startsWith('[mock] simulated tool output');
71
- const effectiveSuccess = isMockHit ? true : !isError;
72
- const tc = {
73
- tool: pending?.tool || 'unknown',
74
- input: pending?.input || null,
75
- output: safeSliceForJson(outputText, 500, ''),
76
- success: effectiveSuccess,
98
+ const inferredFailure = !hasRuntimeStatus && isToolResultFailureText(outputText);
99
+ const tc = pending ?? {
100
+ tool: 'unknown',
101
+ input: null,
102
+ output: null,
103
+ status: 'unknown',
104
+ statusSource: 'unknown',
105
+ success: false,
106
+ callInstanceId: `claude-sdk:orphan-result:${msgIdx}:${blockIndex}`,
107
+ toolUseId,
77
108
  };
78
- toolCalls.push(tc);
79
- if (pending) {
80
- for (let i = turns.length - 1; i >= 0; i--) {
81
- const turn = turns[i];
82
- if (turn.role === 'assistant' && turn.toolCalls) {
83
- const placeholder = turn.toolCalls.find((t) => t.tool === pending.tool && t.output === null);
84
- if (placeholder) {
85
- placeholder.output = tc.output;
86
- placeholder.success = effectiveSuccess;
87
- break;
88
- }
89
- }
90
- }
91
- }
109
+ tc.output = safeSliceForJson(outputText, 500, '');
110
+ tc.status = isMockHit
111
+ ? 'success'
112
+ : hasRuntimeStatus
113
+ ? runtimeError ? 'failure' : 'success'
114
+ : inferredFailure ? 'failure' : 'unknown';
115
+ tc.statusSource = isMockHit
116
+ ? 'tool-output'
117
+ : hasRuntimeStatus ? 'runtime' : inferredFailure ? 'inferred' : 'unknown';
118
+ tc.success = tc.status === 'success';
119
+ if (!pending)
120
+ toolCalls.push(tc);
92
121
  const toolDur = msgTs && lastTurnTs ? msgTs - lastTurnTs : undefined;
93
122
  turns.push({
94
123
  role: 'tool',
@@ -97,7 +126,6 @@ export function extractAgentTrace(messages, timestamps) {
97
126
  });
98
127
  if (msgTs)
99
128
  lastTurnTs = msgTs;
100
- pendingToolUse.delete(toolUseId);
101
129
  }
102
130
  }
103
131
  }
@@ -1,4 +1,5 @@
1
1
  import type { ExecResult, ExecutorInput } from '../types/index.js';
2
+ export { normalizeClaudeSdkMeasurements } from './claude-protocol.js';
2
3
  /**
3
4
  * Map ExecutorInput.allowedSkills to SDK query options for skill isolation.
4
5
  * undefined → {} (SDK default: full ~/.claude/skills/ discovery)
@@ -1,9 +1,10 @@
1
1
  import { join } from 'node:path';
2
2
  import { mkdirSync, writeFileSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
- import { extractAgentTrace, isClaudeSdkResultMessage } from './claude-sdk-trace.js';
5
- import { asErrorLike, buildExecEnv, DEFAULT_TIMEOUT_MS, errorMessage } from './shared.js';
4
+ import { asErrorLike, buildExecEnv, DEFAULT_TIMEOUT_MS, errorMessage, interruptedExecResult, registerSigintSubscriber, timeoutExecResult, } from './shared.js';
6
5
  import { buildSdkHookCallback } from '../eval-core/mocks-runtime.js';
6
+ import { buildClaudeResult } from './claude-protocol.js';
7
+ export { normalizeClaudeSdkMeasurements } from './claude-protocol.js';
7
8
  let sdkQuery = null;
8
9
  /**
9
10
  * Map ExecutorInput.allowedSkills to SDK query options for skill isolation.
@@ -41,7 +42,15 @@ export async function claudeSdkExecutor({ model, system, prompt, cwd, skillDir,
41
42
  const isolationOpts = lean ? { skills: [], disallowedTools: ['*'] } : baseIsolationOpts;
42
43
  const start = Date.now();
43
44
  const abortController = new AbortController();
44
- const timer = setTimeout(() => abortController.abort(), timeoutMs);
45
+ let abortReason;
46
+ const abort = (reason) => {
47
+ if (abortReason)
48
+ return;
49
+ abortReason = reason;
50
+ abortController.abort();
51
+ };
52
+ const timer = setTimeout(() => abort('timeout'), timeoutMs);
53
+ const unregisterSigint = registerSigintSubscriber(() => abort('interrupted'));
45
54
  const env = buildExecEnv(skillDir);
46
55
  const messages = [];
47
56
  const messageTimestamps = [];
@@ -74,14 +83,10 @@ export async function claudeSdkExecutor({ model, system, prompt, cwd, skillDir,
74
83
  ...hooksOpts,
75
84
  },
76
85
  });
77
- const resultMsgs = [];
78
86
  for await (const msg of stream) {
79
87
  messages.push(msg);
80
88
  messageTimestamps.push(Date.now());
81
- if (isClaudeSdkResultMessage(msg))
82
- resultMsgs.push(msg);
83
89
  }
84
- clearTimeout(timer);
85
90
  if (verbose) {
86
91
  try {
87
92
  const debugDir = join(tmpdir(), 'omk-debug');
@@ -95,111 +100,52 @@ export async function claudeSdkExecutor({ model, system, prompt, cwd, skillDir,
95
100
  }
96
101
  catch { }
97
102
  }
98
- if (resultMsgs.length === 0) {
99
- const durationMs = Date.now() - start;
100
- process.stderr.write(`[omk] claude-sdk executor: no result message received (${messages.length} messages)\n`);
101
- return {
102
- ok: false, error: 'no result message received',
103
- durationMs, durationApiMs: 0,
104
- inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0,
105
- costUSD: 0, output: null, stopReason: 'error', numTurns: 0,
106
- ...(mockStatsOf() && { mockStats: mockStatsOf() }),
107
- };
108
- }
109
- let output = '';
110
- let totalInputTokens = 0;
111
- let totalOutputTokens = 0;
112
- let totalCacheReadTokens = 0;
113
- let totalCacheCreationTokens = 0;
114
- let totalCostUSD = 0;
115
- let totalDurationApiMs = 0;
116
- let totalNumTurns = 0;
117
- let lastSubtype = 'success';
118
- for (const r of resultMsgs) {
119
- output += r.result || '';
120
- // Prefer modelUsage (includes subAgent tokens) over usage (main process only)
121
- const modelUsage = r.modelUsage;
122
- if (modelUsage) {
123
- for (const mu of Object.values(modelUsage)) {
124
- totalInputTokens += mu.inputTokens || 0;
125
- totalOutputTokens += mu.outputTokens || 0;
126
- totalCacheReadTokens += mu.cacheReadInputTokens || 0;
127
- totalCacheCreationTokens += mu.cacheCreationInputTokens || 0;
128
- }
129
- }
130
- else {
131
- totalInputTokens += r.usage?.input_tokens || 0;
132
- totalOutputTokens += r.usage?.output_tokens || 0;
133
- totalCacheReadTokens += r.usage?.cache_read_input_tokens || 0;
134
- totalCacheCreationTokens += r.usage?.cache_creation_input_tokens || 0;
135
- }
136
- totalCostUSD += r.total_cost_usd || 0;
137
- totalDurationApiMs += r.duration_api_ms || 0;
138
- totalNumTurns += r.num_turns || 0;
139
- lastSubtype = r.subtype || lastSubtype;
140
- }
141
- const durationMs = resultMsgs[resultMsgs.length - 1].duration_ms || (Date.now() - start);
142
- const trace = extractAgentTrace(messages, messageTimestamps);
143
- if (lastSubtype !== 'success') {
144
- const lastErr = resultMsgs[resultMsgs.length - 1];
145
- const errMsg = lastErr.errors?.join('; ') || lastErr.subtype;
146
- process.stderr.write(`[omk] claude-sdk executor: ${lastErr.subtype}: ${errMsg}\n`);
147
- return {
148
- ok: false, error: errMsg,
149
- durationMs,
150
- durationApiMs: totalDurationApiMs,
151
- inputTokens: totalInputTokens,
152
- outputTokens: totalOutputTokens,
153
- cacheReadTokens: totalCacheReadTokens,
154
- cacheCreationTokens: totalCacheCreationTokens,
155
- costUSD: totalCostUSD,
156
- output: null, stopReason: 'error', numTurns: totalNumTurns,
157
- fullNumTurns: trace.fullNumTurns,
158
- numSubAgents: trace.numSubAgents,
159
- ...(trace.turns.length > 0 && { turns: trace.turns }),
160
- ...(trace.toolCalls.length > 0 && { toolCalls: trace.toolCalls }),
161
- ...(mockStatsOf() && { mockStats: mockStatsOf() }),
162
- };
163
- }
164
103
  return {
165
- ok: true,
166
- durationMs,
167
- durationApiMs: totalDurationApiMs,
168
- inputTokens: totalInputTokens,
169
- outputTokens: totalOutputTokens,
170
- cacheReadTokens: totalCacheReadTokens,
171
- cacheCreationTokens: totalCacheCreationTokens,
172
- costUSD: totalCostUSD,
173
- output,
174
- stopReason: 'end',
175
- numTurns: totalNumTurns,
176
- fullNumTurns: trace.fullNumTurns,
177
- numSubAgents: trace.numSubAgents,
178
- ...(trace.turns.length > 0 && { turns: trace.turns }),
179
- ...(trace.toolCalls.length > 0 && { toolCalls: trace.toolCalls }),
104
+ ...buildClaudeResult({
105
+ messages,
106
+ messageTimestamps,
107
+ wallClockDurationMs: Date.now() - start,
108
+ source: 'claude-sdk',
109
+ }),
180
110
  ...(mockStatsOf() && { mockStats: mockStatsOf() }),
181
111
  };
182
112
  }
183
113
  catch (err) {
184
- clearTimeout(timer);
185
114
  const durationMs = Date.now() - start;
186
115
  const details = asErrorLike(err);
187
116
  const isAbort = details.name === 'AbortError';
117
+ if (abortReason === 'interrupted') {
118
+ return {
119
+ ...interruptedExecResult(durationMs),
120
+ ...(mockStatsOf() && { mockStats: mockStatsOf() }),
121
+ };
122
+ }
123
+ if (abortReason === 'timeout') {
124
+ return {
125
+ ...timeoutExecResult(timeoutMs, durationMs),
126
+ ...(mockStatsOf() && { mockStats: mockStatsOf() }),
127
+ };
128
+ }
188
129
  if (isAbort) {
189
- process.stderr.write(`[omk] claude-sdk executor: timed out after ${timeoutMs / 1000}s\n`);
130
+ process.stderr.write('[omk] claude-sdk executor: execution aborted\n');
190
131
  }
191
132
  else {
192
133
  process.stderr.write(`[omk] claude-sdk executor error: ${errorMessage(err)}\n`);
193
134
  }
194
135
  return {
195
136
  ok: false,
196
- error: isAbort ? `execution timed out after ${timeoutMs / 1000}s` : errorMessage(err),
137
+ error: isAbort ? 'claude-sdk execution aborted' : errorMessage(err),
197
138
  durationMs, durationApiMs: 0,
198
139
  inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0,
199
- costUSD: 0, output: null,
200
- stopReason: isAbort ? 'timeout' : 'error',
140
+ tokenUsageReportedByExecutor: false,
141
+ costUSD: 0, costReportedByExecutor: false, output: null,
142
+ stopReason: 'error',
201
143
  numTurns: 0,
202
144
  ...(mockStatsOf() && { mockStats: mockStatsOf() }),
203
145
  };
204
146
  }
147
+ finally {
148
+ clearTimeout(timer);
149
+ unregisterSigint();
150
+ }
205
151
  }