@pasko70/pibo 1.11.4 → 1.12.1

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 (60) hide show
  1. package/dist/apps/chat/chat-trace-helpers.js +59 -2
  2. package/dist/apps/chat/data/timeline-query-service.js +1 -1
  3. package/dist/apps/chat/web-app.js +16 -8
  4. package/dist/apps/chat-ui/assets/{dist-BXjLdcn6.js → dist-3bD-92Wo.js} +1 -1
  5. package/dist/apps/{context-files-ui/assets/dist-BQK_wUMR.js → chat-ui/assets/dist-BFjy5Y59.js} +1 -1
  6. package/dist/apps/{context-files-ui/assets/dist-Dtv5S2qd.js → chat-ui/assets/dist-BKwQlbhS.js} +1 -1
  7. package/dist/apps/{context-files-ui/assets/dist-DT9QkgTN.js → chat-ui/assets/dist-Cp68y_h5.js} +1 -1
  8. package/dist/apps/chat-ui/assets/{dist-D9B41N2f.js → dist-DE0y_EI7.js} +1 -1
  9. package/dist/apps/chat-ui/assets/{dist-TS4EA6YQ.js → dist-DPb0yDi4.js} +1 -1
  10. package/dist/apps/{context-files-ui/assets/dist-B07Whoa6.js → chat-ui/assets/dist-D_dQ5GaA.js} +1 -1
  11. package/dist/apps/chat-ui/assets/{dist-yjwDcRbw.js → dist-Daq2KRpq.js} +1 -1
  12. package/dist/apps/chat-ui/assets/{dist-CjXSEbsr.js → dist-aZnuCezL.js} +1 -1
  13. package/dist/apps/chat-ui/assets/{dist-CSBDio7C.js → dist-q6Vpm5yH.js} +1 -1
  14. package/dist/apps/chat-ui/assets/index-BJoo_GJP.css +1 -0
  15. package/dist/apps/chat-ui/assets/index-DXPKuGfs.js +237 -0
  16. package/dist/apps/chat-ui/index.html +2 -2
  17. package/dist/apps/chat-vscode-web/assets/index-B3dSrp-L.js +41 -0
  18. package/dist/apps/chat-vscode-web/index.html +1 -1
  19. package/dist/apps/{chat-ui/assets/dist-BeKdUsyg.js → context-files-ui/assets/dist-BYcsFgKp.js} +1 -1
  20. package/dist/apps/context-files-ui/assets/{dist-TG9QLHKj.js → dist-Bdudyq9I.js} +1 -1
  21. package/dist/apps/context-files-ui/assets/{dist-BFlEjppv.js → dist-BvIW3V94.js} +1 -1
  22. package/dist/apps/context-files-ui/assets/{dist-De6eI3Cr.js → dist-CSLUWBzz.js} +1 -1
  23. package/dist/apps/{chat-ui/assets/dist-CNv02m3N.js → context-files-ui/assets/dist-CT-91DFe.js} +1 -1
  24. package/dist/apps/{chat-ui/assets/dist-BtQ20SMU.js → context-files-ui/assets/dist-DcsDivye.js} +1 -1
  25. package/dist/apps/context-files-ui/assets/{dist-BACxmdQ2.js → dist-DeozI4lc.js} +1 -1
  26. package/dist/apps/context-files-ui/assets/{dist-D68eIqmX.js → dist-f2E1-leP.js} +1 -1
  27. package/dist/apps/context-files-ui/assets/{dist-CigbyRK5.js → dist-sus90AFR.js} +1 -1
  28. package/dist/apps/{chat-ui/assets/dist-TAhl1jC0.js → context-files-ui/assets/dist-uFphVGi8.js} +1 -1
  29. package/dist/apps/context-files-ui/assets/index-CQ25ablG.js +202 -0
  30. package/dist/apps/context-files-ui/index.html +1 -1
  31. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  32. package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.12.1.vsix +0 -0
  33. package/dist/core/gateway-resource-guard.js +46 -15
  34. package/dist/core/routed-session.js +40 -1
  35. package/dist/core/runtime.js +2 -0
  36. package/dist/core/session-errors.js +3 -0
  37. package/dist/core/session-router.js +25 -2
  38. package/dist/core/transcript-integrity.js +431 -0
  39. package/dist/data/pibo-store.js +2 -0
  40. package/dist/data/telemetry.js +148 -0
  41. package/dist/debug/index.js +11 -0
  42. package/dist/debug/web.js +1 -1
  43. package/dist/gateway/server.js +1 -0
  44. package/dist/reliability/store.js +11 -6
  45. package/dist/runs/registry.js +38 -1
  46. package/dist/runs/resource-isolation.js +437 -0
  47. package/dist/runs/tools.js +6 -3
  48. package/dist/sessions/pibo-data-store.js +114 -0
  49. package/dist/shared/trace-engine.js +3 -3
  50. package/dist/shared/trace-event-projection.js +165 -36
  51. package/dist/shared/trace-nodes.js +6 -4
  52. package/dist/shared/trace-page-merge.js +106 -2
  53. package/dist/shared/trace-transcript.js +194 -36
  54. package/dist/tools/cdp-client.js +87 -3
  55. package/package.json +1 -1
  56. package/dist/apps/chat-ui/assets/index-BoRl0OB_.js +0 -473
  57. package/dist/apps/chat-ui/assets/index-aTOOomMF.css +0 -1
  58. package/dist/apps/chat-vscode-web/assets/index-DGcSd2ZH.js +0 -275
  59. package/dist/apps/context-files-ui/assets/index-B5j33Q2B.js +0 -438
  60. package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.11.4.vsix +0 -0
