@pasko70/pibo 1.9.13 → 1.10.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 (163) hide show
  1. package/README.md +183 -183
  2. package/context/codex-base-prompt.md +148 -148
  3. package/context/compute-worker.md +23 -23
  4. package/context/pibo-compaction-prompt.md +100 -100
  5. package/context/pibo-native-tooling.md +18 -18
  6. package/context/pibo-system-prompt.md +77 -77
  7. package/dist/apps/chat/agent-profiles.js +2 -2
  8. package/dist/apps/chat/agent-store.js +263 -250
  9. package/dist/apps/chat/chat-request-normalizers.js +17 -0
  10. package/dist/apps/chat/data/chat-data-mappers.js +2 -0
  11. package/dist/apps/chat/data/project-service.js +168 -168
  12. package/dist/apps/chat/data/read-state-service.js +18 -18
  13. package/dist/apps/chat/data/session-query-service.js +25 -25
  14. package/dist/apps/chat/data/timeline-query-service.js +19 -19
  15. package/dist/apps/chat/loop-api.js +181 -0
  16. package/dist/apps/chat/static-assets.js +854 -854
  17. package/dist/apps/chat/web-app.js +36 -16
  18. package/dist/apps/chat/workflow-manual-trigger-runtime.js +149 -46
  19. package/dist/apps/chat/workflow-persistence.js +255 -255
  20. package/dist/apps/chat-ui/assets/{dist-BZ2eTC4f.js → dist-3Sts0Afa.js} +1 -1
  21. package/dist/apps/chat-ui/assets/{dist-BHa-kcGl.js → dist-4NlLjLs7.js} +1 -1
  22. package/dist/apps/chat-ui/assets/{dist-yCYNNb5d.js → dist-BA6mhAec.js} +1 -1
  23. package/dist/apps/chat-ui/assets/{dist-D811wJeV.js → dist-BPl-fzRB.js} +1 -1
  24. package/dist/apps/chat-ui/assets/{dist-Dq4GxJi3.js → dist-BPnn9e2B.js} +1 -1
  25. package/dist/apps/chat-ui/assets/{dist-CzE6k3F3.js → dist-BpfBSMzK.js} +1 -1
  26. package/dist/apps/chat-ui/assets/{dist-CnVsqwSG.js → dist-Cvx5M97R.js} +1 -1
  27. package/dist/apps/chat-ui/assets/{dist-WyXdYl-w.js → dist-DnXWNQRm.js} +1 -1
  28. package/dist/apps/chat-ui/assets/{dist-BNMu92bb.js → dist-hU-2oAb6.js} +1 -1
  29. package/dist/apps/chat-ui/assets/{dist-BCB6zezO.js → dist-r31x5Fcc.js} +1 -1
  30. package/dist/apps/chat-ui/assets/{dist-HqTN67dc.js → dist-uF6UXzI7.js} +1 -1
  31. package/dist/apps/chat-ui/assets/index-CaTGYBOS.js +173 -0
  32. package/dist/apps/chat-ui/assets/{index-C0x9nEcf.css → index-al8DeEjA.css} +1 -1
  33. package/dist/apps/chat-ui/index.html +18 -18
  34. package/dist/apps/chat-ui/manifest.webmanifest +25 -25
  35. package/dist/apps/chat-ui/sw.js +47 -47
  36. package/dist/apps/chat-vscode-web/assets/index-CK4SMZuu.js +41 -0
  37. package/dist/apps/chat-vscode-web/index.html +13 -13
  38. package/dist/apps/cli-ui/cliSessionsCommand.js +23 -23
  39. package/dist/apps/context-files-ui/index.html +11 -11
  40. package/dist/cli-session/localSessionSource.js +34 -12
  41. package/dist/cli.js +61 -44
  42. package/dist/compute/cli.js +54 -54
  43. package/dist/compute/resource-health.js +35 -2
  44. package/dist/core/events.js +6 -1
  45. package/dist/core/routed-session.js +97 -5
  46. package/dist/core/runtime.js +6 -1
  47. package/dist/core/session-router.js +27 -8
  48. package/dist/cron/cli.js +15 -15
  49. package/dist/cron/store.js +49 -49
  50. package/dist/data/cli.js +23 -23
  51. package/dist/data/event-log.js +23 -23
  52. package/dist/data/ingest-service.js +3 -1
  53. package/dist/data/message-store.js +27 -27
  54. package/dist/data/navigation-store.js +9 -9
  55. package/dist/data/observation-store.js +4 -4
  56. package/dist/data/payload-store.js +17 -17
  57. package/dist/data/schema.js +433 -433
  58. package/dist/data/session-store.js +4 -4
  59. package/dist/data/telemetry-queries.js +54 -54
  60. package/dist/data/telemetry.js +197 -197
  61. package/dist/debug/events.js +12 -12
  62. package/dist/debug/failures.js +6 -6
  63. package/dist/debug/index.js +231 -227
  64. package/dist/debug/messages.js +6 -6
  65. package/dist/debug/pty.js +124 -124
  66. package/dist/debug/session.js +29 -29
  67. package/dist/debug/tools.js +5 -5
  68. package/dist/debug/trace.js +42 -7
  69. package/dist/debug/web-snapshot-browser-scripts.js +294 -294
  70. package/dist/debug/web-streaming-browser-library.js +925 -925
  71. package/dist/debug/web-streaming-browser-scripts.js +232 -232
  72. package/dist/debug/web-streaming-provider-telemetry.js +4 -4
  73. package/dist/debug/web.js +93 -93
  74. package/dist/gateway/cli.js +19 -19
  75. package/dist/gateway/server.js +4 -2
  76. package/dist/gateway/web.js +2 -2
  77. package/dist/index.js +1 -0
  78. package/dist/loops/accounting.js +19 -0
  79. package/dist/loops/channel.js +8 -0
  80. package/dist/loops/cli.js +224 -0
  81. package/dist/loops/plugin.js +16 -0
  82. package/dist/loops/prompts.js +86 -0
  83. package/dist/loops/service.js +446 -0
  84. package/dist/loops/stopping.js +170 -0
  85. package/dist/loops/store.js +642 -0
  86. package/dist/loops/templates.js +232 -0
  87. package/dist/loops/tools.js +184 -0
  88. package/dist/loops/types.js +1 -0
  89. package/dist/mcp/config-command.js +53 -53
  90. package/dist/mcp/index.js +21 -21
  91. package/dist/mcp/registry.js +11 -11
  92. package/dist/pi-packages/cli.js +11 -11
  93. package/dist/plugins/builtin.js +8 -0
  94. package/dist/plugins/context-files-store.js +110 -110
  95. package/dist/plugins/context-files.js +4 -4
  96. package/dist/plugins/registry.js +23 -12
  97. package/dist/ralph/cli.js +18 -18
  98. package/dist/ralph/templates.js +140 -140
  99. package/dist/reliability/store.js +256 -229
  100. package/dist/runs/lifecycle.js +59 -0
  101. package/dist/runs/registry.js +47 -1
  102. package/dist/runs/tools.js +31 -13
  103. package/dist/session-ui/terminalRows.js +66 -2
  104. package/dist/sessions/pibo-data-store.js +16 -16
  105. package/dist/sessions/sqlite-store.js +53 -53
  106. package/dist/setup/cli.js +58 -58
  107. package/dist/shared/trace-async-agent-runs.js +4 -4
  108. package/dist/shared/trace-event-projection.js +59 -19
  109. package/dist/shared/trace-nodes.js +5 -0
  110. package/dist/shared/trace-page-merge.js +15 -0
  111. package/dist/shared/trace-run-notifications.js +3 -1
  112. package/dist/signals/projector.js +6 -2
  113. package/dist/tools/agent-browser-wrapper.js +80 -80
  114. package/dist/tools/browser-pool.js +50 -0
  115. package/dist/tools/browser-use-cdp.js +12 -12
  116. package/dist/tools/browser-use-leases.js +12 -8
  117. package/dist/tools/browser-use-wrapper.js +762 -762
  118. package/dist/tools/guides.js +596 -538
  119. package/dist/tools/index.js +123 -99
  120. package/dist/tools/registry.js +21 -3
  121. package/dist/tools/runtime/node-worker-source.js +205 -205
  122. package/dist/tools/runtime/python-worker-source.js +177 -177
  123. package/dist/vscode/cli.js +9 -9
  124. package/dist/web-annotations/cdp.js +900 -900
  125. package/dist/web-annotations/store.js +96 -96
  126. package/docs/README.md +23 -23
  127. package/docs/ops/install-developer-host.md +112 -112
  128. package/docs/ops/install-user-host.md +96 -96
  129. package/docs/ops/upgrade-user-to-developer-host.md +69 -69
  130. package/docs/ops/vscode-extension-release.md +160 -160
  131. package/package.json +99 -95
  132. package/skills/builtin/graphify/SKILL.md +52 -52
  133. package/skills/builtin/loop/SKILL.md +78 -0
  134. package/skills/builtin/pi-agent-harness/SKILL.md +319 -319
  135. package/skills/builtin/pi-agent-harness/agents/openai.yaml +4 -4
  136. package/skills/builtin/pibo-docker-system/SKILL.md +170 -170
  137. package/skills/builtin/pibo-spec-writing/SKILL.md +330 -330
  138. package/skills/builtin/prd/SKILL.md +143 -143
  139. package/skills/builtin/ralph-loop/SKILL.md +361 -359
  140. package/skills/builtin/ralph-prd-json/SKILL.md +123 -123
  141. package/skills/builtin/skill-creator/LICENSE.txt +201 -201
  142. package/skills/builtin/skill-creator/SKILL.md +513 -513
  143. package/skills/builtin/skill-creator/agents/analyzer.md +274 -274
  144. package/skills/builtin/skill-creator/agents/comparator.md +202 -202
  145. package/skills/builtin/skill-creator/agents/grader.md +223 -223
  146. package/skills/builtin/skill-creator/assets/eval_review.html +146 -146
  147. package/skills/builtin/skill-creator/eval-viewer/generate_review.py +471 -471
  148. package/skills/builtin/skill-creator/eval-viewer/viewer.html +1325 -1325
  149. package/skills/builtin/skill-creator/references/schemas.md +430 -430
  150. package/skills/builtin/skill-creator/scripts/aggregate_benchmark.py +401 -401
  151. package/skills/builtin/skill-creator/scripts/generate_report.py +326 -326
  152. package/skills/builtin/skill-creator/scripts/improve_description.py +247 -247
  153. package/skills/builtin/skill-creator/scripts/package_skill.py +136 -136
  154. package/skills/builtin/skill-creator/scripts/quick_validate.py +102 -102
  155. package/skills/builtin/skill-creator/scripts/run_eval.py +310 -310
  156. package/skills/builtin/skill-creator/scripts/run_loop.py +328 -328
  157. package/skills/builtin/skill-creator/scripts/utils.py +47 -47
  158. package/skills/builtin/web-annotations/SKILL.md +93 -93
  159. package/src/mcp/LICENSE.mcp-cli +21 -21
  160. package/dist/apps/chat-ui/assets/index-DwHJfmiF.js +0 -173
  161. package/dist/apps/chat-vscode-web/assets/index-BAMxIaI_.js +0 -41
  162. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  163. package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.9.13.vsix +0 -0
