@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
@@ -1,14 +1,14 @@
1
- <!doctype html>
2
- <html lang="de" class="dark">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <meta name="theme-color" content="#101d22" />
7
- <title>Pibo</title>
8
- <script type="module" crossorigin src="/apps/chat-vscode/assets/index-BAMxIaI_.js"></script>
1
+ <!doctype html>
2
+ <html lang="de" class="dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="theme-color" content="#101d22" />
7
+ <title>Pibo</title>
8
+ <script type="module" crossorigin src="/apps/chat-vscode/assets/index-CK4SMZuu.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/apps/chat-vscode/assets/index-B5QK07zO.css">
10
- </head>
11
- <body>
12
- <div id="root"></div>
13
- </body>
14
- </html>
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
@@ -205,28 +205,28 @@ export function terminalLineLimitFromColumns(columns) {
205
205
  return Math.max(20, Math.floor(columns) - 4);
206
206
  }
207
207
  export function cliSessionsHelpText() {
208
- return `pibo tui:sessions - reduced Web Chat-derived session UI for terminals
209
-
210
- Usage:
211
- pibo tui:sessions [options]
212
-
213
- Options:
214
- --session <id> Open a specific Pibo session id
215
- --max-rows <count> Limit rendered transcript rows (default: 20)
216
- --demo Use deterministic fake session data for smoke testing
217
- -h, --help Show this help
218
-
219
- V2 commands inside the app:
220
- ${cliCommandSummaryText()}
221
-
222
- Scope:
223
- CLI Sessions is a reduced session/chat UI for SSH, bootstrap, recovery, and quick local work.
224
- It starts only when stdin and stdout are interactive TTYs; pipes fail with a clear error.
225
- Transcript rendering uses a bounded default tail window of 20 rows and truncates narrow terminal lines.
226
- Web Chat remains the full control center for projects, workflows, Cron, Ralph, Agent Designer, settings, and context management.
227
-
228
- Related existing commands:
229
- pibo tui
230
- pibo tui:routed
208
+ return `pibo tui:sessions - reduced Web Chat-derived session UI for terminals
209
+
210
+ Usage:
211
+ pibo tui:sessions [options]
212
+
213
+ Options:
214
+ --session <id> Open a specific Pibo session id
215
+ --max-rows <count> Limit rendered transcript rows (default: 20)
216
+ --demo Use deterministic fake session data for smoke testing
217
+ -h, --help Show this help
218
+
219
+ V2 commands inside the app:
220
+ ${cliCommandSummaryText()}
221
+
222
+ Scope:
223
+ CLI Sessions is a reduced session/chat UI for SSH, bootstrap, recovery, and quick local work.
224
+ It starts only when stdin and stdout are interactive TTYs; pipes fail with a clear error.
225
+ Transcript rendering uses a bounded default tail window of 20 rows and truncates narrow terminal lines.
226
+ Web Chat remains the full control center for projects, workflows, Cron, Ralph, Agent Designer, settings, and context management.
227
+
228
+ Related existing commands:
229
+ pibo tui
230
+ pibo tui:routed
231
231
  `;
232
232
  }
@@ -1,14 +1,14 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Pibo Context Files</title>
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Pibo Context Files</title>
7
7
  <script type="module" crossorigin src="/apps/context-files/assets/index-BhAWD3dv.js"></script>
8
8
  <link rel="modulepreload" crossorigin href="/apps/context-files/assets/dist-D1KyNDg5.js">
9
9
  <link rel="stylesheet" crossorigin href="/apps/context-files/assets/index-B92tL3Pp.css">
10
- </head>
11
- <body>
12
- <div id="root"></div>
13
- </body>
14
- </html>
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
@@ -29,6 +29,8 @@ export class LocalCliSessionSource {
29
29
  listeners = new Map();
30
30
  openHandles = new Set();
31
31
  closed = false;
32
+ closing = false;
33
+ closePromise;
32
34
  constructor(options = {}) {
33
35
  this.sessionStore =
34
36
  options.sessionStore ?? createDefaultPiboDataSessionStore();
@@ -349,19 +351,39 @@ export class LocalCliSessionSource {
349
351
  };
350
352
  }
351
353
  async close() {
354
+ if (this.closePromise)
355
+ return this.closePromise;
356
+ this.closePromise = this.closeUnsafe();
357
+ return this.closePromise;
358
+ }
359
+ async closeUnsafe() {
352
360
  if (this.closed)
353
361
  return;
354
- this.closed = true;
355
- for (const handle of [...this.openHandles])
356
- handle.close();
357
- this.listeners.clear();
358
- this.unsubscribeRouter?.();
359
- if (this.ownsRouter)
360
- await this.router?.disposeAll?.();
361
- if (this.ownsSessionStore)
362
- this.sessionStore.close?.();
363
- if (this.ownsDataStore)
364
- this.dataStore?.close();
362
+ this.closing = true;
363
+ let routerError;
364
+ try {
365
+ if (this.ownsRouter)
366
+ await this.router?.disposeAll?.();
367
+ }
368
+ catch (error) {
369
+ routerError = error;
370
+ }
371
+ try {
372
+ this.closed = true;
373
+ for (const handle of [...this.openHandles])
374
+ handle.close();
375
+ this.listeners.clear();
376
+ this.unsubscribeRouter?.();
377
+ if (this.ownsSessionStore)
378
+ this.sessionStore.close?.();
379
+ if (this.ownsDataStore)
380
+ this.dataStore?.close();
381
+ }
382
+ finally {
383
+ this.closing = false;
384
+ }
385
+ if (routerError)
386
+ throw routerError;
365
387
  }
366
388
  listenerCount(sessionId) {
367
389
  if (sessionId)
@@ -825,7 +847,7 @@ export class LocalCliSessionSource {
825
847
  listener(update);
826
848
  }
827
849
  assertOpen() {
828
- if (this.closed)
850
+ if (this.closed || this.closing)
829
851
  throw new CliSourceError("source_closed", "Local CLI session source is closed");
830
852
  }
831
853
  }
package/dist/cli.js CHANGED
@@ -135,9 +135,14 @@ export async function runPiboCli(argv = process.argv) {
135
135
  await runCronCli([argv[0] ?? "node", "pibo cron", ...argv.slice(3)]);
136
136
  return;
137
137
  }
138
+ if (argv[2] === "loop") {
139
+ const { runLoopCli } = await import("./loops/cli.js");
140
+ await runLoopCli([argv[0] ?? "node", "pibo loop", ...argv.slice(3)]);
141
+ return;
142
+ }
138
143
  if (argv[2] === "ralph") {
139
- const { runRalphCli } = await import("./ralph/cli.js");
140
- await runRalphCli([argv[0] ?? "node", "pibo ralph", ...argv.slice(3)]);
144
+ const { runLoopCli } = await import("./loops/cli.js");
145
+ await runLoopCli([argv[0] ?? "node", "pibo ralph", ...argv.slice(3)], { mode: "ralph", commandName: "pibo ralph" });
141
146
  return;
142
147
  }
143
148
  if (argv[2] === "vscode") {
@@ -266,16 +271,27 @@ export async function runPiboCli(argv = process.argv) {
266
271
  const { runCronCli } = await import("./cron/cli.js");
267
272
  await runCronCli([argv[0] ?? "node", "pibo cron", ...args]);
268
273
  });
274
+ program
275
+ .command("loop")
276
+ .description("Manage continuous agent loops")
277
+ .helpOption(false)
278
+ .allowUnknownOption(true)
279
+ .allowExcessArguments(true)
280
+ .argument("[args...]")
281
+ .action(async (args) => {
282
+ const { runLoopCli } = await import("./loops/cli.js");
283
+ await runLoopCli([argv[0] ?? "node", "pibo loop", ...args]);
284
+ });
269
285
  program
270
286
  .command("ralph")
271
- .description("Manage continuous Ralph jobs")
287
+ .description("Legacy alias for Ralph-mode loops")
272
288
  .helpOption(false)
273
289
  .allowUnknownOption(true)
274
290
  .allowExcessArguments(true)
275
291
  .argument("[args...]")
276
292
  .action(async (args) => {
277
- const { runRalphCli } = await import("./ralph/cli.js");
278
- await runRalphCli([argv[0] ?? "node", "pibo ralph", ...args]);
293
+ const { runLoopCli } = await import("./loops/cli.js");
294
+ await runLoopCli([argv[0] ?? "node", "pibo ralph", ...args], { mode: "ralph", commandName: "pibo ralph" });
279
295
  });
280
296
  program
281
297
  .command("vscode")
@@ -445,47 +461,48 @@ function warnIfUnsupportedGatewayNodeVersion() {
445
461
  console.warn(`[pibo] warning: ${message}`);
446
462
  }
447
463
  function printRootDiscoveryText() {
448
- return `pibo - agent-oriented CLI
449
-
450
- Commands:
451
- config Manage local pibo config
452
- mcp Discover and call configured MCP servers
453
- tools Install and inspect curated external CLI tools
454
- pi-packages Register Pi Coding Agent packages
455
- debug Inspect local Pibo data
456
- data Inspect and maintain Pibo data stores
457
- compute Manage Pibo Docker compute workers
458
- resources Inspect and safely reap managed compute and browser resources
459
- setup Plan user-host installs and developer-host upgrades
460
- skills Manage Pibo user skills
461
- cron Manage scheduled Pibo jobs
462
- ralph Manage continuous Ralph jobs
463
- vscode Manage the Pibo VS Code extension
464
- profile Inspect a pibo profile, including active saved Chat custom agents
465
- tui Start the direct Pi TUI
466
- tui:routed Start the local routed Pibo TUI
467
- tui:sessions Start the reduced Web Chat-derived session UI
468
- gateway Inspect and restart host gateways through safe CLI commands
469
- gateway:web Start a web gateway runtime (use --auth=local for loopback-only local auth)
470
-
471
- Options:
472
- --version Print the Pibo CLI version
473
-
474
- Next:
475
- pibo <command> --help
464
+ return `pibo - agent-oriented CLI
465
+
466
+ Commands:
467
+ config Manage local pibo config
468
+ mcp Discover and call configured MCP servers
469
+ tools Install and inspect curated external CLI tools
470
+ pi-packages Register Pi Coding Agent packages
471
+ debug Inspect local Pibo data
472
+ data Inspect and maintain Pibo data stores
473
+ compute Manage Pibo Docker compute workers
474
+ resources Inspect and safely reap managed compute and browser resources
475
+ setup Plan user-host installs and developer-host upgrades
476
+ skills Manage Pibo user skills
477
+ cron Manage scheduled Pibo jobs
478
+ loop Manage continuous agent loops (goal mode by default)
479
+ ralph Legacy alias for Ralph-mode loops
480
+ vscode Manage the Pibo VS Code extension
481
+ profile Inspect a pibo profile, including active saved Chat custom agents
482
+ tui Start the direct Pi TUI
483
+ tui:routed Start the local routed Pibo TUI
484
+ tui:sessions Start the reduced Web Chat-derived session UI
485
+ gateway Inspect and restart host gateways through safe CLI commands
486
+ gateway:web Start a web gateway runtime (use --auth=local for loopback-only local auth)
487
+
488
+ Options:
489
+ --version Print the Pibo CLI version
490
+
491
+ Next:
492
+ pibo <command> --help
476
493
  `;
477
494
  }