@@ -0,0 +1,431 @@
1
+ import { sessionEntryToContextMessages, } from "@earendil-works/pi-coding-agent";
2
+ export const PIBO_TRANSCRIPT_INTEGRITY_RESUME_MESSAGE_TYPE = "pibo-transcript-integrity-resume";
3
+ export const PIBO_TRANSCRIPT_INTEGRITY_RESUME_PROMPT = "Continue the interrupted task autonomously from the repaired transcript. Do not rerun completed tools, wait for additional user input, or mention transcript repair unless it affects the result.";
4
+ export const PIBO_TRANSCRIPT_INTEGRITY_ENTRY_TYPE = "pibo-transcript-integrity";
5
+ const installedStates = new WeakMap();
6
+ export class PiboTranscriptIntegrityError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "PiboTranscriptIntegrityError";
10
+ }
11
+ }
12
+ export function validatePiboTranscriptIntegrityMessages(messages) {
13
+ return validateProjectedMessages(messages.map((message) => ({ message })));
14
+ }
15
+ export function installPiboTranscriptIntegrity(session) {
16
+ const existing = installedStates.get(session);
17
+ if (existing)
18
+ return [...existing.reports];
19
+ const manager = session.sessionManager;
20
+ const state = {
21
+ session,
22
+ originalAppendMessage: manager.appendMessage.bind(manager),
23
+ restoredAssistants: new Map(),
24
+ reports: [],
25
+ continuationPending: false,
26
+ continuationInProgress: false,
27
+ };
28
+ installedStates.set(session, state);
29
+ manager.appendMessage = ((message) => appendMessageWithIntegrity(state, message));
30
+ patchProviderBoundary(state);
31
+ patchCompactionBoundaries(state);
32
+ reconcilePiboTranscriptIntegrity(session, "load");
33
+ return [...state.reports];
34
+ }
35
+ export function reconcilePiboTranscriptIntegrity(session, boundary) {
36
+ const state = installedStates.get(session);
37
+ if (!state)
38
+ throw new PiboTranscriptIntegrityError("Transcript integrity is not installed for this session");
39
+ const manager = session.sessionManager;
40
+ const activeEntries = manager.buildContextEntries();
41
+ const projected = projectEntries(activeEntries);
42
+ const [issue] = validateProjectedMessages(projected, manager.getEntries());
43
+ if (!issue)
44
+ return undefined;
45
+ const invalidEntry = issue.entryId ? manager.getEntry(issue.entryId) : undefined;
46
+ if (!invalidEntry) {
47
+ return quarantineRuntimeTranscript(state, boundary, issue);
48
+ }
49
+ const invalidEntryIndex = activeEntries.findIndex((entry) => entry.id === invalidEntry.id);
50
+ const removedEntryIds = invalidEntryIndex >= 0
51
+ ? activeEntries.slice(invalidEntryIndex).map((entry) => entry.id)
52
+ : [invalidEntry.id];
53
+ const resultMessage = isToolResultMessageEntry(invalidEntry) ? invalidEntry.message : undefined;
54
+ const authoritativeAssistant = resultMessage
55
+ ? uniqueAuthoritativeAssistant(manager.getEntries(), issue.toolCallId)
56
+ : undefined;
57
+ let report;
58
+ branchTo(manager, invalidEntry.parentId);
59
+ if (authoritativeAssistant && resultMessage && (issue.relation === "orphan_result" || issue.relation === "wrong_branch_result")) {
60
+ const repairId = manager.appendCustomEntry(PIBO_TRANSCRIPT_INTEGRITY_ENTRY_TYPE, repairMetadata({
61
+ boundary,
62
+ relation: issue.relation,
63
+ action: "restored_pair",
64
+ toolCallIds: assistantToolCalls(authoritativeAssistant.message).map((call) => call.id),
65
+ removedEntryIds,
66
+ restoredEntryIds: [authoritativeAssistant.id, invalidEntry.id],
67
+ phase: "journal_started",
68
+ }));
69
+ const restoredAssistantEntryId = state.originalAppendMessage(authoritativeAssistant.message);
70
+ rememberRestoredAssistant(state, authoritativeAssistant.message, restoredAssistantEntryId);
71
+ const restoredResultEntryId = state.originalAppendMessage(resultMessage);
72
+ manager.appendCustomEntry(PIBO_TRANSCRIPT_INTEGRITY_ENTRY_TYPE, {
73
+ repairId,
74
+ phase: "journal_committed",
75
+ restoredAssistantEntryId,
76
+ restoredResultEntryId,
77
+ });
78
+ report = {
79
+ repairId,
80
+ boundary,
81
+ relation: issue.relation,
82
+ action: "restored_pair",
83
+ toolCallIds: assistantToolCalls(authoritativeAssistant.message).map((call) => call.id),
84
+ removedEntryIds,
85
+ restoredEntryIds: [authoritativeAssistant.id, invalidEntry.id],
86
+ continuationRequired: boundary === "load",
87
+ };
88
+ }
89
+ else {
90
+ const repairId = manager.appendCustomEntry(PIBO_TRANSCRIPT_INTEGRITY_ENTRY_TYPE, repairMetadata({
91
+ boundary,
92
+ relation: issue.relation,
93
+ action: "quarantined_tail",
94
+ toolCallIds: [issue.toolCallId],
95
+ removedEntryIds,
96
+ restoredEntryIds: [],
97
+ phase: "quarantined",
98
+ }));
99
+ report = {
100
+ repairId,
101
+ boundary,
102
+ relation: issue.relation,
103
+ action: "quarantined_tail",
104
+ toolCallIds: [issue.toolCallId],
105
+ removedEntryIds,
106
+ restoredEntryIds: [],
107
+ continuationRequired: boundary === "load",
108
+ };
109
+ }
110
+ syncAgentTranscript(session);
111
+ assertSessionTranscriptValid(session, boundary);
112
+ recordReport(state, report);
113
+ return report;
114
+ }
115
+ export function claimPiboTranscriptIntegrityContinuation(session) {
116
+ const state = installedStates.get(session);
117
+ if (!state || !state.continuationPending || state.continuationInProgress)
118
+ return [];
119
+ const reports = state.reports.filter((report) => report.continuationRequired);
120
+ if (reports.length === 0) {
121
+ state.continuationPending = false;
122
+ return [];
123
+ }
124
+ state.continuationPending = false;
125
+ state.continuationInProgress = true;
126
+ session.sessionManager.appendCustomEntry(PIBO_TRANSCRIPT_INTEGRITY_ENTRY_TYPE, {
127
+ phase: "continuation_started",
128
+ repairIds: reports.map((report) => report.repairId),
129
+ });
130
+ return reports;
131
+ }
132
+ export function settlePiboTranscriptIntegrityContinuation(session, outcome, error) {
133
+ const state = installedStates.get(session);
134
+ if (!state?.continuationInProgress)
135
+ return;
136
+ state.continuationInProgress = false;
137
+ session.sessionManager.appendCustomEntry(PIBO_TRANSCRIPT_INTEGRITY_ENTRY_TYPE, {
138
+ phase: `continuation_${outcome}`,
139
+ ...(error ? { errorClass: error instanceof Error ? error.name : "Error" } : {}),
140
+ });
141
+ }
142
+ function patchProviderBoundary(state) {
143
+ const agent = state.session.agent;
144
+ const previous = agent.transformContext?.bind(agent);
145
+ const repairedProviderMessages = async (signal) => {
146
+ const repaired = state.session.sessionManager.buildSessionContext().messages;
147
+ if (!previous)
148
+ return repaired;
149
+ const transformed = await previous(repaired, signal);
150
+ return validatePiboTranscriptIntegrityMessages(transformed).length === 0 ? transformed : repaired;
151
+ };
152
+ agent.transformContext = async (messages, signal) => {
153
+ const transformed = previous ? await previous(messages, signal) : messages;
154
+ const durableRepair = reconcilePiboTranscriptIntegrity(state.session, "before_provider");
155
+ if (durableRepair)
156
+ return repairedProviderMessages(signal);
157
+ const [runtimeIssue] = validatePiboTranscriptIntegrityMessages(transformed);
158
+ if (!runtimeIssue)
159
+ return transformed;
160
+ quarantineRuntimeTranscript(state, "before_provider", runtimeIssue);
161
+ return repairedProviderMessages(signal);
162
+ };
163
+ }
164
+ function patchCompactionBoundaries(state) {
165
+ const session = state.session;
166
+ const originalCompact = session.compact.bind(session);
167
+ session.compact = (async (...args) => {
168
+ reconcilePiboTranscriptIntegrity(session, "before_compaction");
169
+ const result = await originalCompact(...args);
170
+ reconcilePiboTranscriptIntegrity(session, "after_compaction");
171
+ return result;
172
+ });
173
+ }
174
+ function appendMessageWithIntegrity(state, message) {
175
+ if (isAssistantMessage(message)) {
176
+ const restoredEntryId = restoredAssistantEntryId(state, message);
177
+ return restoredEntryId ?? state.originalAppendMessage(message);
178
+ }
179
+ if (!isToolResultMessage(message))
180
+ return state.originalAppendMessage(message);
181
+ const activeMessages = state.session.sessionManager.buildSessionContext().messages;
182
+ if (activeToolCall(activeMessages, message.toolCallId))
183
+ return state.originalAppendMessage(message);
184
+ const authoritativeAssistant = uniqueAssistantMessage(state.session.agent.state.messages, message.toolCallId);
185
+ if (authoritativeAssistant) {
186
+ const toolCallIds = assistantToolCalls(authoritativeAssistant).map((call) => call.id);
187
+ const repairId = state.session.sessionManager.appendCustomEntry(PIBO_TRANSCRIPT_INTEGRITY_ENTRY_TYPE, repairMetadata({
188
+ boundary: "persistence",
189
+ relation: "orphan_result",
190
+ action: "restored_pair",
191
+ toolCallIds,
192
+ removedEntryIds: [],
193
+ restoredEntryIds: [],
194
+ phase: "journal_started",
195
+ }));
196
+ const assistantEntryId = state.originalAppendMessage(authoritativeAssistant);
197
+ rememberRestoredAssistant(state, authoritativeAssistant, assistantEntryId);
198
+ const resultEntryId = state.originalAppendMessage(message);
199
+ state.session.sessionManager.appendCustomEntry(PIBO_TRANSCRIPT_INTEGRITY_ENTRY_TYPE, {
200
+ repairId,
201
+ phase: "journal_committed",
202
+ assistantEntryId,
203
+ resultEntryId,
204
+ });
205
+ recordReport(state, {
206
+ repairId,
207
+ boundary: "persistence",
208
+ relation: "orphan_result",
209
+ action: "restored_pair",
210
+ toolCallIds,
211
+ removedEntryIds: [],
212
+ restoredEntryIds: [assistantEntryId, resultEntryId],
213
+ continuationRequired: false,
214
+ });
215
+ return resultEntryId;
216
+ }
217
+ const repairId = state.session.sessionManager.appendCustomEntry(PIBO_TRANSCRIPT_INTEGRITY_ENTRY_TYPE, repairMetadata({
218
+ boundary: "persistence",
219
+ relation: "orphan_result",
220
+ action: "quarantined_runtime_result",
221
+ toolCallIds: [message.toolCallId],
222
+ removedEntryIds: [],
223
+ restoredEntryIds: [],
224
+ phase: "quarantined",
225
+ }));
226
+ recordReport(state, {
227
+ repairId,
228
+ boundary: "persistence",
229
+ relation: "orphan_result",
230
+ action: "quarantined_runtime_result",
231
+ toolCallIds: [message.toolCallId],
232
+ removedEntryIds: [],
233
+ restoredEntryIds: [],
234
+ continuationRequired: false,
235
+ });
236
+ syncAgentTranscript(state.session);
237
+ return repairId;
238
+ }
239
+ function quarantineRuntimeTranscript(state, boundary, issue) {
240
+ const repairId = state.session.sessionManager.appendCustomEntry(PIBO_TRANSCRIPT_INTEGRITY_ENTRY_TYPE, repairMetadata({
241
+ boundary,
242
+ relation: issue.relation,
243
+ action: "quarantined_runtime_result",
244
+ toolCallIds: [issue.toolCallId],
245
+ removedEntryIds: [],
246
+ restoredEntryIds: [],
247
+ phase: "quarantined",
248
+ }));
249
+ const report = {
250
+ repairId,
251
+ boundary,
252
+ relation: issue.relation,
253
+ action: "quarantined_runtime_result",
254
+ toolCallIds: [issue.toolCallId],
255
+ removedEntryIds: [],
256
+ restoredEntryIds: [],
257
+ continuationRequired: boundary === "load",
258
+ };
259
+ syncAgentTranscript(state.session);
260
+ recordReport(state, report);
261
+ return report;
262
+ }
263
+ function validateProjectedMessages(projected, allEntries = []) {
264
+ const calls = new Map();
265
+ const results = new Map();
266
+ const issues = [];
267
+ const allCalls = allAssistantCallIds(allEntries);
268
+ for (let messageIndex = 0; messageIndex < projected.length; messageIndex += 1) {
269
+ const { message, entry } = projected[messageIndex];
270
+ if (isAssistantMessage(message)) {
271
+ for (const call of assistantToolCalls(message)) {
272
+ const existing = calls.get(call.id);
273
+ if (existing) {
274
+ issues.push({ relation: "duplicate_call", toolCallId: call.id, toolName: call.name, messageIndex, entryId: entry?.id });
275
+ continue;
276
+ }
277
+ calls.set(call.id, { name: call.name, messageIndex });
278
+ }
279
+ continue;
280
+ }
281
+ if (!isToolResultMessage(message))
282
+ continue;
283
+ if (results.has(message.toolCallId)) {
284
+ issues.push({ relation: "duplicate_result", toolCallId: message.toolCallId, toolName: message.toolName, messageIndex, entryId: entry?.id });
285
+ continue;
286
+ }
287
+ results.set(message.toolCallId, messageIndex);
288
+ const call = calls.get(message.toolCallId);
289
+ if (!call) {
290
+ issues.push({
291
+ relation: allCalls.has(message.toolCallId) ? "wrong_branch_result" : "orphan_result",
292
+ toolCallId: message.toolCallId,
293
+ toolName: message.toolName,
294
+ messageIndex,
295
+ entryId: entry?.id,
296
+ });
297
+ continue;
298
+ }
299
+ if (call.name !== message.toolName) {
300
+ issues.push({
301
+ relation: "tool_name_mismatch",
302
+ toolCallId: message.toolCallId,
303
+ toolName: message.toolName,
304
+ expectedToolName: call.name,
305
+ messageIndex,
306
+ entryId: entry?.id,
307
+ });
308
+ }
309
+ }
310
+ return issues;
311
+ }
312
+ function projectEntries(entries) {
313
+ return entries.flatMap((entry) => sessionEntryToContextMessages(entry).map((message) => ({ entry, message })));
314
+ }
315
+ function allAssistantCallIds(entries) {
316
+ const ids = new Set();
317
+ for (const entry of entries) {
318
+ if (!isAssistantMessageEntry(entry))
319
+ continue;
320
+ for (const call of assistantToolCalls(entry.message))
321
+ ids.add(call.id);
322
+ }
323
+ return ids;
324
+ }
325
+ function uniqueAuthoritativeAssistant(entries, toolCallId) {
326
+ const candidates = entries.filter((entry) => isAssistantMessageEntry(entry) && assistantToolCalls(entry.message).some((call) => call.id === toolCallId));
327
+ return candidates.length === 1 ? candidates[0] : undefined;
328
+ }
329
+ function uniqueAssistantMessage(messages, toolCallId) {
330
+ const candidates = messages.filter((message) => isAssistantMessage(message) && assistantToolCalls(message).some((call) => call.id === toolCallId));
331
+ return candidates.length === 1 ? candidates[0] : undefined;
332
+ }
333
+ function activeToolCall(messages, toolCallId) {
334
+ for (const message of messages) {
335
+ if (!isAssistantMessage(message))
336
+ continue;
337
+ const call = assistantToolCalls(message).find((candidate) => candidate.id === toolCallId);
338
+ if (call)
339
+ return call;
340
+ }
341
+ return undefined;
342
+ }
343
+ function assistantToolCalls(message) {
344
+ if (!isAssistantMessage(message))
345
+ return [];
346
+ return message.content.flatMap((part) => {
347
+ if (!part || typeof part !== "object")
348
+ return [];
349
+ const candidate = part;
350
+ return candidate.type === "toolCall" && typeof candidate.id === "string" && typeof candidate.name === "string"
351
+ ? [{ type: "toolCall", id: candidate.id, name: candidate.name, arguments: candidate.arguments }]
352
+ : [];
353
+ });
354
+ }
355
+ function isAssistantMessage(message) {
356
+ return Boolean(message && typeof message === "object" && message.role === "assistant" && Array.isArray(message.content));
357
+ }
358
+ function isToolResultMessage(message) {
359
+ return Boolean(message
360
+ && typeof message === "object"
361
+ && message.role === "toolResult"
362
+ && typeof message.toolCallId === "string"
363
+ && typeof message.toolName === "string");
364
+ }
365
+ function isAssistantMessageEntry(entry) {
366
+ return entry.type === "message" && isAssistantMessage(entry.message);
367
+ }
368
+ function isToolResultMessageEntry(entry) {
369
+ return entry.type === "message" && isToolResultMessage(entry.message);
370
+ }
371
+ function branchTo(manager, parentId) {
372
+ if (parentId)
373
+ manager.branch(parentId);
374
+ else
375
+ manager.resetLeaf();
376
+ }
377
+ function syncAgentTranscript(session) {
378
+ session.agent.state.messages = session.sessionManager.buildSessionContext().messages;
379
+ }
380
+ function assertSessionTranscriptValid(session, boundary) {
381
+ const issues = validateProjectedMessages(projectEntries(session.sessionManager.buildContextEntries()), session.sessionManager.getEntries());
382
+ if (issues.length === 0)
383
+ return;
384
+ const issue = issues[0];
385
+ throw new PiboTranscriptIntegrityError(`Transcript integrity repair failed at ${boundary}: ${issue.relation} for tool call ${issue.toolCallId}`);
386
+ }
387
+ function rememberRestoredAssistant(state, message, entryId) {
388
+ const fingerprint = assistantFingerprint(message);
389
+ for (const call of assistantToolCalls(message))
390
+ state.restoredAssistants.set(call.id, { entryId, fingerprint });
391
+ }
392
+ function restoredAssistantEntryId(state, message) {
393
+ const calls = assistantToolCalls(message);
394
+ if (calls.length === 0)
395
+ return undefined;
396
+ const fingerprint = assistantFingerprint(message);
397
+ const restored = calls.map((call) => state.restoredAssistants.get(call.id));
398
+ if (restored.some((item) => !item || item.fingerprint !== fingerprint))
399
+ return undefined;
400
+ const entryIds = new Set(restored.map((item) => item.entryId));
401
+ return entryIds.size === 1 ? restored[0].entryId : undefined;
402
+ }
403
+ function assistantFingerprint(message) {
404
+ return JSON.stringify({
405
+ content: message.content,
406
+ api: message.api,
407
+ provider: message.provider,
408
+ model: message.model,
409
+ responseId: message.responseId,
410
+ timestamp: message.timestamp,
411
+ });
412
+ }
413
+ function repairMetadata(input) {
414
+ return {
415
+ version: 1,
416
+ boundary: input.boundary,
417
+ relation: input.relation,
418
+ action: input.action,
419
+ phase: input.phase,
420
+ toolCallIds: input.toolCallIds,
421
+ removedEntryIds: input.removedEntryIds,
422
+ restoredEntryIds: input.restoredEntryIds,
423
+ removedCount: input.removedEntryIds.length,
424
+ restoredCount: input.restoredEntryIds.length,
425
+ };
426
+ }
427
+ function recordReport(state, report) {
428
+ state.reports.push(report);
429
+ if (report.continuationRequired)
430
+ state.continuationPending = true;
431
+ }
@@ -39,6 +39,8 @@ export class PiboDataStore {
39
39
  this.telemetry = new TelemetryStore(this.db);
40
40
  }
