@dzhechkov/harness-core 0.8.45 → 0.8.47

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 (159) hide show
  1. package/.dz-manifest.json +206 -174
  2. package/README.md +136 -8
  3. package/dist/agentdb-index.d.ts.map +1 -1
  4. package/dist/agentdb-index.js +32 -2
  5. package/dist/agentdb-index.js.map +1 -1
  6. package/dist/agentdb-snapshot.d.ts +14 -1
  7. package/dist/agentdb-snapshot.d.ts.map +1 -1
  8. package/dist/agentdb-snapshot.js +100 -4
  9. package/dist/agentdb-snapshot.js.map +1 -1
  10. package/dist/apply-leg.d.ts +9 -1
  11. package/dist/apply-leg.d.ts.map +1 -1
  12. package/dist/apply-leg.js +47 -10
  13. package/dist/apply-leg.js.map +1 -1
  14. package/dist/architecture.d.ts +0 -5
  15. package/dist/architecture.d.ts.map +1 -1
  16. package/dist/architecture.js +25 -2
  17. package/dist/architecture.js.map +1 -1
  18. package/dist/codex-rollouts.d.ts +35 -7
  19. package/dist/codex-rollouts.d.ts.map +1 -1
  20. package/dist/codex-rollouts.js +201 -113
  21. package/dist/codex-rollouts.js.map +1 -1
  22. package/dist/cost-ledger.d.ts +42 -3
  23. package/dist/cost-ledger.d.ts.map +1 -1
  24. package/dist/cost-ledger.js +478 -15
  25. package/dist/cost-ledger.js.map +1 -1
  26. package/dist/feature-adr-routing.d.ts +16 -0
  27. package/dist/feature-adr-routing.d.ts.map +1 -1
  28. package/dist/feature-adr-routing.js +21 -0
  29. package/dist/feature-adr-routing.js.map +1 -1
  30. package/dist/guard.d.ts +7 -0
  31. package/dist/guard.d.ts.map +1 -1
  32. package/dist/guard.js +48 -0
  33. package/dist/guard.js.map +1 -1
  34. package/dist/index.d.ts +13 -6
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +13 -3
  37. package/dist/index.js.map +1 -1
  38. package/dist/loop-plan.d.ts +4 -0
  39. package/dist/loop-plan.d.ts.map +1 -1
  40. package/dist/loop-plan.js +4 -0
  41. package/dist/loop-plan.js.map +1 -1
  42. package/dist/mutation-gate.d.ts +36 -1
  43. package/dist/mutation-gate.d.ts.map +1 -1
  44. package/dist/mutation-gate.js +109 -19
  45. package/dist/mutation-gate.js.map +1 -1
  46. package/dist/npm-homepage.d.ts +64 -0
  47. package/dist/npm-homepage.d.ts.map +1 -0
  48. package/dist/npm-homepage.js +109 -0
  49. package/dist/npm-homepage.js.map +1 -0
  50. package/dist/operations.d.ts +6 -0
  51. package/dist/operations.d.ts.map +1 -1
  52. package/dist/operations.js +150 -24
  53. package/dist/operations.js.map +1 -1
  54. package/dist/pack-inventory.d.ts +13 -0
  55. package/dist/pack-inventory.d.ts.map +1 -1
  56. package/dist/pack-inventory.js +28 -2
  57. package/dist/pack-inventory.js.map +1 -1
  58. package/dist/parity.d.ts.map +1 -1
  59. package/dist/parity.js +4 -1
  60. package/dist/parity.js.map +1 -1
  61. package/dist/publish-sibling-drift.d.ts +43 -1
  62. package/dist/publish-sibling-drift.d.ts.map +1 -1
  63. package/dist/publish-sibling-drift.js +269 -13
  64. package/dist/publish-sibling-drift.js.map +1 -1
  65. package/dist/publish.d.ts +5 -1
  66. package/dist/publish.d.ts.map +1 -1
  67. package/dist/publish.js +18 -1
  68. package/dist/publish.js.map +1 -1
  69. package/dist/qe-bridge.d.ts +63 -1
  70. package/dist/qe-bridge.d.ts.map +1 -1
  71. package/dist/qe-bridge.js +82 -0
  72. package/dist/qe-bridge.js.map +1 -1
  73. package/dist/release-package-audit.d.ts +86 -0
  74. package/dist/release-package-audit.d.ts.map +1 -0
  75. package/dist/release-package-audit.js +272 -0
  76. package/dist/release-package-audit.js.map +1 -0
  77. package/dist/release.d.ts +8 -1
  78. package/dist/release.d.ts.map +1 -1
  79. package/dist/release.js +58 -32
  80. package/dist/release.js.map +1 -1
  81. package/dist/round-exec-claim.d.ts +36 -0
  82. package/dist/round-exec-claim.d.ts.map +1 -0
  83. package/dist/round-exec-claim.js +17 -0
  84. package/dist/round-exec-claim.js.map +1 -0
  85. package/dist/round.d.ts +10 -1
  86. package/dist/round.d.ts.map +1 -1
  87. package/dist/round.js +5 -1
  88. package/dist/round.js.map +1 -1
  89. package/dist/run-records.d.ts +11 -0
  90. package/dist/run-records.d.ts.map +1 -1
  91. package/dist/run-records.js +124 -20
  92. package/dist/run-records.js.map +1 -1
  93. package/dist/session-retro.d.ts +33 -0
  94. package/dist/session-retro.d.ts.map +1 -1
  95. package/dist/session-retro.js +170 -34
  96. package/dist/session-retro.js.map +1 -1
  97. package/dist/sign.d.ts +44 -0
  98. package/dist/sign.d.ts.map +1 -1
  99. package/dist/sign.js +126 -1
  100. package/dist/sign.js.map +1 -1
  101. package/dist/stage-usage.d.ts +148 -0
  102. package/dist/stage-usage.d.ts.map +1 -0
  103. package/dist/stage-usage.js +261 -0
  104. package/dist/stage-usage.js.map +1 -0
  105. package/dist/statusline.d.ts +23 -0
  106. package/dist/statusline.d.ts.map +1 -1
  107. package/dist/statusline.js +167 -1
  108. package/dist/statusline.js.map +1 -1
  109. package/dist/workflow-run-dispatch.d.ts +21 -19
  110. package/dist/workflow-run-dispatch.d.ts.map +1 -1
  111. package/dist/workflow-run-dispatch.js +206 -95
  112. package/dist/workflow-run-dispatch.js.map +1 -1
  113. package/dist/workflow-run.d.ts +12 -3
  114. package/dist/workflow-run.d.ts.map +1 -1
  115. package/dist/workflow-run.js +29 -2
  116. package/dist/workflow-run.js.map +1 -1
  117. package/package.json +4 -4
  118. package/sbom.json +309 -229
  119. package/src/agentdb-index.ts +31 -2
  120. package/src/agentdb-snapshot.ts +105 -4
  121. package/src/apply-leg.ts +47 -10
  122. package/src/architecture.ts +15 -2
  123. package/src/codex-rollouts.ts +155 -147
  124. package/src/cost-ledger.ts +308 -19
  125. package/src/feature-adr-routing.ts +22 -0
  126. package/src/guard.ts +52 -0
  127. package/src/index.ts +19 -3
  128. package/src/loop-plan.ts +8 -0
  129. package/src/mutation-gate.ts +153 -19
  130. package/src/npm-homepage.ts +142 -0
  131. package/src/operations.ts +177 -22
  132. package/src/pack-inventory.ts +27 -2
  133. package/src/parity.ts +4 -1
  134. package/src/publish-sibling-drift.ts +214 -11
  135. package/src/publish.ts +20 -1
  136. package/src/qe-bridge.ts +86 -1
  137. package/src/release-package-audit.ts +264 -0
  138. package/src/release.ts +55 -22
  139. package/src/round-exec-claim.ts +43 -0
  140. package/src/round.ts +8 -0
  141. package/src/run-records.ts +101 -22
  142. package/src/session-retro.ts +153 -31
  143. package/src/sign.ts +136 -1
  144. package/src/stage-usage.ts +191 -0
  145. package/src/statusline.ts +149 -1
  146. package/src/workflow-run-dispatch.ts +148 -89
  147. package/src/workflow-run.ts +40 -4
  148. package/dist/ledger-cost-fill.d.ts +0 -58
  149. package/dist/ledger-cost-fill.d.ts.map +0 -1
  150. package/dist/ledger-cost-fill.js +0 -78
  151. package/dist/ledger-cost-fill.js.map +0 -1
  152. package/dist/retro.d.ts +0 -131
  153. package/dist/retro.d.ts.map +0 -1
  154. package/dist/retro.js +0 -207
  155. package/dist/retro.js.map +0 -1
  156. package/dist/sbom.d.ts +0 -42
  157. package/dist/sbom.d.ts.map +0 -1
  158. package/dist/sbom.js +0 -120
  159. package/dist/sbom.js.map +0 -1
