@cat-factory/orchestration 0.159.2 → 0.161.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 (34) hide show
  1. package/dist/container/dependencies.d.ts +16 -1
  2. package/dist/container/dependencies.d.ts.map +1 -1
  3. package/dist/container/platform-modules.d.ts.map +1 -1
  4. package/dist/container/platform-modules.js +15 -0
  5. package/dist/container/platform-modules.js.map +1 -1
  6. package/dist/container.d.ts +7 -0
  7. package/dist/container.d.ts.map +1 -1
  8. package/dist/container.js +1 -0
  9. package/dist/container.js.map +1 -1
  10. package/dist/index.d.ts +3 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/modules/debug/RunDebugService.d.ts +96 -0
  15. package/dist/modules/debug/RunDebugService.d.ts.map +1 -0
  16. package/dist/modules/debug/RunDebugService.js +190 -0
  17. package/dist/modules/debug/RunDebugService.js.map +1 -0
  18. package/dist/modules/debug/debug.logic.d.ts +104 -0
  19. package/dist/modules/debug/debug.logic.d.ts.map +1 -0
  20. package/dist/modules/debug/debug.logic.js +403 -0
  21. package/dist/modules/debug/debug.logic.js.map +1 -0
  22. package/dist/modules/debug/promptMessages.d.ts +27 -0
  23. package/dist/modules/debug/promptMessages.d.ts.map +1 -0
  24. package/dist/modules/debug/promptMessages.js +165 -0
  25. package/dist/modules/debug/promptMessages.js.map +1 -0
  26. package/dist/modules/execution/RunStateMachine.d.ts +4 -0
  27. package/dist/modules/execution/RunStateMachine.d.ts.map +1 -1
  28. package/dist/modules/execution/RunStateMachine.js +31 -4
  29. package/dist/modules/execution/RunStateMachine.js.map +1 -1
  30. package/dist/modules/observability/observability.logic.d.ts +10 -0
  31. package/dist/modules/observability/observability.logic.d.ts.map +1 -1
  32. package/dist/modules/observability/observability.logic.js +1 -1
  33. package/dist/modules/observability/observability.logic.js.map +1 -1
  34. package/package.json +11 -11