41
41
  transaction(action) {
42
+ if (this.db.isTransaction)
43
+ return action();
42
44
  this.db.exec("BEGIN IMMEDIATE");
43
45
  try {
44
46
  const result = action();
@@ -86,6 +86,154 @@ export class TelemetryStore {
86
86
  `).all(turnId);
87
87
  return rows.map(toolCallFromRow);
88
88
  }
89
+ listActiveTurns() {
90
+ const rows = this.db.prepare(`
91
+ SELECT * FROM telemetry_turns
92
+ WHERE status IN ('queued', 'running')
93
+ ORDER BY queued_at ASC, created_at ASC
94
+ `).all();
95
+ return rows.map(turnFromRow);
96
+ }
97
+ recoverInterruptedTurns(input = {}) {
98
+ const at = input.at ?? new Date().toISOString();
99
+ return this.transaction(() => this.listActiveTurns().map((turn) => {
100
+ const outcome = input.resolveOutcome?.(turn) ?? {
101
+ status: "aborted",
102
+ summary: "Turn was interrupted by gateway restart.",
103
+ };
104
+ const phaseStatus = outcome.status;
105
+ const providerStatus = outcome.status;
106
+ const toolStatus = outcome.status;
107
+ const phaseName = outcome.status === "timeout"
108
+ ? "timeout"
109
+ : outcome.status === "error"
110
+ ? "error"
111
+ : "abort";
112
+ for (const phase of this.listOpenPhasesForTurn(turn.turnId)) {
113
+ this.finishPhase(phase.phaseId, {
114
+ status: phaseStatus,
115
+ endedAt: at,
116
+ lastProgressAt: at,
117
+ summary: outcome.summary,
118
+ });
119
+ }
120
+ for (const request of this.listActiveProviderRequestsForTurn(turn.turnId)) {
121
+ this.upsertProviderRequest({
122
+ providerRequestId: request.providerRequestId,
123
+ piboSessionId: request.piboSessionId,
124
+ rootSessionId: request.rootSessionId,
125
+ roomId: request.roomId,
126
+ turnId: request.turnId,
127
+ phaseId: request.phaseId,
128
+ provider: request.provider,
129
+ api: request.api,
130
+ model: request.model,
131
+ transport: request.transport,
132
+ serviceTier: request.serviceTier,
133
+ status: providerStatus,
134
+ responseHeadersAt: request.responseHeadersAt,
135
+ firstByteAt: request.firstByteAt,
136
+ lastRawEventAt: request.lastRawEventAt,
137
+ lastNormalizedEventAt: request.lastNormalizedEventAt,
138
+ completedAt: at,
139
+ httpStatus: request.httpStatus,
140
+ upstreamResponseId: request.upstreamResponseId,
141
+ rawEventCount: request.rawEventCount,
142
+ normalizedEventCount: request.normalizedEventCount,
143
+ parseErrorCount: request.parseErrorCount,
144
+ unknownEventCount: request.unknownEventCount,
145
+ bytesReceived: request.bytesReceived,
146
+ eventTypeCounts: request.eventTypeCounts,
147
+ eventStreamId: request.eventStreamId,
148
+ eventId: request.eventId,
149
+ payloadRef: request.payloadRef,
150
+ errorCategory: "runtime_restart",
151
+ errorMessage: outcome.summary,
152
+ captureMode: request.captureMode,
153
+ retentionClass: request.retentionClass,
154
+ updatedAt: at,
155
+ });
156
+ }
157
+ for (const toolCall of this.listActiveToolCallsForTurn(turn.turnId)) {
158
+ const executionEndedAt = toolCall.executionStartedAt ? at : toolCall.executionEndedAt;
159
+ this.upsertToolCall({
160
+ toolCallId: toolCall.toolCallId,
161
+ piboSessionId: toolCall.piboSessionId,
162
+ rootSessionId: toolCall.rootSessionId,
163
+ roomId: toolCall.roomId,
164
+ turnId: toolCall.turnId,
165
+ providerRequestId: toolCall.providerRequestId,
166
+ providerItemId: toolCall.providerItemId,
167
+ outputIndex: toolCall.outputIndex,
168
+ toolName: toolCall.toolName,
169
+ status: toolStatus,
170
+ argsStartedAt: toolCall.argsStartedAt,
171
+ firstDeltaAt: toolCall.firstDeltaAt,
172
+ lastDeltaAt: toolCall.lastDeltaAt,
173
+ argsCompletedAt: toolCall.argsCompletedAt,
174
+ executionStartedAt: toolCall.executionStartedAt,
175
+ executionEndedAt,
176
+ durationMs: toolCall.executionStartedAt && executionEndedAt
177
+ ? Math.max(0, Date.parse(executionEndedAt) - Date.parse(toolCall.executionStartedAt))
178
+ : toolCall.durationMs,
179
+ argsBytes: toolCall.argsBytes,
180
+ parseStatus: toolCall.parseStatus,
181
+ safeArgKeys: toolCall.safeArgKeys,
182
+ eventStreamId: toolCall.eventStreamId,
183
+ eventId: toolCall.eventId,
184
+ payloadRef: toolCall.payloadRef,
185
+ runId: toolCall.runId,
186
+ errorCategory: "runtime_restart",
187
+ errorMessage: outcome.summary,
188
+ retentionClass: toolCall.retentionClass,
189
+ updatedAt: at,
190
+ });
191
+ }
192
+ this.upsertPhase({
193
+ phaseId: `${turn.turnId}:runtime_restart`,
194
+ turnId: turn.turnId,
195
+ piboSessionId: turn.piboSessionId,
196
+ rootSessionId: turn.rootSessionId,
197
+ roomId: turn.roomId,
198
+ name: phaseName,
199
+ status: phaseStatus,
200
+ startedAt: at,
201
+ endedAt: at,
202
+ lastProgressAt: at,
203
+ eventId: turn.eventId,
204
+ runId: turn.runId,
205
+ summary: outcome.summary,
206
+ retentionClass: turn.retentionClass,
207
+ updatedAt: at,
208
+ });
209
+ const recovered = this.upsertTurn({
210
+ turnId: turn.turnId,
211
+ piboSessionId: turn.piboSessionId,
212
+ rootSessionId: turn.rootSessionId,
213
+ roomId: turn.roomId,
214
+ inputEventId: turn.inputEventId,
215
+ eventId: turn.eventId,
216
+ eventStreamId: turn.eventStreamId,
217
+ payloadRef: turn.payloadRef,
218
+ runId: turn.runId,
219
+ source: turn.source,
220
+ status: outcome.status,
221
+ currentPhase: phaseName,
222
+ queuedAt: turn.queuedAt,
223
+ startedAt: turn.startedAt,
224
+ completedAt: at,
225
+ lastProgressAt: at,
226
+ queuedBehind: turn.queuedBehind,
227
+ queueDepth: 0,
228
+ summary: outcome.summary,
229
+ retentionClass: turn.retentionClass,
230
+ createdAt: turn.createdAt,
231
+ updatedAt: at,
232
+ metadata: turn.metadata,
233
+ });
234
+ return { turn: recovered, outcome };
235
+ }));
236
+ }
89
237
  listProviderEventsPage(providerRequestId, input = {}) {
90
238
  return listTelemetryProviderEventsPage(this.db, providerRequestId, input);
91
239
  }
@@ -989,6 +989,17 @@ function compactRunRow(run) {
989
989
  timeoutAt: run.timeoutAt,
990
990
  timeoutPhase: run.timeoutPhase,
991
991
  serviceWarning: run.serviceWarning,
992
+ isolation: run.resources?.isolationMode,
993
+ resourceUnit: run.resources?.unitName,
994
+ resourceLimitReason: run.resources?.limitReason,
995
+ minimumHostAvailableBytes: run.resources?.minimumHostAvailableBytes,
996
+ peakMemoryFullPsiAvg10: run.resources?.peakMemoryFullPsiAvg10,
997
+ peakIoFullPsiAvg10: run.resources?.peakIoFullPsiAvg10,
998
+ memoryPeakBytes: run.resources?.cgroup?.memoryPeakBytes,
999
+ memoryMaxBytes: run.resources?.cgroup?.memoryMaxBytes,
1000
+ tasksPeak: run.resources?.cgroup?.tasksPeak,
1001
+ ioReadBytes: run.resources?.cgroup?.ioReadBytes,
1002
+ ioWriteBytes: run.resources?.cgroup?.ioWriteBytes,
992
1003
  summary: run.summary,
993
1004
  };
994
1005
  }
package/dist/debug/web.js CHANGED
@@ -486,7 +486,7 @@ async function runStreamingBenchmarkSeries(client, runs, durationMs, options) {
486
486
  async function runStreamingBenchmark(client, durationMs, options = {}) {
487
487
  await client.send("Page.bringToFront").catch(() => undefined);
488
488
  const benchmarkTimeoutMs = durationMs + (options.startBackendFixture ? 20_000 : 10_000);
489
- const benchmark = await client.evaluate(buildStreamingBenchmarkExpression(durationMs, options), benchmarkTimeoutMs);
489
+ const benchmark = await client.evaluateJson(buildStreamingBenchmarkExpression(durationMs, options), benchmarkTimeoutMs);
490
490
  const withRenderOrder = { ...benchmark, renderOrder: analyzeStreamingRenderOrderCapture(benchmark.renderOrder) };
491
491
  const withProvider = { ...withRenderOrder, provider: options.providerTelemetry };
492
492
  const scored = { ...withProvider, score: scoreStreamingBenchmark(withProvider), providerPreservation: summarizeStreamingProviderPreservation(withProvider) };
@@ -149,6 +149,7 @@ export class PiboGatewayServer {
149
149
  pluginRegistry: this.pluginRegistry,
150
150
  sessionStore: this.sessionStore,
151
151
  messagePreflight: createLoopMessagePreflight({ path: this.options.loopStorePath }),
152
+ recoverInterruptedRuntimeState: true,
152
153
  });
153
154
  this.unsubscribe = this.router.subscribe((event) => this.broadcastRouterEvent(event));
154
155
  this.server = createServer((socket) => this.handleSocket(socket));