@@ -60,7 +60,18 @@ export interface DispatchResult {
60
60
  /** null when the runtime did not report a count — NEVER 0, never estimated (ADR-004 C4). */
61
61
  tokensIn: number | null;
62
62
  tokensOut: number | null;
63
- tokensSource: 'claude-envelope' | 'codex-stderr' | null;
63
+ tokensSource: 'claude-envelope' | 'codex-stderr' | 'codex-json' | null;
64
+ tokensTotal?: number | null;
65
+ tokensCacheRead?: number | null;
66
+ tokensCacheWrite?: number | null;
67
+ tokensReasoning?: number | null;
68
+ reportedTotalBasis?: 'raw-inclusive' | 'uncached-display' | 'reported-unknown' | 'output-only' | 'unknown';
69
+ inputCacheSemantics?: 'includes-cache-read-write' | 'excludes-cache-read-write' | 'uncached-display' | 'unknown';
70
+ reportedCostUsd?: number | null;
71
+ usageDiagnostics?: string[];
72
+ totalDerivation?: string;
73
+ modelProvenance?: string;
74
+ usageSource?: { schema: string; scope: string; threadId: string | null; turnId: string | null; receiptId: string | null };
64
75
  failure?: DispatchFailure;
65
76
  }
66
77
 
@@ -181,7 +192,7 @@ export { CODEX_EXEC_PROMPT_CEILING_CHARS };
181
192
  export function codexExecArgv(modelId: string, prompt: string, deliverable: Deliverable): string[] {
182
193
  const returnValue = (deliverable ?? 'return-value') !== 'file';
183
194
  const text = returnValue ? CODEX_SCOPING_PREFIX + '\n\n' + prompt : prompt;
184
- return ['exec', '-m', modelId, ...(returnValue ? ['--sandbox', 'read-only'] : []), text];
195
+ return ['exec', '-m', modelId, ...(returnValue ? ['--sandbox', 'read-only'] : []), '--json', text];
185
196
  }
186
197
 
187
198
  /** The liveness probe: an allowlist says an id is SPELLABLE, only a probe says it ANSWERS. */