@@ -34,12 +34,12 @@ export function reconcileAsyncAgentRunStatuses(nodes) {
34
34
  if (!latest)
35
35
  continue;
36
36
  const status = stringValue(latest.snapshot.status);
37
- if (status !== "completed" && status !== "cancelled" && status !== "failed")
37
+ if (status !== "completed" && status !== "cancelled" && status !== "failed" && status !== "timed_out")
38
38
  continue;
39
- node.status = status === "failed" ? "error" : "done";
39
+ node.status = status === "failed" || status === "timed_out" ? "error" : "done";
40
40
  node.completedAt = latest.completedAt ?? node.completedAt;
41
41
  node.output = latest.snapshot;
42
- if (status === "failed")
42
+ if (status === "failed" || status === "timed_out")
43
43
  node.error = stringValue(latest.snapshot.summary) ?? node.error;
44
44
  }
45
45
  }
@@ -67,7 +67,7 @@ function createAsyncAgentRunNode(parent, piboSessionId, startedAt, delegation) {
67
67
  title: subagentName,
68
68
  status: asyncAgentStatus(parent, runStatus),
69
69
  startedAt: delegation?.startedAt ?? startedAt ?? parent.startedAt,
70
- completedAt: runStatus === "completed" || runStatus === "cancelled" ? parent.completedAt : undefined,
70
+ completedAt: runStatus === "completed" || runStatus === "cancelled" || runStatus === "failed" || runStatus === "timed_out" ? parent.completedAt : undefined,
71
71
  summary: `Started by ${parent.title}`,
72
72
  input: {
73
73
  startedBy: parent.title,
@@ -5,6 +5,13 @@ import { createRunNotificationNode, parseRunNotificationText } from "./trace-run
5
5
  import { findLikelyTraceChildSession, isSubagentToolName, } from "./trace-subagent-links.js";
6
6
  export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent, childByParent, linkedChildByToolCallId, hasPersistedTranscript, openTranscriptEventIds, sessionStatus) {
7
7
  const payload = storedEvent.payload;
8
+ const confirmedUserMessage = hasPersistedTranscript ? confirmedUserMessageEchoNode(nodes, storedEvent) : undefined;
9
+ if (confirmedUserMessage) {
10
+ if (payload.type === "message_steered" && payload.activeEventId) {
11
+ confirmedUserMessage.parentId = messageTurnNodeId(payload.activeEventId);
12
+ }
13
+ return;
14
+ }
8
15
  if (hasPersistedTranscript &&
9
16
  isTranscriptEchoEvent(payload) &&
10
17
  !shouldKeepTranscriptEchoEvent(payload, openTranscriptEventIds)) {
@@ -84,8 +91,14 @@ export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent,
84
91
  return;
85
92
  }
86
93
  }
87
- if (node.eventId && byId.has(node.id))
88
- return;
94
+ if (node.eventId) {
95
+ const existing = byId.get(node.id);
96
+ if (existing) {
97
+ if (node.type === "user.message" && node.parentId)
98
+ existing.parentId = node.parentId;
99
+ return;
100
+ }
101
+ }
89
102
  attachExecutionCommandToOpenTurn(node, byId);
90
103
  attachAsyncAgentRunNode(node, piboSessionId, storedEvent.createdAt);
91
104
  nodes.push(node);
@@ -174,7 +187,7 @@ export function reconcileTranscriptUserMessageTimestamps(nodes, events) {
174
187
  let userCursor = 0;
175
188
  for (const storedEvent of events) {
176
189
  const event = storedEvent.payload;
177
- if (event.type !== "message_queued" || event.source !== "user")
190
+ if ((event.type !== "message_queued" && event.type !== "message_steered") || event.source !== "user")
178
191
  continue;
179
192
  const eventId = typeof event.eventId === "string" ? event.eventId : storedEvent.eventId;
180
193
  const text = typeof event.text === "string" ? event.text : undefined;
@@ -192,12 +205,15 @@ export function reconcileTranscriptUserMessageTimestamps(nodes, events) {
192
205
  }
193
206
  }
194
207
  export function isConfirmedUserMessageEcho(nodes, event) {
208
+ return Boolean(confirmedUserMessageEchoNode(nodes, event));
209
+ }
210
+ function confirmedUserMessageEchoNode(nodes, event) {
195
211
  const payload = event.payload;
196
- if (payload.type !== "message_queued" || payload.source !== "user")
197
- return false;
212
+ if ((payload.type !== "message_queued" && payload.type !== "message_steered") || payload.source !== "user")
213
+ return undefined;
198
214
  const eventId = typeof payload.eventId === "string" ? payload.eventId : event.eventId;
199
215
  const text = typeof payload.text === "string" ? payload.text : undefined;
200
- return nodes.some((node) => {
216
+ return flattenTraceNodes([...nodes]).find((node) => {
201
217
  if (node.type !== "user.message" || node.source !== "transcript")
202
218
  return false;
203
219
  if (eventId && (node.entryId === eventId || node.stableKey === `entry:${eventId}`))
@@ -261,7 +277,8 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
261
277
  children: [],
262
278
  };
263
279
  switch (event.type) {
264
- case "message_queued": {
280
+ case "message_queued":
281
+ case "message_steered": {
265
282
  const notification = parseRunNotificationText(event.text);
266
283
  if (event.source === "service" && notification) {
267
284
  return createRunNotificationNode({
@@ -277,6 +294,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
277
294
  }
278
295
  return {
279
296
  ...base,
297
+ ...(event.type === "message_steered" && event.activeEventId ? { parentId: messageTurnNodeId(event.activeEventId) } : {}),
280
298
  type: "user.message",
281
299
  title: "User Message",
282
300
  status: "done",
@@ -368,10 +386,12 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
368
386
  children: [],
369
387
  };
370
388
  }
371
- case "subagent_session":
389
+ case "subagent_session": {
390
+ const childPiboSessionId = nonEmptyString(event.childPiboSessionId);
391
+ const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event);
372
392
  return {
373
393
  ...base,
374
- id: event.toolCallId ? `tool:${event.toolCallId}` : id,
394
+ id: event.toolCallId ? `tool:${event.toolCallId}` : `event:subagent_session:${eventInstanceKey}`,
375
395
  eventId,
376
396
  toolCallId: event.toolCallId,
377
397
  type: "agent.delegation",
@@ -379,10 +399,13 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
379
399
  status: sessionStatus === "running" ? "running" : "done",
380
400
  summary: event.subagentName,
381
401
  input: { subagentName: event.subagentName, threadKey: event.threadKey },
382
- linkedPiboSessionId: event.childPiboSessionId,
383
- stableKey: event.toolCallId ? `tool:${event.toolCallId}` : `subagent:${event.childPiboSessionId}`,
402
+ linkedPiboSessionId: childPiboSessionId,
403
+ stableKey: event.toolCallId
404
+ ? `tool:${event.toolCallId}`
405
+ : childPiboSessionId ? `subagent:${childPiboSessionId}` : `subagent:event:${eventInstanceKey}`,
384
406
  children: [],
385
407
  };
408
+ }
386
409
  case "execution_result":
387
410
  if (isInternalSessionOperation(event.action))
388
411
  return undefined;
@@ -394,21 +417,24 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
394
417
  input: { action: event.action },
395
418
  output: event.result,
396
419
  };
397
- case "compaction_start":
420
+ case "compaction_start": {
421
+ const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event);
398
422
  return {
399
423
  ...base,
400
- id: `event:compaction:${eventSequence ?? streamId ?? cryptoSafeId(event)}`,
424
+ id: `event:compaction:${eventInstanceKey}`,
401
425
  type: "execution.compaction",
402
426
  title: "compact",
403
427
  status: "running",
404
428
  summary: "Compacting",
405
429
  input: { reason: event.reason },
406
- stableKey: "compaction:active",
430
+ stableKey: `compaction:${eventInstanceKey}`,
407
431
  };
408
- case "compaction_end":
432
+ }
433
+ case "compaction_end": {
434
+ const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event);
409
435
  return {
410
436
  ...base,
411
- id: `event:compaction:end:${eventSequence ?? streamId ?? cryptoSafeId(event)}`,
437
+ id: `event:compaction:end:${eventInstanceKey}`,
412
438
  type: "execution.compaction",
413
439
  title: "compact",
414
440
  status: event.errorMessage ? "error" : "done",
@@ -417,8 +443,9 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
417
443
  input: { reason: event.reason },
418
444
  output: event.result,
419
445
  error: event.errorMessage,
420
- stableKey: "compaction:active",
446
+ stableKey: `compaction:${eventInstanceKey}`,
421
447
  };
448
+ }
422
449
  case "session_error":
423
450
  return {
424
451
  ...base,
@@ -435,7 +462,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
435
462
  }
436
463
  }
437
464
  function mergeAssistantDeltaEvent(nodes, byId, event, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex) {
438
- if (event.text.length === 0)
465
+ if (typeof event.text !== "string" || event.text.length === 0)
439
466
  return;
440
467
  const assistantId = assistantEventNodeId(event);
441
468
  const id = assistantId ? assistantMessageNodeId(assistantId) : `event:assistant_delta:${cryptoSafeId(event)}`;
@@ -474,7 +501,7 @@ function mergeAssistantMessageEvent(target, update) {
474
501
  target.completedAt = update.completedAt ?? target.completedAt;
475
502
  }
476
503
  function mergeThinkingDeltaEvent(nodes, byId, event, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex) {
477
- if (event.text.length === 0)
504
+ if (typeof event.text !== "string" || event.text.length === 0)
478
505
  return;
479
506
  const thinkingId = thinkingEventNodeId(event);
480
507
  const id = thinkingId ? thinkingNodeId(thinkingId) : `event:thinking_delta:${cryptoSafeId(event)}`;
@@ -642,6 +669,7 @@ function eventTraceNodeOrder(eventSequence, type, streamId, streamFrameIndex) {
642
669
  function eventNodeKind(type) {
643
670
  switch (type) {
644
671
  case "message_queued":
672
+ case "message_steered":
645
673
  return "user.message";
646
674
  case "message_started":
647
675
  case "message_finished":
@@ -792,6 +820,18 @@ function parseTimestamp(value) {
792
820
  const timestamp = new Date(value).getTime();
793
821
  return Number.isFinite(timestamp) ? timestamp : undefined;
794
822
  }
823
+ function traceEventInstanceKey(eventSequence, streamId, event) {
824
+ if (eventSequence !== undefined)
825
+ return `sequence:${eventSequence}`;
826
+ if (streamId !== undefined)
827
+ return `stream:${streamId}`;
828
+ return cryptoSafeId(event);
829
+ }
830
+ function nonEmptyString(value) {
831
+ if (typeof value !== "string")
832
+ return undefined;
833
+ return value.trim() || undefined;
834
+ }
795
835
  function cryptoSafeId(value) {
796
836
  return base64UrlEncode(new TextEncoder().encode(JSON.stringify(value))).slice(0, 48);
797
837
  }
@@ -22,6 +22,11 @@ export function compareTraceNodes(left, right) {
22
22
  const bySameTurnPhase = compareSameTurnPhase(left, right);
23
23
  if (bySameTurnPhase !== 0)
24
24
  return bySameTurnPhase;
25
+ if (left.orderKey?.sourceRank === right.orderKey?.sourceRank) {
26
+ const bySameSourceOrder = compareTraceOrder(left.orderKey, right.orderKey);
27
+ if (bySameSourceOrder !== 0)
28
+ return bySameSourceOrder;
29
+ }
25
30
  const byStartTime = compareOptionalIsoTime(left.startedAt, right.startedAt);
26
31
  if (byStartTime !== 0)
27
32
  return byStartTime;
@@ -23,6 +23,21 @@ export function mergeOlderTracePage(current, older) {
23
23
  eventLimit: (current.eventLimit ?? 0) + (older.eventLimit ?? older.pageSize ?? 0),
24
24
  };
25
25
  }
26
+ export function mergeRefreshedTracePage(current, refreshed) {
27
+ if (current.piboSessionId !== refreshed.piboSessionId)
28
+ return refreshed;
29
+ return {
30
+ ...refreshed,
31
+ nodes: mergeTraceNodes(current.nodes, refreshed.nodes),
32
+ rawEvents: current.rawEvents.length ? current.rawEvents : refreshed.rawEvents,
33
+ beforeCursor: current.beforeCursor,
34
+ firstEventSequence: current.firstEventSequence ?? refreshed.firstEventSequence,
35
+ nextBeforeSequence: current.nextBeforeSequence,
36
+ nextBeforeCursor: current.nextBeforeCursor,
37
+ hasOlderEvents: current.hasOlderEvents,
38
+ eventLimit: current.eventLimit ?? refreshed.eventLimit,
39
+ };
40
+ }
26
41
  function mergeTraceNodes(olderNodes, currentNodes) {
27
42
  const byId = new Map();
28
43
  for (const node of flattenTraceNodes([...olderNodes])) {
@@ -19,7 +19,7 @@ export function parseRunNotificationText(text) {
19
19
  export function createRunNotificationNode(input) {
20
20
  const runs = runNotificationRuns(input.notification);
21
21
  const singleRun = runs.length === 1 ? runs[0] : undefined;
22
- const failedCount = countRunGroup(input.notification.failed);
22
+ const failedCount = countRunGroup(input.notification.failed) + countRunGroup(input.notification.timedOut);
23
23
  const runningCount = countRunGroup(input.notification.running);
24
24
  return {
25
25
  id: input.id,
@@ -43,6 +43,7 @@ function runNotificationRuns(notification) {
43
43
  return [
44
44
  ...runGroup(notification.completed),
45
45
  ...runGroup(notification.failed),
46
+ ...runGroup(notification.timedOut),
46
47
  ...runGroup(notification.cancelled),
47
48
  ...runGroup(notification.running),
48
49
  ];
@@ -57,6 +58,7 @@ function runNotificationSummary(notification) {
57
58
  const parts = [
58
59
  [countRunGroup(notification.completed), "completed"],
59
60
  [countRunGroup(notification.failed), "failed"],
61
+ [countRunGroup(notification.timedOut), "timed out"],
60
62
  [countRunGroup(notification.cancelled), "cancelled"],
61
63
  [countRunGroup(notification.running), "running"],
62
64
  ]
@@ -12,7 +12,7 @@ function node(input, context) {
12
12
  function runStatus(status) {
13
13
  if (status === "completed")
14
14
  return "done";
15
- if (status === "failed")
15
+ if (status === "failed" || status === "timed_out")
16
16
  return "error";
17
17
  return status;
18
18
  }
@@ -133,6 +133,10 @@ export const outputSignalProducer = {
133
133
  mutations.push({ type: "set_session_queue", piboSessionId, queuedMessages: event.queuedMessages });
134
134
  mutations.push({ type: "upsert_node", node: node({ id: `message:${piboSessionId}:${event.eventId ?? context.now()}`, kind: "message", status: "queued", piboSessionId, metadata: { source: event.source } }, context) });
135
135
  }
136
+ if (event.type === "message_steered") {
137
+ mutations.push({ type: "patch_node", nodeId: `session:${piboSessionId}`, patch: { status: "running" } });
138
+ mutations.push({ type: "upsert_node", node: node({ id: `message:${piboSessionId}:${event.eventId ?? context.now()}`, kind: "message", status: "done", piboSessionId, parentNodeId: event.activeEventId ? `turn:${piboSessionId}:${event.activeEventId}` : undefined, completedAt: context.now(), metadata: { source: event.source, delivery: "steer" } }, context) });
139
+ }
136
140
  if (event.type === "message_started") {
137
141
  mutations.push({ type: "patch_node", nodeId: `session:${piboSessionId}`, patch: { status: "running" } });
138
142
  if (event.eventId) {
@@ -200,7 +204,7 @@ export const runSignalProducer = {
200
204
  if (data.type !== "run_changed")
201
205
  return [];
202
206
  const run = data.run;
203
- return [{ type: "upsert_node", node: node({ id: `run:${run.runId}`, kind: "yielded_run", status: runStatus(run.status), piboSessionId: run.controllerPiboSessionId, startedAt: run.createdAt, completedAt: run.completedAt, error: run.status === "failed" ? { message: run.summary ?? "Run failed.", source: "run" } : undefined, metadata: { runId: run.runId, toolName: run.toolName, completionPolicy: run.completionPolicy, consumed: run.consumed, summary: run.summary, previousStatus: data.previousStatus, reason: data.reason } }, context) }];
207
+ return [{ type: "upsert_node", node: node({ id: `run:${run.runId}`, kind: "yielded_run", status: runStatus(run.status), piboSessionId: run.controllerPiboSessionId, startedAt: run.createdAt, completedAt: run.completedAt, error: run.status === "failed" || run.status === "timed_out" ? { message: run.summary ?? "Run failed.", source: "run" } : undefined, metadata: { runId: run.runId, toolName: run.toolName, completionPolicy: run.completionPolicy, consumed: run.consumed, summary: run.summary, timeoutMs: run.timeoutMs, timeoutAt: run.timeoutAt, timeoutPhase: run.timeoutPhase, serviceWarning: run.serviceWarning, previousStatus: data.previousStatus, reason: data.reason } }, context) }];
204
208
  },
205
209
  };
206
210
  export function createDefaultSignalProducers() {
@@ -6,86 +6,86 @@ function shellQuote(value) {
6
6
  return `'${value.replaceAll("'", "'\\''")}'`;
7
7
  }
8
8
  function createAgentBrowserWrapper(realExecutablePath, defaultHomeDir) {
9
- return `#!/usr/bin/env bash
10
- set -euo pipefail
11
-
12
- real_agent_browser=${shellQuote(realExecutablePath)}
13
- default_home=${shellQuote(defaultHomeDir)}
14
- fresh_flag=${shellQuote(AGENT_BROWSER_FRESH_PROFILE_FLAG)}
15
-
16
- if [ -z "\${AGENT_BROWSER_HOME:-}" ]; then
17
- export AGENT_BROWSER_HOME="$default_home"
18
- fi
19
- mkdir -p "$AGENT_BROWSER_HOME" "$AGENT_BROWSER_HOME/profiles"
20
-
21
- # agent-browser@0.27.0 uses HOME for ~/.agent-browser state. Redirect it by
22
- # default so doctor, config, sockets, and auth vault files stay inside Pibo's
23
- # tool home. Operators can opt out for upstream debugging.
24
- if [ "\${PIBO_AGENT_BROWSER_PRESERVE_HOME:-0}" != "1" ]; then
25
- export HOME="$AGENT_BROWSER_HOME"
26
- fi
27
-
28
- explicit_runtime=0
29
- fresh_profile=0
30
- show_help=0
31
- starts_browser=0
32
- args=()
33
-
34
- print_wrapper_help() {
35
- cat <<'HELP'
36
- Pibo agent-browser wrapper
37
- - keeps Agent Browser state under AGENT_BROWSER_HOME by default
38
- - prepends a persistent Pibo profile for browser-launching commands
39
- - pass --fresh-profile to disable Pibo profile injection
40
- - explicit --profile, --state, --cdp, --auto-connect, --provider, --engine,
41
- --executable-path, or --config flags override Pibo defaults
42
- HELP
43
- }
44
-
45
- while [ "$#" -gt 0 ]; do
46
- case "$1" in
47
- --help|-h)
48
- show_help=1
49
- args+=("$1")
50
- ;;
51
- --fresh-profile)
52
- fresh_profile=1
53
- ;;
54
- --profile|--state|--cdp|--auto-connect|--provider|--engine|--executable-path|--config)
55
- explicit_runtime=1
56
- args+=("$1")
57
- if [ "$#" -gt 1 ]; then
58
- shift
59
- args+=("$1")
60
- fi
61
- ;;
62
- --profile=*|--state=*|--cdp=*|--auto-connect=*|--provider=*|--engine=*|--executable-path=*|--config=*)
63
- explicit_runtime=1
64
- args+=("$1")
65
- ;;
66
- open|launch|new|goto)
67
- starts_browser=1
68
- args+=("$1")
69
- ;;
70
- *)
71
- args+=("$1")
72
- ;;
73
- esac
74
- shift
75
- done
76
-
77
- if [ "$show_help" -eq 1 ]; then
78
- print_wrapper_help
79
- exec "$real_agent_browser" "\${args[@]}"
80
- fi
81
-
82
- if [ "$starts_browser" -eq 1 ] && [ "$fresh_profile" -eq 0 ] && [ "$explicit_runtime" -eq 0 ]; then
83
- profile_dir=\${AGENT_BROWSER_PROFILE:-$AGENT_BROWSER_HOME/profiles/${AGENT_BROWSER_DEFAULT_PROFILE}}
84
- mkdir -p "$profile_dir"
85
- exec "$real_agent_browser" --profile "$profile_dir" "\${args[@]}"
86
- fi
87
-
88
- exec "$real_agent_browser" "\${args[@]}"
9
+ return `#!/usr/bin/env bash
10
+ set -euo pipefail
11
+
12
+ real_agent_browser=${shellQuote(realExecutablePath)}
13
+ default_home=${shellQuote(defaultHomeDir)}
14
+ fresh_flag=${shellQuote(AGENT_BROWSER_FRESH_PROFILE_FLAG)}
15
+
16
+ if [ -z "\${AGENT_BROWSER_HOME:-}" ]; then
17
+ export AGENT_BROWSER_HOME="$default_home"
18
+ fi
19
+ mkdir -p "$AGENT_BROWSER_HOME" "$AGENT_BROWSER_HOME/profiles"
20
+
21
+ # agent-browser@0.27.0 uses HOME for ~/.agent-browser state. Redirect it by
22
+ # default so doctor, config, sockets, and auth vault files stay inside Pibo's
23
+ # tool home. Operators can opt out for upstream debugging.
24
+ if [ "\${PIBO_AGENT_BROWSER_PRESERVE_HOME:-0}" != "1" ]; then
25
+ export HOME="$AGENT_BROWSER_HOME"
26
+ fi
27
+
28
+ explicit_runtime=0
29
+ fresh_profile=0
30
+ show_help=0
31
+ starts_browser=0
32
+ args=()
33
+
34
+ print_wrapper_help() {
35
+ cat <<'HELP'
36
+ Pibo agent-browser wrapper
37
+ - keeps Agent Browser state under AGENT_BROWSER_HOME by default
38
+ - prepends a persistent Pibo profile for browser-launching commands
39
+ - pass --fresh-profile to disable Pibo profile injection
40
+ - explicit --profile, --state, --cdp, --auto-connect, --provider, --engine,
41
+ --executable-path, or --config flags override Pibo defaults
42
+ HELP
43
+ }
44
+
45
+ while [ "$#" -gt 0 ]; do
46
+ case "$1" in
47
+ --help|-h)
48
+ show_help=1
49
+ args+=("$1")
50
+ ;;
51
+ --fresh-profile)
52
+ fresh_profile=1
53
+ ;;
54
+ --profile|--state|--cdp|--auto-connect|--provider|--engine|--executable-path|--config)
55
+ explicit_runtime=1
56
+ args+=("$1")
57
+ if [ "$#" -gt 1 ]; then
58
+ shift
59
+ args+=("$1")
60
+ fi
61
+ ;;
62
+ --profile=*|--state=*|--cdp=*|--auto-connect=*|--provider=*|--engine=*|--executable-path=*|--config=*)
63
+ explicit_runtime=1
64
+ args+=("$1")
65
+ ;;
66
+ open|launch|new|goto)
67
+ starts_browser=1
68
+ args+=("$1")
69
+ ;;
70
+ *)
71
+ args+=("$1")
72
+ ;;
73
+ esac
74
+ shift
75
+ done
76
+
77
+ if [ "$show_help" -eq 1 ]; then
78
+ print_wrapper_help
79
+ exec "$real_agent_browser" "\${args[@]}"
80
+ fi
81
+
82
+ if [ "$starts_browser" -eq 1 ] && [ "$fresh_profile" -eq 0 ] && [ "$explicit_runtime" -eq 0 ]; then
83
+ profile_dir=\${AGENT_BROWSER_PROFILE:-$AGENT_BROWSER_HOME/profiles/${AGENT_BROWSER_DEFAULT_PROFILE}}
84
+ mkdir -p "$profile_dir"
85
+ exec "$real_agent_browser" --profile "$profile_dir" "\${args[@]}"
86
+ fi
87
+
88
+ exec "$real_agent_browser" "\${args[@]}"
89
89
  `;
90
90
  }
91
91
  export function ensureAgentBrowserWrapper(status) {
@@ -1,6 +1,7 @@
1
1
  import { access, mkdir, open, readFile, rename, rm, writeFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import { randomUUID } from "node:crypto";
4
+ import { spawn } from "node:child_process";
4
5
  const DEFAULT_MAX_BROWSER_PROCESSES = 1;
5
6
  const DEFAULT_LOCK_TIMEOUT_MS = 5_000;
6
7
  const DEFAULT_LOCK_POLL_INTERVAL_MS = 50;
@@ -239,6 +240,55 @@ export async function acquireBrowserPoolLease(paths, identity, options = {}) {
239
240
  }
240
241
  }, options.lockOptions);
241
242
  }
243
+ export async function restartRecordedBrowserPoolChrome(state) {
244
+ if (!state.userDataDir)
245
+ throw new Error("Browser pool has no persisted user-data directory for reacquisition");
246
+ if (!state.cdpPort)
247
+ throw new Error("Browser pool has no persisted CDP port for reacquisition");
248
+ const candidates = [process.env.PIBO_CHROME_BIN, "/usr/bin/google-chrome", "/usr/bin/chromium", "/usr/bin/chromium-browser", "/opt/google/chrome/chrome"].filter((value) => !!value);
249
+ let executable;
250
+ for (const candidate of candidates) {
251
+ try {
252
+ await access(candidate);
253
+ executable = candidate;
254
+ break;
255
+ }
256
+ catch { /* try next */ }
257
+ }
258
+ if (!executable)
259
+ throw new Error("No supported Chrome/Chromium executable is available to reacquire the persisted browser profile");
260
+ await removeDefaultStaleBrowserFiles(state);
261
+ const cdpUrl = `http://127.0.0.1:${state.cdpPort}`;
262
+ const child = spawn(executable, [
263
+ `--remote-debugging-port=${state.cdpPort}`,
264
+ "--remote-debugging-address=127.0.0.1",
265
+ `--user-data-dir=${state.userDataDir}`,
266
+ `--profile-directory=${process.env.PIBO_BROWSER_USE_DEFAULT_PROFILE || "PIBo"}`,
267
+ "--headless=new",
268
+ "--no-sandbox",
269
+ "--disable-gpu",
270
+ "--disable-dev-shm-usage",
271
+ "about:blank",
272
+ ], { detached: true, stdio: "ignore" });
273
+ await new Promise((resolve, reject) => {
274
+ child.once("spawn", resolve);
275
+ child.once("error", reject);
276
+ });
277
+ child.unref();
278
+ if (!child.pid)
279
+ throw new Error("Chrome reacquisition did not return a process id");
280
+ for (let attempt = 0; attempt < 100; attempt += 1) {
281
+ const health = await checkBrowserPoolCdpHealth(cdpUrl, { timeoutMs: 250 });
282
+ if (health.ok)
283
+ return { pid: child.pid, processGroupId: child.pid, cdpPort: state.cdpPort, cdpUrl, userDataDir: state.userDataDir };
284
+ await delay(100);
285
+ }
286
+ try {
287
+ process.kill(-child.pid, "SIGKILL");
288
+ }
289
+ catch { /* already exited */ }
290
+ throw new Error(`Reacquired Chrome did not expose CDP at ${cdpUrl}`);
291
+ }
242
292
  export async function releaseBrowserPoolLease(paths, identity, options = {}) {
243
293
  const now = options.now ?? (() => new Date());
244
294
  const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
@@ -47,18 +47,18 @@ async function discoverCdpUrl() {
47
47
  }
48
48
  import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
49
49
  import { join } from 'node:path';
50
- const CHAT_TARGET_PROBE = `(() => {
51
- const text = String(document.body?.innerText || "");
52
- const textareas = [...document.querySelectorAll("textarea")].map((textarea) => ({
53
- placeholder: textarea.placeholder || "",
54
- disabled: Boolean(textarea.disabled),
55
- value: textarea.value || "",
56
- }));
57
- return {
58
- title: document.title || "",
59
- text: text.slice(0, 3000),
60
- textareas,
61
- };
50
+ const CHAT_TARGET_PROBE = `(() => {
51
+ const text = String(document.body?.innerText || "");
52
+ const textareas = [...document.querySelectorAll("textarea")].map((textarea) => ({
53
+ placeholder: textarea.placeholder || "",
54
+ disabled: Boolean(textarea.disabled),
55
+ value: textarea.value || "",
56
+ }));
57
+ return {
58
+ title: document.title || "",
59
+ text: text.slice(0, 3000),
60
+ textareas,
61
+ };
62
62
  })()`;
63
63
  export async function listBrowserUseCdpTargets(options = {}) {
64
64
  const cdpUrl = await normalizeCdpUrl(options.cdpUrl);
@@ -365,21 +365,19 @@ export async function acquireBrowserUseLease(context, options = {}) {
365
365
  }
366
366
  await writeRegistry(context.status, registry);
367
367
  if (reapedCount > 0 && !options.json) {
368
- console.log(`Reaped ${reapedCount} stale lease${reapedCount === 1 ? '' : 's'}`);
368
+ console.error(`Reaped ${reapedCount} stale lease${reapedCount === 1 ? '' : 's'}`);
369
369
  }
370
370
  if (options.json)
371
371
  printLeaseJson(context.status, lease);
372
372
  else
373
373
  printLeaseEnv(context.status, lease);
374
- if (!options.noWarmup) {
375
- const warmup = await warmupBrowserUseLease(context, lease);
376
- if (!warmup.success && !options.json) {
377
- console.log(`Warning: Browser warm-up failed: ${warmup.error}`);
378
- }
374
+ const warmup = await warmupBrowserUseLease(context, lease, 15000, options.headed);
375
+ if (!warmup.success && !options.json) {
376
+ console.error(`Warning: Browser warm-up failed: ${warmup.error?.trim() || 'unknown error'}`);
379
377
  }
380
378
  });
381
379
  }
382
- async function warmupBrowserUseLease(context, lease, timeoutMs = 15000) {
380
+ async function warmupBrowserUseLease(context, lease, timeoutMs = 15000, headed = false) {
383
381
  const wrapperPath = join(context.status.homeDir, 'bin', 'browser-use');
384
382
  if (!existsSync(wrapperPath)) {
385
383
  return { success: false, error: 'browser-use wrapper not found' };
@@ -393,7 +391,13 @@ async function warmupBrowserUseLease(context, lease, timeoutMs = 15000) {
393
391
  ...(lease.browserPoolLeaseId ? { PIBO_BROWSER_POOL_LEASE_ID: lease.browserPoolLeaseId } : {}),
394
392
  };
395
393
  return new Promise((resolve) => {
396
- const child = spawn(wrapperPath, ['--session', lease.sessionName, '--pibo-ensure-chrome'], {
394
+ const args = [
395
+ ...(headed ? ['--headed'] : []),
396
+ '--session',
397
+ lease.sessionName,
398
+ '--pibo-ensure-chrome',
399
+ ];
400
+ const child = spawn(wrapperPath, args, {
397
401
  env,
398
402
  stdio: ['ignore', 'pipe', 'pipe'],
399
403
  });