478
495
  function printConfigDiscoveryText() {
479
- return `pibo config - local config at ${getDefaultPiboConfigPath()}
480
-
481
- Commands:
482
- keys List supported config keys
483
- show Print redacted config JSON
484
- get <key> Print one redacted config value
485
- set <key> <value> Set one config value
486
- del <key> Delete one config value
487
-
488
- Next:
489
- pibo config keys
496
+ return `pibo config - local config at ${getDefaultPiboConfigPath()}
497
+
498
+ Commands:
499
+ keys List supported config keys
500
+ show Print redacted config JSON
501
+ get <key> Print one redacted config value
502
+ set <key> <value> Set one config value
503
+ del <key> Delete one config value
504
+
505
+ Next:
506
+ pibo config keys
490
507
  `;
491
508
  }
@@ -188,16 +188,16 @@ export async function runComputeCli(argv) {
188
188
  .helpOption("-h, --help", "Show help")
189
189
  .showHelpAfterError()
190
190
  .helpCommand("help [command]", "Show help for command")
191
- .addHelpText("after", `
192
- Examples:
193
- $ pibo compute spawn
194
- $ pibo compute dev spawn --worktree my-fix
195
- $ pibo compute list
196
- $ pibo compute release pibo-worker-abc123
197
-
198
- Next:
199
- $ pibo compute spawn --help
200
- $ pibo compute dev --help
191
+ .addHelpText("after", `
192
+ Examples:
193
+ $ pibo compute spawn
194
+ $ pibo compute dev spawn --worktree my-fix
195
+ $ pibo compute list
196
+ $ pibo compute release pibo-worker-abc123
197
+
198
+ Next:
199
+ $ pibo compute spawn --help
200
+ $ pibo compute dev --help
201
201
  `);
202
202
  program
203
203
  .command("spawn")
@@ -208,11 +208,11 @@ Next:
208
208
  .option("--idle-seconds <n>", "Tag the worker idle retention in seconds")
209
209
  .option("--ralph-job-id <id>", "Tag the Ralph job id when this worker is Ralph-owned")
210
210
  .option("--ralph-run-id <id>", "Tag the Ralph run id when this worker is Ralph-owned")
211
- .addHelpText("after", `
212
- Creates a worker container and starts the gateway. Prints JSON with the container id and mapped ports.
213
-
214
- Use this for quick isolated checks. For code changes, prefer:
215
- $ pibo compute dev spawn --worktree my-fix
211
+ .addHelpText("after", `
212
+ Creates a worker container and starts the gateway. Prints JSON with the container id and mapped ports.
213
+
214
+ Use this for quick isolated checks. For code changes, prefer:
215
+ $ pibo compute dev spawn --worktree my-fix
216
216
  `)
217
217
  .action(async (options) => {
218
218
  await mkdir(path.dirname(HASH_FILE), { recursive: true });
@@ -238,11 +238,11 @@ Use this for quick isolated checks. For code changes, prefer:
238
238
  .command("dev")
239
239
  .description("Manage development workers with Git worktrees")
240
240
  .helpCommand("help [command]", "Show help for command")
241
- .addHelpText("after", `
242
- Use dev workers for Pibo code changes. Each worker gets its own Git worktree and port block.
243
-
244
- Next:
245
- $ pibo compute dev spawn --help
241
+ .addHelpText("after", `
242
+ Use dev workers for Pibo code changes. Each worker gets its own Git worktree and port block.
243
+
244
+ Next:
245
+ $ pibo compute dev spawn --help
246
246
  `);
247
247
  devCmd
248
248
  .command("spawn")
@@ -254,11 +254,11 @@ Next:
254
254
  .option("--idle-seconds <n>", "Tag the worker idle retention in seconds")
255
255
  .option("--ralph-job-id <id>", "Tag the Ralph job id when this worker is Ralph-owned")
256
256
  .option("--ralph-run-id <id>", "Tag the Ralph run id when this worker is Ralph-owned")
257
- .addHelpText("after", `
258
- Creates .worktrees/<name>, starts a worker, and prints JSON with ports and the worktree path.
259
-
260
- Example:
261
- $ pibo compute dev spawn --worktree my-fix
257
+ .addHelpText("after", `
258
+ Creates .worktrees/<name>, starts a worker, and prints JSON with ports and the worktree path.
259
+
260
+ Example:
261
+ $ pibo compute dev spawn --worktree my-fix
262
262
  `)
263
263
  .action(async (options) => {
264
264
  await mkdir(path.dirname(DEP_HASH_FILE), { recursive: true });
@@ -293,8 +293,8 @@ Example:
293
293
  program
294
294
  .command("rebuild")
295
295
  .description("Rebuild the pibo:latest Docker image")
296
- .addHelpText("after", `
297
- Rebuilds from the current workspace and refreshes compute image hashes.
296
+ .addHelpText("after", `
297
+ Rebuilds from the current workspace and refreshes compute image hashes.
298
298
  `)
299
299
  .action(async () => {
300
300
  console.error(`Rebuilding ${IMAGE_NAME} from ${WORKSPACE_DIR}...`);
@@ -309,11 +309,11 @@ Rebuilds from the current workspace and refreshes compute image hashes.
309
309
  .option("--all", "Include stopped, exited, dead, and restarting Pibo workers")
310
310
  .option("--json", "Print machine-readable worker metadata")
311
311
  .option("--verbose", "Show full Docker port bindings in text output")
312
- .addHelpText("after", `
313
- Shows each worker's name, role, status, mapped ports, creation time, holder, and Ralph labels when present.
314
-
315
- Next:
316
- $ pibo compute list --all --json
312
+ .addHelpText("after", `
313
+ Shows each worker's name, role, status, mapped ports, creation time, holder, and Ralph labels when present.
314
+
315
+ Next:
316
+ $ pibo compute list --all --json
317
317
  `)
318
318
  .action(async (options) => {
319
319
  const workers = await listWorkers({ all: options.all === true });
@@ -330,14 +330,14 @@ Next:
330
330
  .option("--json", "Print machine-readable resource health")
331
331
  .option("--browser-pool-root <path>", "Browser pool root directory to scan")
332
332
  .option("--browser-use-home <path>", "Browser-use home directory to scan for stale CDP files")
333
- .addHelpText("after", `
334
- Reports browser main-process counts, active browser-pool leases, stale CDP files, dirty/OOM workers, Docker disk pressure, and reaper/timer status.
335
- The command is read-only and never reaps, prunes, stops, or removes resources.
336
-
337
- Next:
338
- $ pibo compute health --json
339
- $ pibo tools browser-use pool reap --json
340
- $ pibo compute reap --dry-run --json
333
+ .addHelpText("after", `
334
+ Reports browser main-process counts, active browser-pool leases, stale CDP files, dirty/OOM workers, Docker disk pressure, and reaper/timer status.
335
+ The command is read-only and never reaps, prunes, stops, or removes resources.
336
+
337
+ Next:
338
+ $ pibo compute health --json
339
+ $ pibo tools browser-use pool reap --json
340
+ $ pibo compute reap --dry-run --json
341
341
  `)
342
342
  .action(async (options) => {
343
343
  const health = await getComputeResourceHealth({ browserPoolRoot: options.browserPoolRoot, browserUseHome: options.browserUseHome });
@@ -352,13 +352,13 @@ Next:
352
352
  .alias("disk")
353
353
  .description("Show read-only Docker disk and build-cache diagnostics")
354
354
  .option("--json", "Print machine-readable disk diagnostics")
355
- .addHelpText("after", `
356
- Reports Docker image, container, local volume, and build-cache usage without pruning anything.
357
- Cleanup suggestions distinguish compute containers, images, build cache, and Git worktrees.
358
-
359
- Next:
360
- $ pibo compute diagnostics --json
361
- $ pibo compute reap --dry-run --json
355
+ .addHelpText("after", `
356
+ Reports Docker image, container, local volume, and build-cache usage without pruning anything.
357
+ Cleanup suggestions distinguish compute containers, images, build cache, and Git worktrees.
358
+
359
+ Next:
360
+ $ pibo compute diagnostics --json
361
+ $ pibo compute reap --dry-run --json
362
362
  `)
363
363
  .action(async (options) => {
364
364
  const diagnostics = await getComputeDiskDiagnostics();
@@ -372,9 +372,9 @@ Next:
372
372
  .command("release")
373
373
  .description("Stop and remove a worker container")
374
374
  .argument("<id>", "Container name or ID")
375
- .addHelpText("after", `
376
- Use the name or id shown by:
377
- $ pibo compute list
375
+ .addHelpText("after", `
376
+ Use the name or id shown by:
377
+ $ pibo compute list
378
378
  `)
379
379
  .action(async (id) => {
380
380
  await releaseWorker(id);
@@ -390,10 +390,10 @@ Use the name or id shown by:
390
390
  .option("--dry-run", "Preview the cleanup plan without removing containers")
391
391
  .option("--apply", "Apply the cleanup plan and remove selected containers")
392
392
  .option("--json", "Print machine-readable cleanup plan/result")
393
- .addHelpText("after", `
394
- Defaults to a dry-run plan for one-time workers. Dev workers require --include-dev.
395
- Stopped, dirty/OOM, and old workers are selected unless disabled by selector flags.
396
- Worktrees are preserved; container cleanup never deletes Git worktrees.
393
+ .addHelpText("after", `
394
+ Defaults to a dry-run plan for one-time workers. Dev workers require --include-dev.
395
+ Stopped, dirty/OOM, and old workers are selected unless disabled by selector flags.
396
+ Worktrees are preserved; container cleanup never deletes Git worktrees.
397
397
  `)
398
398
  .action(async (options) => {
399
399
  const maxAge = Number(options.maxAgeMinutes);
@@ -73,8 +73,11 @@ export function buildComputeResourceHealth(options = {}) {
73
73
  if (browserProcessMatchesPool(process, pool.state))
74
74
  assignedMainPids.add(process.pid);
75
75
  }
76
+ const activeWorkerOwnedMainPids = new Set(mainProcesses
77
+ .filter((process) => Boolean(findOwningActiveWorker(process, processes, workers)))
78
+ .map((process) => process.pid));
76
79
  const unassignedMainProcessDetails = mainProcesses
77
- .filter((process) => !assignedMainPids.has(process.pid))
80
+ .filter((process) => !assignedMainPids.has(process.pid) && !activeWorkerOwnedMainPids.has(process.pid))
78
81
  .map((process) => describeUnassignedBrowserProcess(process, workers));
79
82
  const unassignedChromiumMainProcesses = unassignedMainProcessDetails.length;
80
83
  const activePoolIds = perWorker.filter((pool) => pool.activeLeaseCount > 0).map((pool) => `${pool.workerId}/${pool.poolId}`);
@@ -148,6 +151,28 @@ function browserLeakMessage(unassignedCount) {
148
151
  return `${unassignedCount} unmanaged Chromium main process(es) are not associated with a managed browser pool.`;
149
152
  return "Chromium main-process count exceeds managed pool expectations.";
150
153
  }
154
+ function findOwningActiveWorker(process, processes, workers) {
155
+ const containerId = process.containerId;
156
+ if (containerId && containerId.length >= 12) {
157
+ const byContainer = workers.find((worker) => worker.state === "running" && (worker.id === containerId || worker.id.startsWith(containerId) || containerId.startsWith(worker.id)));
158
+ if (byContainer)
159
+ return byContainer;
160
+ }
161
+ const activeWorkersByPid = new Map(workers
162
+ .filter((worker) => worker.state === "running" && worker.hostPid !== undefined && worker.hostPid > 1)
163
+ .map((worker) => [worker.hostPid, worker]));
164
+ const processByPid = new Map(processes.map((candidate) => [candidate.pid, candidate]));
165
+ const visited = new Set();
166
+ let current = process;
167
+ while (current && current.pid > 1 && !visited.has(current.pid)) {
168
+ visited.add(current.pid);
169
+ const direct = activeWorkersByPid.get(current.pid) ?? activeWorkersByPid.get(current.ppid);
170
+ if (direct)
171
+ return direct;
172
+ current = processByPid.get(current.ppid);
173
+ }
174
+ return undefined;
175
+ }
151
176
  function describeUnassignedBrowserProcess(process, workers) {
152
177
  const worker = workers.find((candidate) => candidate.hostPid !== undefined && candidate.hostPid > 0 && candidate.hostPid === process.ppid);
153
178
  const userDataDir = readChromeArg(process.args, "user-data-dir");
@@ -248,7 +273,12 @@ async function collectWorkers() {
248
273
  async function collectProcesses() {
249
274
  try {
250
275
  const { stdout } = await execFileAsync("ps", ["-eo", "pid=,ppid=,pgid=,etimes=,comm=,args="], { maxBuffer: 10 * 1024 * 1024 });
251
- return { processes: parseProcessList(stdout) };
276
+ const processes = parseProcessList(stdout);
277
+ await Promise.all(processes.filter((process) => process.isChromium && process.isMainProcess).map(async (process) => {
278
+ const cgroup = await readFile(`/proc/${process.pid}/cgroup`, "utf8").catch(() => "");
279
+ process.containerId = parseDockerContainerIdFromCgroup(cgroup);
280
+ }));
281
+ return { processes };
252
282
  }
253
283
  catch (error) {
254
284
  return { processes: [], error: error instanceof Error ? error.message : String(error) };
@@ -299,6 +329,9 @@ function detectReaperTimerStatus() {
299
329
  }
300
330
  return readResourceReaperTimerStatus();
301
331
  }
332
+ export function parseDockerContainerIdFromCgroup(value) {
333
+ return value.match(/(?:^|\/)docker[-/]([a-f0-9]{12,64})(?:\.scope|\/|$)/im)?.[1];
334
+ }
302
335
  function browserProcessMatchesPool(process, state) {
303
336
  if (state.pid && process.pid === state.pid)
304
337
  return true;
@@ -1 +1,6 @@
1
- export {};
1
+ export class PiboSteeringUnavailableError extends Error {
2
+ constructor(message = "The active session cannot accept steering right now.", options) {
3
+ super(message, options);
4
+ this.name = "PiboSteeringUnavailableError";
5
+ }
6
+ }