@@ -205,55 +216,130 @@ export function interpretCodexProbe(out: { stdout: string; exitCode: number | nu
205
216
  * builds/configs do print one; it is tested, not assumed. (Named consequence: codex runs report no
206
217
  * token counts today. That is the honest state, not a bug to paper over — see the manifest.)
207
218
  */
208
- export function extractCodexTokens(stderr: string): { tokensIn: number | null; tokensOut: number | null } {
209
- const text = String(stderr ?? '');
210
- const num = (raw: string | undefined): number | null => {
211
- if (raw === undefined) return null;
212
- const n = Number(raw.replace(/[,_\s]/g, ''));
213
- return Number.isFinite(n) && n >= 0 ? n : null;
219
+ type ObservedUsage = Pick<DispatchResult, 'tokensIn' | 'tokensOut' | 'tokensSource' | 'tokensTotal' | 'tokensCacheRead' | 'tokensCacheWrite' | 'tokensReasoning' | 'reportedTotalBasis' | 'inputCacheSemantics' | 'reportedCostUsd' | 'usageDiagnostics' | 'usageSource' | 'totalDerivation'>;
220
+ const count = (value: unknown): number | null => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : null;
221
+ const emptyUsage = (): ObservedUsage => ({ tokensIn: null, tokensOut: null, tokensTotal: null, tokensCacheRead: null,
222
+ tokensCacheWrite: null, tokensReasoning: null, tokensSource: null, reportedTotalBasis: 'unknown', inputCacheSemantics: 'unknown',
223
+ reportedCostUsd: null, usageDiagnostics: [] });
224
+
225
+ export function extractCodexTokens(stderr: string): ObservedUsage {
226
+ const text = String(stderr ?? ''); const usage = emptyUsage();
227
+ const numeric = (raw: string | undefined): number | null => raw === undefined ? null : count(Number(raw.replace(/[,_\s]/g, '')));
228
+ const input = /\binput\b[^\n\d+.-]{0,20}([+-]?\d[\d,_]*(?:\.\d+)?)|\btokens?\s+in\b[^\n\d+.-]{0,10}([+-]?\d[\d,_]*(?:\.\d+)?)/i.exec(text);
229
+ const output = /\boutput\b[^\n\d+.-]{0,20}([+-]?\d[\d,_]*(?:\.\d+)?)|\btokens?\s+out\b[^\n\d+.-]{0,10}([+-]?\d[\d,_]*(?:\.\d+)?)/i.exec(text);
230
+ const total = /tokens used\s*\n\s*([+-]?\d[\d,_]*(?:\.\d+)?)|Token usage:\s*total=([+-]?\d[\d,_]*(?:\.\d+)?)/i.exec(text);
231
+ usage.tokensIn = numeric(input?.[1] ?? input?.[2]); usage.tokensOut = numeric(output?.[1] ?? output?.[2]);
232
+ usage.tokensTotal = numeric(total?.[1] ?? total?.[2]);
233
+ if (input && usage.tokensIn === null) usage.usageDiagnostics!.push('invalid-counter:input');
234
+ if (output && usage.tokensOut === null) usage.usageDiagnostics!.push('invalid-counter:output');
235
+ if (total && usage.tokensTotal === null) usage.usageDiagnostics!.push('invalid-counter:total');
236
+ const cached = /\(\+([\d,_]+) cached\)/i.exec(text);
237
+ if (total?.[2] && cached) usage.tokensCacheRead = numeric(cached[1]);
238
+ usage.reportedTotalBasis = total?.[2] ? 'uncached-display' : 'reported-unknown';
239
+ usage.inputCacheSemantics = total?.[2] ? 'uncached-display' : 'unknown';
240
+ usage.tokensSource = usage.tokensTotal != null || usage.tokensIn != null || usage.tokensOut != null ? 'codex-stderr' : null;
241
+ usage.totalDerivation = usage.tokensTotal === null ? 'not-recorded' : 'reported';
242
+ return usage;
243
+ }
244
+
245
+ export function extractClaudeUsage(stdout: string): ObservedUsage {
246
+ let chosen: Record<string, unknown> | null = null;
247
+ for (const line of [String(stdout).trim(), ...String(stdout).split(/\r?\n/)]) {
248
+ try { const obj = JSON.parse(line) as Record<string, unknown>; if (obj && obj['type'] === 'result') chosen = obj; } catch { /* non-envelope line */ }
249
+ }
250
+ const result = emptyUsage(); if (!chosen) return result;
251
+ const usage = chosen['usage']; if (!usage || typeof usage !== 'object' || Array.isArray(usage)) return result;
252
+ const raw = usage as Record<string, unknown>;
253
+ const invalid = (obj: Record<string, unknown>, key: string) => obj[key] != null && count(obj[key]) === null;
254
+ const field = (obj: Record<string, unknown>, key: string): number | null => {
255
+ if (invalid(obj, key)) result.usageDiagnostics!.push('invalid-count:' + key);
256
+ return count(obj[key]);
214
257
  };
215
- // Two accepted spellings per half: `input tokens: N` / `input: N tokens` and `tokens in: N`.
216
- const inMatch = /\binput\b[^\n\d]{0,20}([\d,_]+)|\btokens?\s+in\b[^\n\d]{0,10}([\d,_]+)/i.exec(text);
217
- const outMatch = /\boutput\b[^\n\d]{0,20}([\d,_]+)|\btokens?\s+out\b[^\n\d]{0,10}([\d,_]+)/i.exec(text);
218
- const tokensIn = inMatch === null ? null : num(inMatch[1] ?? inMatch[2]);
219
- const tokensOut = outMatch === null ? null : num(outMatch[1] ?? outMatch[2]);
220
- return { tokensIn, tokensOut };
258
+ result.tokensIn = field(raw, 'input_tokens'); result.tokensOut = field(raw, 'output_tokens');
259
+ result.tokensCacheRead = field(raw, 'cache_read_input_tokens'); result.tokensCacheWrite = field(raw, 'cache_creation_input_tokens');
260
+ const creation = raw['cache_creation'];
261
+ if (creation != null) {
262
+ if (typeof creation !== 'object' || Array.isArray(creation)) {
263
+ result.usageDiagnostics!.push('invalid-cache-creation-shape'); result.tokensCacheWrite = null;
264
+ } else {
265
+ const nested = creation as Record<string, unknown>;
266
+ const a = field(nested, 'ephemeral_5m_input_tokens'); const b = field(nested, 'ephemeral_1h_input_tokens');
267
+ const nestedInvalid = invalid(nested, 'ephemeral_5m_input_tokens') || invalid(nested, 'ephemeral_1h_input_tokens');
268
+ const total = a !== null && b !== null ? count(a + b) : null;
269
+ if (a !== null && b !== null && total === null) result.usageDiagnostics!.push('cache-creation-overflow');
270
+ if (nestedInvalid || (a !== null && b !== null && total === null)) result.tokensCacheWrite = null;
271
+ else if (raw['cache_creation_input_tokens'] == null) result.tokensCacheWrite = total;
272
+ else if (result.tokensCacheWrite !== null && total !== null && total !== result.tokensCacheWrite) {
273
+ result.usageDiagnostics!.push('cache-creation-mismatch'); result.tokensCacheWrite = null;
274
+ }
275
+ }
276
+ }
277
+ result.tokensReasoning = field(raw, 'reasoning_output_tokens');
278
+ if (result.tokensReasoning !== null && result.tokensOut !== null && result.tokensReasoning > result.tokensOut) {
279
+ result.usageDiagnostics!.push('reasoning-exceeds-output'); result.tokensReasoning = null;
280
+ }
281
+ result.tokensTotal = field(raw, 'total_tokens');
282
+ const components = [result.tokensIn, result.tokensCacheRead, result.tokensCacheWrite, result.tokensOut];
283
+ const partitionKnown = components.every((n) => n !== null);
284
+ const derived = partitionKnown ? count(components.reduce<number>((n, v) => n + (v ?? 0), 0)) : null;
285
+ if (partitionKnown && derived === null) result.usageDiagnostics!.push('total-overflow');
286
+ if (invalid(raw, 'total_tokens')) result.totalDerivation = 'invalid-reported-total';
287
+ else if (raw['total_tokens'] == null) {
288
+ result.tokensTotal = derived; result.totalDerivation = derived === null ? 'not-recorded' : 'disjoint-dimensions';
289
+ } else result.totalDerivation = 'reported';
290
+ if (derived !== null && result.tokensTotal !== null && result.tokensTotal !== derived) result.usageDiagnostics!.push('total-split-mismatch');
291
+ result.reportedCostUsd = typeof chosen['total_cost_usd'] === 'number' && Number.isFinite(chosen['total_cost_usd']) && chosen['total_cost_usd'] >= 0 ? chosen['total_cost_usd'] : null;
292
+ result.reportedTotalBasis = 'raw-inclusive'; result.inputCacheSemantics = 'excludes-cache-read-write'; result.tokensSource = 'claude-envelope';
293
+ return result;
221
294
  }
222
295
 
223
- /**
224
- * The claude `--output-format json` USAGE fields, pinned to the LIVE envelope shape.
225
- *
226
- * MEASURED this session (`claude -p --output-format json`, sonnet):
227
- * `usage.input_tokens = 2`, `usage.output_tokens = 4`, alongside `cache_read_input_tokens` and
228
- * `cache_creation_input_tokens`. Only the two plain counters are reported — cache tokens are a
229
- * SEPARATE dimension `wf-budget-1` has no field for, and silently folding them into `tokensIn`
230
- * would inflate every cached run's cost picture. Added to the architecture's export list; see the
231
- * manifest.
232
- */
233
- export function extractClaudeUsage(stdout: string): { tokensIn: number | null; tokensOut: number | null } {
234
- const env = extractClaudeResult(String(stdout ?? ''));
235
- if (!env.ok) return { tokensIn: null, tokensOut: null };
236
- // re-scan for the envelope object itself: extractClaudeResult hands back only the text
237
- const lines = String(stdout ?? '').split(/\r?\n/);
238
- const candidates = [String(stdout ?? '').trim(), ...lines.map((l) => l.trim())].filter((c) => c.startsWith('{') && c.endsWith('}'));
239
- let usage: Record<string, unknown> | null = null;
240
- for (const c of candidates) {
241
- let obj: unknown;
242
- try {
243
- obj = JSON.parse(c);
244
- } catch {
245
- continue;
296
+ function codexStructuredOutput(stdout: string): { structured: boolean; valid: boolean; text: string; usage: ObservedUsage; reportedModel?: string | null } {
297
+ const lines = stdout.split(/\r?\n/).filter((line) => line.trim());
298
+ const events: Record<string, unknown>[] = []; let malformed = false; let recognized = false;
299
+ const types = new Set(['thread.started', 'turn.started', 'turn.completed', 'turn.failed', 'item.started', 'item.updated', 'item.completed', 'error']);
300
+ for (const line of lines) {
301
+ if (/"type"\s*:\s*"(?:thread\.|turn\.|item\.|error")/.test(line)) recognized = true;
302
+ try { const event = JSON.parse(line) as Record<string, unknown>;
303
+ if (event && typeof event === 'object' && typeof event['type'] === 'string' && (types.has(event['type']) || /^(thread|turn|item)\./.test(event['type']))) recognized = true;
304
+ events.push(event);
305
+ } catch { malformed = true; }
306
+ }
307
+ if (!recognized) return { structured: false, valid: true, text: stdout.trim(), usage: emptyUsage() };
308
+ const result = emptyUsage(); let failedTerminal = false; let text = ''; let threadId: string | null = null; const terminals: Record<string, unknown>[] = []; const reportedModels = new Set<string>();
309
+ for (const event of events) {
310
+ if (!event || typeof event !== 'object' || !types.has(String(event['type']))) { malformed = true; continue; }
311
+ if (typeof event['model'] === 'string' && event['model'].trim()) reportedModels.add(event['model']);
312
+ if (event['type'] === 'thread.started' && typeof event['thread_id'] === 'string') { if (threadId !== null && threadId !== event['thread_id']) malformed = true; threadId = event['thread_id']; }
313
+ if (event['type'] === 'item.completed' && event['item'] && typeof event['item'] === 'object') {
314
+ const item = event['item'] as Record<string, unknown>; if (item['type'] === 'agent_message' && typeof item['text'] === 'string') text = item['text'];
246
315
  }
247
- if (obj === null || typeof obj !== 'object') continue;
248
- const u = (obj as Record<string, unknown>)['usage'];
249
- if (u !== null && typeof u === 'object' && !Array.isArray(u)) usage = u as Record<string, unknown>;
316
+ if (event['type'] === 'turn.completed' || (event['type'] === 'turn.failed' && event['usage'] != null)) terminals.push(event);
317
+ if (event['type'] === 'turn.failed' || event['type'] === 'error') failedTerminal = true;
250
318
  }
251
- if (usage === null) return { tokensIn: null, tokensOut: null };
252
- const pick = (k: string): number | null => {
253
- const v = usage?.[k];
254
- return typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : null;
255
- };
256
- return { tokensIn: pick('input_tokens'), tokensOut: pick('output_tokens') };
319
+ const distinct = new Set(terminals.map((v) => { const u = v['usage'] && typeof v['usage'] === 'object' ? v['usage'] as Record<string,unknown> : {}; return JSON.stringify([v['turn_id'] ?? null, ...['input_tokens','cached_input_tokens','cache_write_input_tokens','output_tokens','reasoning_output_tokens','total_tokens'].map((key) => u[key] ?? null)]); }));
320
+ if (distinct.size !== 1) { malformed = true; result.usageDiagnostics!.push(distinct.size === 0 ? 'terminal-usage-unavailable' : 'conflicting-terminal-usage'); }
321
+ if (distinct.size === 1) {
322
+ const terminal = terminals[0]!; const raw = terminal['usage'];
323
+ if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
324
+ const usage = raw as Record<string, unknown>;
325
+ result.tokensIn = count(usage['input_tokens']); result.tokensOut = count(usage['output_tokens']);
326
+ result.tokensCacheRead = count(usage['cached_input_tokens']); result.tokensCacheWrite = count(usage['cache_write_input_tokens']);
327
+ result.tokensReasoning = count(usage['reasoning_output_tokens']);
328
+ const derived = result.tokensIn !== null && result.tokensOut !== null ? count(result.tokensIn + result.tokensOut) : null;
329
+ result.tokensTotal = count(usage['total_tokens']) ?? derived;
330
+ result.totalDerivation = count(usage['total_tokens']) !== null ? 'reported' : derived === null ? 'not-recorded' : 'input-plus-output';
331
+ if (derived !== null && result.tokensTotal !== derived) result.usageDiagnostics!.push('total-split-mismatch');
332
+ if (result.tokensCacheRead != null && result.tokensIn != null && result.tokensCacheRead > result.tokensIn) result.usageDiagnostics!.push('cache-exceeds-input');
333
+ if (result.tokensReasoning != null && result.tokensOut != null && result.tokensReasoning > result.tokensOut) result.usageDiagnostics!.push('reasoning-exceeds-output');
334
+ for (const [key, value] of Object.entries(usage)) if (key.endsWith('_tokens') && count(value) === null) result.usageDiagnostics!.push('invalid-counter:' + key);
335
+ result.tokensSource = 'codex-json'; result.reportedTotalBasis = 'raw-inclusive'; result.inputCacheSemantics = 'includes-cache-read-write';
336
+ result.usageSource = { schema: 'codex-exec-json', scope: 'terminal-turn', threadId, turnId: typeof terminal['turn_id'] === 'string' ? terminal['turn_id'] : null, receiptId: null };
337
+ } else malformed = true;
338
+ }
339
+ if (malformed) result.usageDiagnostics!.push('malformed-event-stream');
340
+ if (failedTerminal) result.usageDiagnostics!.push('runtime-turn-failed');
341
+ if (reportedModels.size > 1) result.usageDiagnostics!.push('provider-model-mismatch');
342
+ return { structured: true, valid: !malformed && !failedTerminal, text, usage: result, reportedModel: reportedModels.size === 1 ? [...reportedModels][0]! : null };
257
343
  }
258
344
 
259
345
  // ── the adapter factories (pure over the injected ChildRunner) ───────────────────────────────────
@@ -264,6 +350,7 @@ function failed(family: BridgeFamily, model: string | null, wallMs: number, reas
264
350
  text: null,
265
351
  family,
266
352
  modelUsed: model,
353
+ modelProvenance: 'probed-request',
267
354
  wallMs,
268
355
  tokensIn: null,
269
356
  tokensOut: null,
@@ -314,25 +401,15 @@ export function makeCodexExecDispatcher(run: ChildRunner, opts?: { bin?: string;
314
401
  cwd: req.cwd,
315
402
  detached: true,
316
403
  });
404
+ const parsed = codexStructuredOutput(String(r.stdout ?? ''));
405
+ const usage = parsed.structured ? parsed.usage : extractCodexTokens(r.stderr);
317
406
  const cls = classifyChildRun(r);
318
- if (cls.kind === 'timeout') return failed('openai', req.resolvedModelId, req.timeoutMs, 'dispatch-timeout', `the ${req.timeoutMs}ms deadline fired on step ${req.stepId}`);
319
- if (cls.kind === 'dead') return failed('openai', req.resolvedModelId, clock() - t0, 'dispatch-dead', cls.detail);
320
- const text = String(r.stdout ?? '').trim();
321
- if (cls.exitCode !== 0 || text === '') {
322
- return failed('openai', req.resolvedModelId, clock() - t0, 'dispatch-dead',
323
- `codex exited ${cls.exitCode} with ${text === '' ? 'NO stdout' : String(text.length) + ' chars of stdout'} — a clean exit with nothing to read is the spawned-but-mute case, not an empty success`);
324
- }
325
- const tokens = extractCodexTokens(r.stderr);
326
- return {
327
- outcome: 'ok',
328
- text,
329
- family: 'openai',
330
- modelUsed: req.resolvedModelId,
331
- wallMs: clock() - t0,
332
- tokensIn: tokens.tokensIn,
333
- tokensOut: tokens.tokensOut,
334
- tokensSource: tokens.tokensIn === null && tokens.tokensOut === null ? null : 'codex-stderr',
335
- };
407
+ if (cls.kind === 'timeout') return { ...failed('openai', req.resolvedModelId, req.timeoutMs, 'dispatch-timeout', `the ${req.timeoutMs}ms deadline fired on step ${req.stepId}`), ...usage };
408
+ if (cls.kind === 'dead') return { ...failed('openai', req.resolvedModelId, clock() - t0, 'dispatch-dead', cls.detail), ...usage };
409
+ if (cls.exitCode !== 0 || !parsed.valid || parsed.text === '') return { ...failed('openai', req.resolvedModelId, clock() - t0, 'dispatch-dead', 'Codex exited nonzero, had an invalid structured event stream, or was spawned-but-mute with no final text'), ...usage };
410
+ return { outcome: 'ok', text: parsed.text, family: 'openai', modelUsed: parsed.reportedModel ?? req.resolvedModelId,
411
+ modelProvenance: parsed.reportedModel ? 'provider-reported' : 'probed-request', wallMs: clock() - t0, ...usage };
412
+
336
413
  },
337
414
  };
338
415
  }
@@ -380,33 +457,15 @@ export function makeClaudePDispatcher(run: ChildRunner, opts?: { bin?: string; i
380
457
  cwd: fileMode ? req.cwd : isolatedCwd(),
381
458
  detached: true,
382
459
  });
460
+ const usage = extractClaudeUsage(r.stdout);
383
461
  const cls = classifyChildRun(r);
384
- if (cls.kind === 'timeout') return failed('claude', req.resolvedModelId, req.timeoutMs, 'dispatch-timeout', `the ${req.timeoutMs}ms deadline fired on step ${req.stepId}`);
385
- if (cls.kind === 'dead') return failed('claude', req.resolvedModelId, clock() - t0, 'dispatch-dead', cls.detail);
386
- // The EXIT CODE and the ENVELOPE must agree (Step-8 HIGH-10). A parseable success envelope
387
- // from a process that exited nonzero is a CONTRADICTION, not a success: the runtime told us
388
- // twice and the two answers differ, so believing the friendlier one is how a failed dispatch
389
- // becomes a green step. The codex adapter already required exit 0; this one did not.
390
- if (cls.exitCode !== 0) {
391
- return failed('claude', req.resolvedModelId, clock() - t0, 'dispatch-dead',
392
- `claude exited ${cls.exitCode} — a nonzero exit is a failed dispatch even when stdout carries a parseable success envelope; the two disagree and the exit code is the runtime's own verdict`);
393
- }
462
+ if (cls.kind === 'timeout') return { ...failed('claude', req.resolvedModelId, req.timeoutMs, 'dispatch-timeout', `the ${req.timeoutMs}ms deadline fired on step ${req.stepId}`), ...usage };
463
+ if (cls.kind === 'dead') return { ...failed('claude', req.resolvedModelId, clock() - t0, 'dispatch-dead', cls.detail), ...usage };
464
+ if (cls.exitCode !== 0) return { ...failed('claude', req.resolvedModelId, clock() - t0, 'dispatch-dead', `claude exited ${cls.exitCode} — a nonzero exit is a failed dispatch even with a success envelope`), ...usage };
394
465
  const env = extractClaudeResult(r.stdout);
395
- if (!env.ok) {
396
- return failed('claude', req.resolvedModelId, clock() - t0, 'dispatch-dead',
397
- `claude exited ${cls.exitCode} but the reply is not readable as a result envelope: ${env.detail}`);
398
- }
399
- const usage = extractClaudeUsage(r.stdout);
400
- return {
401
- outcome: 'ok',
402
- text: env.text,
403
- family: 'claude',
404
- modelUsed: req.resolvedModelId,
405
- wallMs: clock() - t0,
406
- tokensIn: usage.tokensIn,
407
- tokensOut: usage.tokensOut,
408
- tokensSource: usage.tokensIn === null && usage.tokensOut === null ? null : 'claude-envelope',
409
- };
466
+ if (!env.ok) return { ...failed('claude', req.resolvedModelId, clock() - t0, 'dispatch-dead', `reply is not readable as a result envelope: ${env.detail}`), ...usage };
467
+ return { outcome: 'ok', text: env.text, family: 'claude', modelUsed: req.resolvedModelId, modelProvenance: 'probed-request', wallMs: clock() - t0, ...usage };
468
+
410
469
  },
411
470
  };
412
471
  }