@@ -0,0 +1,403 @@
1
+ import { foldRollupTotals, foldRollupsByAgentKind, foldRollupsByPhase } from '@cat-factory/kernel';
2
+ import { cacheHitRate, classifyCall, isWarningFinishReason, outputHeadroomRatio, transportOverheadRatio, } from '../observability/observability.logic.js';
3
+ // Pure projections + derivations behind the remote debugging surface. Everything here is a
4
+ // total function of already-fetched data — no clock, no repository, no I/O — so the shapes an
5
+ // external client depends on are unit-testable without a store, and the service above stays a
6
+ // thin "fetch the bounded things, hand them to these" layer.
7
+ /** Cap on the container post-mortem inlined on a step (it is a log tail, not a document). */
8
+ export const MAX_EVICTION_DETAIL_CHARS = 4_000;
9
+ /**
10
+ * Count Unicode CODE POINTS — the unit the stores measure in. SQL `length()`/`substr()` count
11
+ * code points on both SQLite and Postgres, while JS `.length` counts UTF-16 units, so an
12
+ * astral-plane character (an emoji) is one to the store and two to `.length`. Every `chars`/
13
+ * `totalChars` on this surface is in code points; mixing the units made `truncated` lie on
14
+ * exactly the boundary case it exists for (a body cut by SQL whose UTF-16 length happened to
15
+ * equal the code-point total read as untruncated).
16
+ */
17
+ function codePointLength(text) {
18
+ let count = 0;
19
+ for (let i = 0; i < text.length; i += 1) {
20
+ count += 1;
21
+ const unit = text.charCodeAt(i);
22
+ // A high surrogate leads a two-unit pair encoding one code point; skip its partner.
23
+ if (unit >= 0xd800 && unit <= 0xdbff)
24
+ i += 1;
25
+ }
26
+ return count;
27
+ }
28
+ /** Advance a UTF-16 index by `count` code points (never landing inside a surrogate pair). */
29
+ function advanceCodePoints(text, from, count) {
30
+ let index = from;
31
+ for (let taken = 0; taken < count && index < text.length; taken += 1) {
32
+ const unit = text.charCodeAt(index);
33
+ index += unit >= 0xd800 && unit <= 0xdbff ? 2 : 1;
34
+ }
35
+ return index;
36
+ }
37
+ /**
38
+ * Slice a stored body to a caller's window and SAY SO. The metadata matters more than the
39
+ * text: a bare truncated string reads exactly like a short one, so a model handed the first
40
+ * 2 kB of a 40 kB reply would confidently report that the agent said almost nothing.
41
+ *
42
+ * `budget` of 0 returns no text at all while still reporting the full size — that is the
43
+ * shape a sweep uses, and the reason a size-only page is still worth reading. `offset`
44
+ * starts the window later, which is how the tail of a large body is reached; past the end
45
+ * it returns an empty slice whose `offset` is clamped to the total, so
46
+ * `offset + chars <= totalChars` always holds.
47
+ *
48
+ * Budgets, offsets and sizes are CODE POINTS (see {@link codePointLength}), matching the
49
+ * SQL-sliced bodies — and the cut walks whole code points, so it can never split a
50
+ * surrogate pair and hand the caller a lone half of one.
51
+ */
52
+ export function sliceText(text, budget, offset = 0) {
53
+ const total = codePointLength(text);
54
+ const start = Math.max(0, Math.min(offset, total));
55
+ const chars = Math.max(0, Math.min(budget, total - start));
56
+ let sliced = text;
57
+ if (start > 0 || chars < total) {
58
+ const begin = advanceCodePoints(text, 0, start);
59
+ const end = advanceCodePoints(text, begin, chars);
60
+ sliced = text.slice(begin, end);
61
+ }
62
+ return {
63
+ text: sliced,
64
+ chars,
65
+ offset: start,
66
+ totalChars: total,
67
+ truncated: chars < total,
68
+ };
69
+ }
70
+ /**
71
+ * Project a body the STORE already sliced. The repository returns `{ text, totalChars }`
72
+ * because it cut the body in SQL (so the untaken bytes never left the database); this only
73
+ * re-derives the fields the wire shape adds. Deliberately does NOT re-slice: `text` is
74
+ * already within the window, and slicing again would silently disagree with `totalChars`.
75
+ *
76
+ * `offset` is the window start the caller asked the store for (0 for a list slice), clamped
77
+ * to the total so an ask past the end reports where the body actually stops. A search's
78
+ * per-body `matchOffset` rides through untouched — null (no match in this body) and absent
79
+ * (no search ran) stay distinct on the wire.
80
+ *
81
+ * `chars` and the truncation check count CODE POINTS, because `totalChars` came from SQL
82
+ * `length()` which counts the same — a JS `.length` here reads a SQL-cut emoji-bearing body
83
+ * as untruncated (see {@link codePointLength}).
84
+ */
85
+ export function toDebugText(slice, offset = 0) {
86
+ const chars = codePointLength(slice.text);
87
+ return {
88
+ text: slice.text,
89
+ chars,
90
+ offset: Math.max(0, Math.min(offset, slice.totalChars)),
91
+ totalChars: slice.totalChars,
92
+ truncated: chars < slice.totalChars,
93
+ ...(slice.matchOffset !== undefined ? { matchOffset: slice.matchOffset } : {}),
94
+ };
95
+ }
96
+ /** Project a persisted run onto the lean summary every debug list and overview leads with. */
97
+ export function toDebugRunSummary(execution) {
98
+ return {
99
+ runId: execution.id,
100
+ blockId: execution.blockId,
101
+ pipelineId: execution.pipelineId,
102
+ pipelineName: execution.pipelineName,
103
+ status: execution.status,
104
+ createdAt: execution.createdAt ?? 0,
105
+ currentStep: execution.currentStep,
106
+ stepCount: execution.steps.length,
107
+ failure: execution.failure ?? null,
108
+ };
109
+ }
110
+ /** Project one pipeline step onto the debug view (identity + clocks + container mortality). */
111
+ export function toDebugRunStep(step, index) {
112
+ const detail = step.firstEvictionDetail;
113
+ return {
114
+ index,
115
+ agentKind: step.agentKind,
116
+ state: step.state,
117
+ progress: step.progress,
118
+ model: step.model ?? null,
119
+ skipped: step.skipped ?? false,
120
+ startedAt: step.startedAt ?? null,
121
+ finishedAt: step.finishedAt ?? null,
122
+ lastActivityAt: step.lastActivityAt ?? null,
123
+ subtasks: step.subtasks
124
+ ? {
125
+ completed: step.subtasks.completed,
126
+ inProgress: step.subtasks.inProgress,
127
+ total: step.subtasks.total,
128
+ }
129
+ : null,
130
+ outputChars: (step.output ?? '').length,
131
+ hasStructuredResult: step.custom != null,
132
+ evictionRecoveries: step.evictionRecoveries ?? 0,
133
+ firstEvictionDetail: detail ? sliceText(detail, MAX_EVICTION_DETAIL_CHARS) : null,
134
+ };
135
+ }
136
+ /**
137
+ * Project a bounded call-page row onto the wire shape. `bodyOffset` is the window start the
138
+ * store's slices were taken at (a point read's `?bodyOffset=`; always 0 on a list row).
139
+ */
140
+ export function toDebugLlmCall(call, bodyOffset = 0) {
141
+ return {
142
+ callId: call.id,
143
+ runId: call.executionId,
144
+ agentKind: call.agentKind,
145
+ provider: call.provider,
146
+ model: call.model,
147
+ createdAt: call.createdAt,
148
+ outcome: classifyCall(call),
149
+ ok: call.ok,
150
+ httpStatus: call.httpStatus,
151
+ errorMessage: call.errorMessage,
152
+ finishReason: call.finishReason,
153
+ streaming: call.streaming,
154
+ phase: call.phase,
155
+ turnIndex: call.turnIndex,
156
+ messageCount: call.messageCount,
157
+ toolCount: call.toolCount,
158
+ requestMaxTokens: call.requestMaxTokens,
159
+ promptTokens: call.promptTokens,
160
+ cacheReadTokens: call.cacheReadTokens,
161
+ cacheWriteTokens: call.cacheWriteTokens,
162
+ completionTokens: call.completionTokens,
163
+ totalTokens: call.totalTokens,
164
+ upstreamMs: call.upstreamMs,
165
+ overheadMs: call.overheadMs,
166
+ totalMs: call.totalMs,
167
+ elidedLeadingMessages: call.promptPrefixCount,
168
+ prompt: toDebugText(call.prompt, bodyOffset),
169
+ response: toDebugText(call.response, bodyOffset),
170
+ reasoning: toDebugText(call.reasoning, bodyOffset),
171
+ };
172
+ }
173
+ /** Project a snapshot index row onto the wire shape. */
174
+ export function toDebugAgentContextEntry(row) {
175
+ return {
176
+ snapshotId: row.id,
177
+ agentKind: row.agentKind,
178
+ stepIndex: row.stepIndex,
179
+ createdAt: row.createdAt,
180
+ model: row.model,
181
+ harness: row.harness,
182
+ systemPromptChars: row.systemPromptChars,
183
+ userPromptChars: row.userPromptChars,
184
+ fragmentsChars: row.fragmentsChars,
185
+ contextFilesChars: row.contextFilesChars,
186
+ };
187
+ }
188
+ /**
189
+ * Project a whole snapshot onto the wire shape, budgeting EVERY body INDEPENDENTLY rather
190
+ * than against one shared allowance. A snapshot routinely holds one enormous injected file
191
+ * next to the prompts, and a shared budget spent in array order would leave the prompts — the
192
+ * thing a reader almost always came for — empty because a README came first.
193
+ */
194
+ export function toDebugAgentContextDetail(snapshot, bodyChars, bodyOffset = 0) {
195
+ return {
196
+ snapshotId: snapshot.id,
197
+ runId: snapshot.executionId,
198
+ agentKind: snapshot.agentKind,
199
+ stepIndex: snapshot.stepIndex,
200
+ createdAt: snapshot.createdAt,
201
+ model: snapshot.model,
202
+ harness: snapshot.harness,
203
+ systemPrompt: sliceText(snapshot.systemPrompt, bodyChars, bodyOffset),
204
+ userPrompt: sliceText(snapshot.userPrompt, bodyChars, bodyOffset),
205
+ fragments: snapshot.fragments.map((f) => ({
206
+ id: f.id,
207
+ body: sliceText(f.body, bodyChars, bodyOffset),
208
+ })),
209
+ contextFiles: snapshot.contextFiles.map((f) => ({
210
+ path: f.path,
211
+ title: f.title,
212
+ url: f.url,
213
+ content: sliceText(f.content, bodyChars, bodyOffset),
214
+ })),
215
+ extras: snapshot.extras,
216
+ };
217
+ }
218
+ /**
219
+ * Fold the store's `(agentKind, phase)` rollup cells into the run-level totals + the two
220
+ * breakdowns the overview reports. Built from {@link LlmCallMetricSummary} — the aggregate the
221
+ * store computes without touching a text column — rather than from the calls themselves, so a
222
+ * 3,000-call run costs one GROUP BY here instead of reading 3,000 rows to add them up in
223
+ * JavaScript.
224
+ *
225
+ * Both breakdowns are folds over the SAME cells (kernel's `foldRollupsBy*`), so they total
226
+ * identically to each other and to `totals` by construction — the alternative, one aggregate
227
+ * per axis, could only ever produce two answers to the same question.
228
+ *
229
+ * The per-kind output reuses the metrics EXPORT's shapes on purpose: both describe the same
230
+ * run's model activity, and two independently-derived totals would eventually disagree.
231
+ */
232
+ export function foldLlmRollup(summaries) {
233
+ const byAgentKind = foldRollupsByAgentKind(summaries).map((s) => ({
234
+ agentKind: s.agentKind,
235
+ calls: s.calls,
236
+ promptTokens: s.promptTokens,
237
+ cacheReadTokens: s.cacheReadTokens,
238
+ cacheWriteTokens: s.cacheWriteTokens,
239
+ cacheHitRate: cacheHitRate(s.cacheReadTokens, s.cacheWriteTokens, s.promptTokens),
240
+ completionTokens: s.completionTokens,
241
+ peakCompletionTokens: s.peakCompletionTokens,
242
+ maxOutputTokens: s.maxOutputTokens,
243
+ outputHeadroomRatio: outputHeadroomRatio(s.peakCompletionTokens, s.maxOutputTokens),
244
+ truncatedCalls: s.truncatedCalls,
245
+ upstreamMs: s.upstreamMs,
246
+ overheadMs: s.overheadMs,
247
+ transportOverheadRatio: transportOverheadRatio(s.upstreamMs, s.overheadMs),
248
+ errors: s.errors,
249
+ warnings: s.warnings,
250
+ }));
251
+ const phases = foldRollupsByPhase(summaries);
252
+ // Denominator for each phase's share of the carry cost. Folded from the phase rows
253
+ // themselves rather than re-summed off `summaries`, so the shares provably sum to 1.
254
+ const runCarryCost = phases.reduce((acc, p) => acc + p.carryCostTokens, 0);
255
+ const byPhase = phases
256
+ .map((p) => ({
257
+ phase: p.phase,
258
+ calls: p.calls,
259
+ promptTokens: p.promptTokens,
260
+ cacheReadTokens: p.cacheReadTokens,
261
+ cacheWriteTokens: p.cacheWriteTokens,
262
+ cacheHitRate: cacheHitRate(p.cacheReadTokens, p.cacheWriteTokens, p.promptTokens),
263
+ completionTokens: p.completionTokens,
264
+ carryCostTokens: p.carryCostTokens,
265
+ carryCostShare: runCarryCost > 0 ? p.carryCostTokens / runCarryCost : null,
266
+ upstreamMs: p.upstreamMs,
267
+ overheadMs: p.overheadMs,
268
+ errors: p.errors,
269
+ warnings: p.warnings,
270
+ truncatedCalls: p.truncatedCalls,
271
+ }))
272
+ // Expensive slice first: the caller reading this is asking which phase to attack, and a
273
+ // store-order list buries the answer behind whichever phase happened to run first.
274
+ .sort((a, b) => b.carryCostTokens - a.carryCostTokens || b.calls - a.calls);
275
+ const runTotals = foldRollupTotals(summaries);
276
+ return {
277
+ totals: {
278
+ calls: runTotals.calls,
279
+ promptTokens: runTotals.promptTokens,
280
+ cacheReadTokens: runTotals.cacheReadTokens,
281
+ cacheWriteTokens: runTotals.cacheWriteTokens,
282
+ cacheHitRate: cacheHitRate(runTotals.cacheReadTokens, runTotals.cacheWriteTokens, runTotals.promptTokens),
283
+ completionTokens: runTotals.completionTokens,
284
+ upstreamMs: runTotals.upstreamMs,
285
+ overheadMs: runTotals.overheadMs,
286
+ transportOverheadRatio: transportOverheadRatio(runTotals.upstreamMs, runTotals.overheadMs),
287
+ errors: runTotals.errors,
288
+ warnings: runTotals.warnings,
289
+ truncatedCalls: runTotals.truncatedCalls,
290
+ },
291
+ byAgentKind,
292
+ byPhase,
293
+ };
294
+ }
295
+ /** A cache hit rate below this on a substantial prompt volume is worth flagging. */
296
+ const COLD_CACHE_RATE = 0.1;
297
+ /** Only flag a cold cache once the run has actually sent enough prompt to benefit from one. */
298
+ const COLD_CACHE_MIN_PROMPT_TOKENS = 50_000;
299
+ /** Above this share of latency spent in transport, the proxy is the story, not the model. */
300
+ const HIGH_TRANSPORT_OVERHEAD = 0.5;
301
+ /**
302
+ * Precompute the diagnostic hints the overview publishes. Every one of these is derivable by
303
+ * the caller from the same payload — which is exactly the point: a model that has to
304
+ * rediscover "13 of 40 calls were truncated" by arithmetic over a JSON blob will sometimes get
305
+ * it wrong and will always spend context getting it right. Ordered most-severe first, so a
306
+ * reader that truncates the list keeps what matters.
307
+ *
308
+ * Deliberately NOT a verdict. Each signal names one observation and its magnitude; nothing
309
+ * here claims to know why the run failed, because a wrong confident cause is worse for a
310
+ * debugging client than an ordered list of facts.
311
+ */
312
+ export function deriveSignals(input) {
313
+ const { execution, steps, totals, byAgentKind, sinks, provisioningFailures } = input;
314
+ const signals = [];
315
+ const push = (code, severity, message, extra = {}) => {
316
+ signals.push({
317
+ code,
318
+ severity,
319
+ message,
320
+ count: extra.count ?? null,
321
+ agentKind: extra.agentKind ?? null,
322
+ stepIndex: extra.stepIndex ?? null,
323
+ });
324
+ };
325
+ if (execution.status === 'failed') {
326
+ const failure = execution.failure;
327
+ push('run_failed', 'error', failure
328
+ ? `The run failed with '${failure.kind}': ${failure.message}`
329
+ : 'The run failed without recording a structured failure.', { stepIndex: execution.currentStep });
330
+ }
331
+ if (provisioningFailures > 0) {
332
+ push('provisioning_failed', 'error', `${provisioningFailures} provisioning attempt(s) for this run failed. Read GET /debug/runs/:runId/logs for the verbatim provider error — a run whose infrastructure never came up records no model calls at all.`, { count: provisioningFailures });
333
+ }
334
+ if (totals.errors > 0) {
335
+ push('llm_calls_failed', 'error', `${totals.errors} model call(s) failed.`, {
336
+ count: totals.errors,
337
+ });
338
+ }
339
+ for (const step of steps) {
340
+ if (step.evictionRecoveries > 0) {
341
+ push('container_evicted', 'warning', `Step ${step.index} (${step.agentKind}) lost its container ${step.evictionRecoveries} time(s) and was re-dispatched.`, { count: step.evictionRecoveries, agentKind: step.agentKind, stepIndex: step.index });
342
+ }
343
+ }
344
+ for (const insight of byAgentKind) {
345
+ if (insight.truncatedCalls > 0) {
346
+ push('output_truncated', 'warning', `${insight.truncatedCalls} of ${insight.agentKind}'s ${insight.calls} call(s) hit the output limit, so the model's reply was cut mid-answer.`, { count: insight.truncatedCalls, agentKind: insight.agentKind });
347
+ }
348
+ }
349
+ // The most common hard diagnosis has NO row of its own: a run that failed while every model
350
+ // call looks healthy. Tool-EXECUTION errors (malformed arguments, a stuck edit loop) happen
351
+ // inside the container and are recorded only as text inside the prompt deltas — each call
352
+ // still reports `ok` with a clean finish reason, so without this pointer the overview reads
353
+ // like a healthy run that inexplicably died and the caller has nothing to follow.
354
+ if (execution.status === 'failed' &&
355
+ sinks.llmCalls.available &&
356
+ totals.calls > 0 &&
357
+ totals.errors === 0 &&
358
+ totals.truncatedCalls === 0) {
359
+ push('failure_outside_model_calls', 'warning', `The run failed but none of its ${totals.calls} model call(s) failed or was truncated — the model side looks healthy, so the cause most likely sits in tool execution inside the container or in the engine, neither of which records calls here. Search the bodies for tool errors (GET /debug/runs/:runId/llm-calls?contains=...), read the newest calls' deltas, and check each step's firstEvictionDetail plus /logs.`, { count: totals.calls });
360
+ }
361
+ if (totals.transportOverheadRatio != null &&
362
+ totals.transportOverheadRatio > HIGH_TRANSPORT_OVERHEAD) {
363
+ push('transport_overhead_high', 'warning', `${Math.round(totals.transportOverheadRatio * 100)}% of the run's model latency was transport/proxy overhead rather than model execution.`);
364
+ }
365
+ if (totals.promptTokens >= COLD_CACHE_MIN_PROMPT_TOKENS &&
366
+ totals.cacheHitRate != null &&
367
+ totals.cacheHitRate < COLD_CACHE_RATE) {
368
+ push('prompt_cache_cold', 'info', `Only ${Math.round(totals.cacheHitRate * 100)}% of ${totals.promptTokens} prompt tokens were served from the provider's prefix cache, so the conversation was re-billed almost in full every turn.`);
369
+ }
370
+ if (execution.status === 'blocked') {
371
+ push('run_parked', 'info', 'The run is parked awaiting a human decision; it will wait indefinitely by design.', { stepIndex: execution.currentStep });
372
+ }
373
+ if (execution.status === 'paused') {
374
+ push('run_paused', 'info', 'The run is paused by the spend safeguard.', {
375
+ stepIndex: execution.currentStep,
376
+ });
377
+ }
378
+ // A sink that is not wired and a sink that is wired but empty need DIFFERENT follow-up
379
+ // actions from the caller — "turn capture on / this deployment does not keep it" versus
380
+ // "nothing happened here, look elsewhere" — so they are never collapsed into one hint.
381
+ for (const [name, sink, what] of [
382
+ ['llmCalls', sinks.llmCalls, 'model calls'],
383
+ ['agentContext', sinks.agentContext, 'agent-context snapshots'],
384
+ ['searchQueries', sinks.searchQueries, 'web searches'],
385
+ ['provisioningLog', sinks.provisioningLog, 'provisioning events'],
386
+ ]) {
387
+ if (!sink.available) {
388
+ // Availability is REPOSITORY presence only. A workspace that turned capture off (or a
389
+ // deployment without LLM_RECORD_PROMPTS) still reads `available: true, count: 0` — the
390
+ // capture gates act at record time, and this reader cannot see them.
391
+ push('telemetry_unavailable', 'info', `No ${what} are retained: the '${name}' sink is not wired on this deployment. Its count of 0 does not mean none happened.`);
392
+ }
393
+ }
394
+ // Skipped for a `done` run on purpose: a completed run with no model calls is a legitimate
395
+ // shape (a gate-only or pass-through pipeline), not a diagnosis.
396
+ if (sinks.llmCalls.available && sinks.llmCalls.count === 0 && execution.status !== 'done') {
397
+ push('no_model_calls', 'warning', 'The run recorded no model calls at all, so it failed or stalled before (or outside of) any agent work.');
398
+ }
399
+ return signals;
400
+ }
401
+ /** Re-exported so the service and its tests classify a call exactly as the SPA does. */
402
+ export { classifyCall, isWarningFinishReason };
403
+ //# sourceMappingURL=debug.logic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.logic.js","sourceRoot":"","sources":["../../../src/modules/debug/debug.logic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { LlmCallMetricPage } from '@cat-factory/kernel';
2
+ import type { DebugLlmCall, DebugPromptMessage } from '@cat-factory/contracts';
3
+ /**
4
+ * Parse a stored prompt delta into per-message rows, or null when it is not a JSON array —
5
+ * the caller degrades the view to raw and SAYS so, rather than serving a guess.
6
+ *
7
+ * `elided` is the call's `promptPrefixCount`: each row's `index` is its position in the
8
+ * FULL conversation, so two calls' parsed views line up without the reader doing delta
9
+ * arithmetic. `budget` bounds each message's content (and each tool call's arguments)
10
+ * independently.
11
+ */
12
+ export declare function parsePromptMessages(promptJson: string, elided: number, budget: number): DebugPromptMessage[] | null;
13
+ /**
14
+ * Project a WHOLE-BODY row onto the wire shape as the parsed messages view. Takes the row
15
+ * unsliced (the parse needs the complete delta — a truncated JSON array parses as nothing),
16
+ * so the raw/messages split is decided here, in one place:
17
+ *
18
+ * - parsed: `prompt` carries sizes only (its text is the same bytes re-presented as
19
+ * `promptMessages`, and sending both would double the payload), each message budgeted
20
+ * independently.
21
+ * - unparseable: `promptMessages: null` and the raw window served exactly as `view=raw`
22
+ * would have — the view DEGRADES, it never returns less than the raw read.
23
+ *
24
+ * `response`/`reasoning` are plain text either way and take the same window raw view does.
25
+ */
26
+ export declare function toDebugLlmCallMessagesView(call: LlmCallMetricPage, bodyChars: number, bodyOffset: number): DebugLlmCall;
27
+ //# sourceMappingURL=promptMessages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promptMessages.d.ts","sourceRoot":"","sources":["../../../src/modules/debug/promptMessages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAsG9E;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,kBAAkB,EAAE,GAAG,IAAI,CAyB7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,iBAAiB,EACvB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,YAAY,CAoBd"}
@@ -0,0 +1,165 @@
1
+ import { sliceText, toDebugLlmCall } from './debug.logic.js';
2
+ // The `?view=messages` half of the single-call point read: parse the stored prompt delta —
3
+ // `JSON.stringify` of a `{ role, content }` array on BOTH telemetry producers (the proxy
4
+ // stores the OpenAI-shaped request messages; the harness stores its reconstructed
5
+ // transcript) — into per-message rows, each with its OWN content budget.
6
+ //
7
+ // Independent budgets are the reason this exists rather than leaving the parse to the
8
+ // caller: in the raw view the delta is one string, so a 100 kB leading tool result must be
9
+ // paid for in full before anything after it is visible, while here every message shows its
10
+ // head. The response stays computable before the request — its worst case is
11
+ // `(messageCount − elidedLeadingMessages) × budget`, and both factors ride the list row.
12
+ //
13
+ // The parse is LENIENT BY CONTRACT. The two producers agree on the array-of-role-content
14
+ // envelope but not on content shapes (strings, OpenAI content parts + `tool_calls`, vendor
15
+ // tool_use/tool_result blocks), and the text is model-adjacent data that can be anything.
16
+ // So: an unparseable delta degrades the whole view to raw (`promptMessages: null`, stated,
17
+ // never guessed at), and inside a parsed array every unrecognised shape degrades to a
18
+ // placeholder or a JSON dump rather than failing the message — a reader locating a tool
19
+ // error must never lose the whole view to one exotic content block.
20
+ /** Read a string property leniently off an untrusted parsed object. */
21
+ function stringField(value, key) {
22
+ const field = value[key];
23
+ return typeof field === 'string' ? field : null;
24
+ }
25
+ /**
26
+ * Flatten one message's `content` to text. Handles the shapes the two producers actually
27
+ * store — a plain string, an array of parts (OpenAI `{type:'text',text}` and vendor
28
+ * `{type:'tool_use'|'tool_result',…}` blocks), a bare object — and stands in a `[type]`
29
+ * placeholder for a part with no text, so the message's SHAPE survives even when its
30
+ * content is not textual.
31
+ */
32
+ function contentToText(content) {
33
+ if (content == null)
34
+ return '';
35
+ if (typeof content === 'string')
36
+ return content;
37
+ if (Array.isArray(content)) {
38
+ return content
39
+ .map((part) => {
40
+ if (typeof part === 'string')
41
+ return part;
42
+ if (part && typeof part === 'object') {
43
+ const record = part;
44
+ const text = stringField(record, 'text');
45
+ if (text != null)
46
+ return text;
47
+ // A vendor tool_result block nests its payload under `content`.
48
+ if ('content' in record && stringField(record, 'type') === 'tool_result') {
49
+ return contentToText(record['content']);
50
+ }
51
+ return `[${stringField(record, 'type') ?? 'part'}]`;
52
+ }
53
+ return `[${typeof part}]`;
54
+ })
55
+ .join('\n');
56
+ }
57
+ if (typeof content === 'object')
58
+ return JSON.stringify(content);
59
+ return String(content);
60
+ }
61
+ /**
62
+ * Collect the tool invocations an assistant turn requested, across both producers' shapes:
63
+ * OpenAI `tool_calls: [{function:{name,arguments}}]` and vendor content blocks
64
+ * `{type:'tool_use', name, input}`. Arguments are serialized and budgeted like content.
65
+ */
66
+ function collectToolCalls(message, budget) {
67
+ const calls = [];
68
+ const openAi = message['tool_calls'];
69
+ if (Array.isArray(openAi)) {
70
+ for (const entry of openAi) {
71
+ if (!entry || typeof entry !== 'object')
72
+ continue;
73
+ const fn = entry['function'];
74
+ const record = fn && typeof fn === 'object' ? fn : {};
75
+ const args = record['arguments'];
76
+ calls.push({
77
+ name: stringField(record, 'name') ?? 'unknown',
78
+ args: sliceText(typeof args === 'string' ? args : args == null ? '' : JSON.stringify(args), budget),
79
+ });
80
+ }
81
+ }
82
+ const content = message['content'];
83
+ if (Array.isArray(content)) {
84
+ for (const part of content) {
85
+ if (!part || typeof part !== 'object')
86
+ continue;
87
+ const record = part;
88
+ if (stringField(record, 'type') !== 'tool_use')
89
+ continue;
90
+ const input = record['input'];
91
+ calls.push({
92
+ name: stringField(record, 'name') ?? 'unknown',
93
+ args: sliceText(input == null ? '' : JSON.stringify(input), budget),
94
+ });
95
+ }
96
+ }
97
+ return calls;
98
+ }
99
+ /**
100
+ * Parse a stored prompt delta into per-message rows, or null when it is not a JSON array —
101
+ * the caller degrades the view to raw and SAYS so, rather than serving a guess.
102
+ *
103
+ * `elided` is the call's `promptPrefixCount`: each row's `index` is its position in the
104
+ * FULL conversation, so two calls' parsed views line up without the reader doing delta
105
+ * arithmetic. `budget` bounds each message's content (and each tool call's arguments)
106
+ * independently.
107
+ */
108
+ export function parsePromptMessages(promptJson, elided, budget) {
109
+ let parsed;
110
+ try {
111
+ parsed = JSON.parse(promptJson);
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ if (!Array.isArray(parsed))
117
+ return null;
118
+ return parsed.map((entry, position) => {
119
+ const message = entry && typeof entry === 'object' && !Array.isArray(entry)
120
+ ? entry
121
+ : null;
122
+ return {
123
+ index: elided + position,
124
+ role: message ? (stringField(message, 'role') ?? 'unknown') : 'unknown',
125
+ name: message ? stringField(message, 'name') : null,
126
+ toolCallId: message ? stringField(message, 'tool_call_id') : null,
127
+ toolCalls: message ? collectToolCalls(message, budget) : [],
128
+ content: sliceText(message ? contentToText(message['content']) : JSON.stringify(entry), budget),
129
+ };
130
+ });
131
+ }
132
+ /**
133
+ * Project a WHOLE-BODY row onto the wire shape as the parsed messages view. Takes the row
134
+ * unsliced (the parse needs the complete delta — a truncated JSON array parses as nothing),
135
+ * so the raw/messages split is decided here, in one place:
136
+ *
137
+ * - parsed: `prompt` carries sizes only (its text is the same bytes re-presented as
138
+ * `promptMessages`, and sending both would double the payload), each message budgeted
139
+ * independently.
140
+ * - unparseable: `promptMessages: null` and the raw window served exactly as `view=raw`
141
+ * would have — the view DEGRADES, it never returns less than the raw read.
142
+ *
143
+ * `response`/`reasoning` are plain text either way and take the same window raw view does.
144
+ */
145
+ export function toDebugLlmCallMessagesView(call, bodyChars, bodyOffset) {
146
+ const messages = parsePromptMessages(call.prompt.text, call.promptPrefixCount, bodyChars);
147
+ // Project the metadata off body-less slices (the three bodies are replaced below), so the
148
+ // full texts are not walked a second time just to produce fields that get overwritten.
149
+ const projected = toDebugLlmCall({
150
+ ...call,
151
+ prompt: { text: '', totalChars: call.prompt.totalChars },
152
+ response: { text: '', totalChars: call.response.totalChars },
153
+ reasoning: { text: '', totalChars: call.reasoning.totalChars },
154
+ });
155
+ return {
156
+ ...projected,
157
+ prompt: messages == null
158
+ ? sliceText(call.prompt.text, bodyChars, bodyOffset)
159
+ : sliceText(call.prompt.text, 0),
160
+ response: sliceText(call.response.text, bodyChars, bodyOffset),
161
+ reasoning: sliceText(call.reasoning.text, bodyChars, bodyOffset),
162
+ promptMessages: messages,
163
+ };
164
+ }
165
+ //# sourceMappingURL=promptMessages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promptMessages.js","sourceRoot":"","sources":["../../../src/modules/debug/promptMessages.ts"],"names":[],"mappings":"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"}
@@ -120,6 +120,10 @@ export declare class RunStateMachine {
120
120
  * (not step index), so the aggregate is per-agent-kind within the run; steps
121
121
  * sharing a kind get the same rollup. Best-effort and a no-op when the sink is
122
122
  * not wired, so it never blocks an emit.
123
+ *
124
+ * The store returns the finer `(agentKind, phase)` grain, so the step's headline numbers
125
+ * are a fold up to the kind and its `byPhase` breakdown is the same cells re-cut — ONE
126
+ * aggregate for both, since an emit runs this on every step settlement.
123
127
  */
124
128
  private attachStepMetrics;
125
129
  /** Set the block's in-progress/blocked status and step-completion progress. */