@@ -164,7 +164,8 @@ export interface WfRunState {
164
164
  dispatcherOverride?: boolean;
165
165
  }
166
166
 
167
- export interface WfBudgetRow {
167
+ export interface WfBudgetRow extends Pick<DispatchResult, 'tokensTotal' | 'tokensCacheRead' | 'tokensCacheWrite' | 'tokensReasoning' | 'reportedTotalBasis' | 'inputCacheSemantics' | 'reportedCostUsd' | 'usageDiagnostics' | 'usageSource' | 'totalDerivation'> {
168
+ projectRoot?: string;
168
169
  schema: typeof WF_BUDGET_ROW_SCHEMA;
169
170
  kind: 'stage' | 'probe';
170
171
  runId: string;
@@ -175,10 +176,17 @@ export interface WfBudgetRow {
175
176
  attempt: number | null;
176
177
  family: BridgeFamily;
177
178
  model: string | null;
179
+ modelProvenance?: string;
180
+ requestedModel?: string | null;
178
181
  wallMs: number;
179
182
  tokensIn: number | null;
180
183
  tokensOut: number | null;
181
- tokensSource: 'claude-envelope' | 'codex-stderr' | null;
184
+ tokensSource: DispatchResult['tokensSource'];
185
+ phase?: string | null;
186
+ role?: string | null;
187
+ tier?: string | null;
188
+ mode?: string | null;
189
+ estimate?: unknown;
182
190
  outcome: 'ok' | 'null' | 'error' | null;
183
191
  timeoutMs: number | null;
184
192
  }
@@ -239,6 +247,7 @@ export interface RunnerInputs {
239
247
  wallClockExtraMs: number | null;
240
248
  runnerVersion: string;
241
249
  cwdRoot: string;
250
+ projectRoot?: string;
242
251
  }
243
252
 
244
253
  /** Small, dependency-free 64-bit FNV — the same shape the checkpoint plane uses, kept local so this
@@ -1063,6 +1072,7 @@ export async function runWorkflow(inputs: RunnerInputs, pre: PreflightOk, deps:
1063
1072
  schema: WF_BUDGET_ROW_SCHEMA,
1064
1073
  kind: 'probe',
1065
1074
  runId: inputs.runId,
1075
+ projectRoot: inputs.projectRoot ?? inputs.cwdRoot,
1066
1076
  dispatchSeq: null,
1067
1077
  stepId: null,
1068
1078
  itemKey: null,
@@ -1073,6 +1083,14 @@ export async function runWorkflow(inputs: RunnerInputs, pre: PreflightOk, deps:
1073
1083
  tokensIn: null,
1074
1084
  tokensOut: null,
1075
1085
  tokensSource: null,
1086
+ tokensTotal: null,
1087
+ tokensCacheRead: null,
1088
+ tokensCacheWrite: null,
1089
+ tokensReasoning: null,
1090
+ reportedTotalBasis: 'unknown',
1091
+ inputCacheSemantics: 'unknown',
1092
+ reportedCostUsd: null,
1093
+ usageDiagnostics: ['probe-usage-not-recorded'],
1076
1094
  outcome: null,
1077
1095
  timeoutMs: null,
1078
1096
  });
@@ -1500,16 +1518,34 @@ async function dispatchOnce(
1500
1518
  schema: WF_BUDGET_ROW_SCHEMA,
1501
1519
  kind: 'stage',
1502
1520
  runId: ctx.inputs.runId,
1521
+ projectRoot: ctx.inputs.projectRoot ?? ctx.inputs.cwdRoot,
1503
1522
  dispatchSeq,
1504
1523
  stepId: spec.stepId,
1505
1524
  itemKey,
1506
1525
  attempt,
1507
1526
  family,
1508
- model: res.modelUsed ?? model,
1527
+ model: res.modelUsed,
1528
+ requestedModel: model,
1529
+ modelProvenance: res.modelProvenance ?? (res.modelUsed === null ? 'not-recorded' : 'dispatcher-reported'),
1509
1530
  wallMs,
1510
1531
  tokensIn: res.tokensIn,
1511
1532
  tokensOut: res.tokensOut,
1512
1533
  tokensSource: res.tokensSource,
1534
+ tokensTotal: res.tokensTotal ?? null,
1535
+ totalDerivation: res.totalDerivation ?? 'not-recorded',
1536
+ tokensCacheRead: res.tokensCacheRead ?? null,
1537
+ tokensCacheWrite: res.tokensCacheWrite ?? null,
1538
+ tokensReasoning: res.tokensReasoning ?? null,
1539
+ reportedTotalBasis: res.reportedTotalBasis ?? 'unknown',
1540
+ inputCacheSemantics: res.inputCacheSemantics ?? 'unknown',
1541
+ reportedCostUsd: res.reportedCostUsd ?? null,
1542
+ usageDiagnostics: res.usageDiagnostics ?? [],
1543
+ ...(res.usageSource !== undefined ? { usageSource: res.usageSource } : {}),
1544
+ phase: spec.phase,
1545
+ role: spec.role ?? null,
1546
+ tier: spec.tier ?? null,
1547
+ mode: spec.mode ?? null,
1548
+ estimate: spec.estimate ?? null,
1513
1549
  outcome,
1514
1550
  timeoutMs,
1515
1551
  });
@@ -1699,7 +1735,7 @@ function appendLedger(ctx: RunCtx, outcome: string): void {
1699
1735
  outcome,
1700
1736
  date: null,
1701
1737
  });
1702
- if (line !== null) ctx.deps.store.appendLedgerLine(line);
1738
+ if (line !== null) ctx.deps.store.appendLedgerLine(JSON.stringify({ ...JSON.parse(line), summary: true, sourceProjection: 'workflow-budget', workflowRunId: ctx.inputs.runId }));
1703
1739
  }
1704
1740
 
1705
1741
  /** PAUSE — flush WITHOUT `run.closed` (parity with the render's top-level terminal return, which
@@ -1,58 +0,0 @@
1
- /**
2
- * Заполнение пустых полей стоимости в леджере прогонов ИЗ ЗАПИСИ ХОСТА.
3
- *
4
- * ЗАЧЕМ. Запись 3c026d5d: стоимость фичи на три четверти набита руками. ИЗМЕРЕНО 2026-09-19 по
5
- * живому `.dz/feature-adr/run-cost-ledger.jsonl`: 393 строки, из них 293 без числа токенов.
6
- * Числа при этом НЕ пропали — для прогонов Workflow они лежат в записи хоста
7
- * `~/.claude/projects/<проект>/<сессия>/workflows/wf_<runId>.json`, где есть `totalTokens`,
8
- * `durationMs` и `agentCount`. Песочница воркфлоу их видеть не может (у неё нет файловой системы
9
- * и нет уведомления о завершении), поэтому соединение делается СНАРУЖИ и ПОСЛЕ прогона.
10
- *
11
- * ЧЕСТНАЯ ДОСЯГАЕМОСТЬ, и она меньше, чем подразумевает запись. Из 293 пустых строк:
12
- * • 75 — прогоны Workflow (`auto:true`); только их и может достать это соединение,
13
- * и лишь 25 из них несут `runId`, по которому запись хоста находится СЕГОДНЯ;
14
- * • 218 — ручные круги ведущего, для которых записи хоста НЕ СУЩЕСТВУЕТ ПО ПОСТРОЕНИЮ:
15
- * Workflow не запускался, и стоимость этим путём не восстановима в принципе.
16
- * То есть механизм закрывает не «три четверти», а максимум 26% разрыва, а сегодня 9%.
17
- * Остальное — задача ПИСАТЕЛЯ (проставлять пригодный `runId`) и отдельный вопрос про то,
18
- * чем мерить ручные круги.
19
- *
20
- * ПРОВЕНАНС ОБЯЗАТЕЛЕН. Заполненное поле помечается `costSource: 'derived'`, потому что
21
- * выведенное число и набранное руками — разные по доверию величины, и строка, где их нельзя
22
- * различить, обесценивает обе.
23
- */
24
- /** Стоимость прогона, вычитанная из записи хоста. */
25
- export interface DerivedRunCost {
26
- readonly tokens: number;
27
- readonly minutes: number;
28
- readonly agents: number | null;
29
- }
30
- /** Строка леджера в той части, которая нас касается. */
31
- export interface LedgerCostRow {
32
- readonly runId?: unknown;
33
- readonly tokens?: unknown;
34
- readonly minutes?: unknown;
35
- readonly agents?: unknown;
36
- readonly costSource?: unknown;
37
- readonly [k: string]: unknown;
38
- }
39
- /**
40
- * Прочитать стоимость из записи хоста. `null`, если запись не несёт обязательных чисел —
41
- * частичная запись НЕ даёт частичного заполнения: половина выведенной строки хуже пустой,
42
- * потому что выглядит как измерение.
43
- */
44
- export declare function readHostRunCost(record: unknown): DerivedRunCost | null;
45
- /**
46
- * Заполнить ТОЛЬКО пустые поля. Уже набранное руками число НИКОГДА не перетирается: оно могло
47
- * быть исправлено человеком по другим основаниям, и выведенное значение не имеет права его
48
- * вытеснять. Возвращает `null`, если заполнять нечего — чтобы вызывающий не переписывал файл зря.
49
- */
50
- export declare function fillLedgerCost(row: LedgerCostRow, derived: DerivedRunCost): LedgerCostRow | null;
51
- /** Три исхода на строку — и «нечего заполнять» отделено от «не нашли запись». */
52
- export type FillOutcome = 'filled' | 'nothing-to-fill' | 'no-host-record';
53
- /** Решение по одной строке: чем она разрешается и почему. */
54
- export declare function decideFill(row: LedgerCostRow, record: unknown): {
55
- outcome: FillOutcome;
56
- next: LedgerCostRow | null;
57
- };
58
- //# sourceMappingURL=ledger-cost-fill.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ledger-cost-fill.d.ts","sourceRoot":"","sources":["../src/ledger-cost-fill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAUtE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,GAAG,aAAa,GAAG,IAAI,CAUhG;AAED,iFAAiF;AACjF,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAE1E,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,GAAG;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAAA;CAAE,CAKpH"}
@@ -1,78 +0,0 @@
1
- /**
2
- * Заполнение пустых полей стоимости в леджере прогонов ИЗ ЗАПИСИ ХОСТА.
3
- *
4
- * ЗАЧЕМ. Запись 3c026d5d: стоимость фичи на три четверти набита руками. ИЗМЕРЕНО 2026-09-19 по
5
- * живому `.dz/feature-adr/run-cost-ledger.jsonl`: 393 строки, из них 293 без числа токенов.
6
- * Числа при этом НЕ пропали — для прогонов Workflow они лежат в записи хоста
7
- * `~/.claude/projects/<проект>/<сессия>/workflows/wf_<runId>.json`, где есть `totalTokens`,
8
- * `durationMs` и `agentCount`. Песочница воркфлоу их видеть не может (у неё нет файловой системы
9
- * и нет уведомления о завершении), поэтому соединение делается СНАРУЖИ и ПОСЛЕ прогона.
10
- *
11
- * ЧЕСТНАЯ ДОСЯГАЕМОСТЬ, и она меньше, чем подразумевает запись. Из 293 пустых строк:
12
- * • 75 — прогоны Workflow (`auto:true`); только их и может достать это соединение,
13
- * и лишь 25 из них несут `runId`, по которому запись хоста находится СЕГОДНЯ;
14
- * • 218 — ручные круги ведущего, для которых записи хоста НЕ СУЩЕСТВУЕТ ПО ПОСТРОЕНИЮ:
15
- * Workflow не запускался, и стоимость этим путём не восстановима в принципе.
16
- * То есть механизм закрывает не «три четверти», а максимум 26% разрыва, а сегодня 9%.
17
- * Остальное — задача ПИСАТЕЛЯ (проставлять пригодный `runId`) и отдельный вопрос про то,
18
- * чем мерить ручные круги.
19
- *
20
- * ПРОВЕНАНС ОБЯЗАТЕЛЕН. Заполненное поле помечается `costSource: 'derived'`, потому что
21
- * выведенное число и набранное руками — разные по доверию величины, и строка, где их нельзя
22
- * различить, обесценивает обе.
23
- */
24
- const isFiniteNumber = (v) => typeof v === 'number' && Number.isFinite(v);
25
- /**
26
- * Прочитать стоимость из записи хоста. `null`, если запись не несёт обязательных чисел —
27
- * частичная запись НЕ даёт частичного заполнения: половина выведенной строки хуже пустой,
28
- * потому что выглядит как измерение.
29
- */
30
- export function readHostRunCost(record) {
31
- if (record === null || typeof record !== 'object')
32
- return null;
33
- const r = record;
34
- if (!isFiniteNumber(r['totalTokens']) || !isFiniteNumber(r['durationMs']))
35
- return null;
36
- if (r['totalTokens'] < 0 || r['durationMs'] < 0)
37
- return null;
38
- return {
39
- tokens: r['totalTokens'],
40
- minutes: Math.round(r['durationMs'] / 60_000),
41
- agents: isFiniteNumber(r['agentCount']) ? r['agentCount'] : null,
42
- };
43
- }
44
- /**
45
- * Заполнить ТОЛЬКО пустые поля. Уже набранное руками число НИКОГДА не перетирается: оно могло
46
- * быть исправлено человеком по другим основаниям, и выведенное значение не имеет права его
47
- * вытеснять. Возвращает `null`, если заполнять нечего — чтобы вызывающий не переписывал файл зря.
48
- */
49
- export function fillLedgerCost(row, derived) {
50
- const next = { ...row };
51
- let filled = false;
52
- if (!isFiniteNumber(row['tokens'])) {
53
- next['tokens'] = derived.tokens;
54
- filled = true;
55
- }
56
- if (!isFiniteNumber(row['minutes'])) {
57
- next['minutes'] = derived.minutes;
58
- filled = true;
59
- }
60
- if (!isFiniteNumber(row['agents']) && derived.agents !== null) {
61
- next['agents'] = derived.agents;
62
- filled = true;
63
- }
64
- if (!filled)
65
- return null;
66
- // Провенанс ставится ТОЛЬКО когда что-то действительно заполнено.
67
- next['costSource'] = 'derived';
68
- return next;
69
- }
70
- /** Решение по одной строке: чем она разрешается и почему. */
71
- export function decideFill(row, record) {
72
- const derived = record === undefined ? null : readHostRunCost(record);
73
- if (derived === null)
74
- return { outcome: 'no-host-record', next: null };
75
- const next = fillLedgerCost(row, derived);
76
- return next === null ? { outcome: 'nothing-to-fill', next: null } : { outcome: 'filled', next };
77
- }
78
- //# sourceMappingURL=ledger-cost-fill.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ledger-cost-fill.js","sourceRoot":"","sources":["../src/ledger-cost-fill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAmBH,MAAM,cAAc,GAAG,CAAC,CAAU,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAEhG;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAe;IAC7C,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC/D,MAAM,CAAC,GAAG,MAAiC,CAAC;IAC5C,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACvF,IAAI,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,OAAO;QACL,MAAM,EAAE,CAAC,CAAC,aAAa,CAAC;QACxB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC;QAC7C,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;KACjE,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAkB,EAAE,OAAuB;IACxE,MAAM,IAAI,GAA4B,EAAE,GAAG,GAAG,EAAE,CAAC;IACjD,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;QAAC,MAAM,GAAG,IAAI,CAAC;IAAC,CAAC;IACvF,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QAAC,IAAI,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QAAC,MAAM,GAAG,IAAI,CAAC;IAAC,CAAC;IAC1F,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;QAAC,MAAM,GAAG,IAAI,CAAC;IAAC,CAAC;IAClH,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,kEAAkE;IAClE,IAAI,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC;IAC/B,OAAO,IAAqB,CAAC;AAC/B,CAAC;AAKD,6DAA6D;AAC7D,MAAM,UAAU,UAAU,CAAC,GAAkB,EAAE,MAAe;IAC5D,MAAM,OAAO,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IACtE,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACvE,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC1C,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAClG,CAAC"}