@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
package/dist/debug/web.js CHANGED
@@ -52,98 +52,98 @@ export async function runDebugWeb(args) {
52
52
  throw new Error(`Unknown pibo debug web command "${command}". Run pibo debug web --help.`);
53
53
  }
54
54
  function printWebDiscovery() {
55
- console.log(`pibo debug web - inspect browser render state via CDP
56
-
57
- Commands:
58
- targets List Chrome CDP targets with Chat auth hints
59
- attach-chat Show the best authenticated Chat Web target
60
- snapshot Capture a scoped compact DOM snapshot
61
- diff Compare current scoped snapshot against previous or artifact
62
- watch Record a bounded scoped DOM/focus/route timeline
63
- scenario Run guided Chat Web debug workflows
64
- report Render saved debug artifacts as reviewer-friendly Markdown
65
-
66
- Next:
67
- pibo debug web targets
68
- pibo debug web snapshot --preset session-list
69
- pibo debug web watch --preset chat-shell --duration 5000
70
- pibo debug web report streaming-benchmark --from artifact.json
55
+ console.log(`pibo debug web - inspect browser render state via CDP
56
+
57
+ Commands:
58
+ targets List Chrome CDP targets with Chat auth hints
59
+ attach-chat Show the best authenticated Chat Web target
60
+ snapshot Capture a scoped compact DOM snapshot
61
+ diff Compare current scoped snapshot against previous or artifact
62
+ watch Record a bounded scoped DOM/focus/route timeline
63
+ scenario Run guided Chat Web debug workflows
64
+ report Render saved debug artifacts as reviewer-friendly Markdown
65
+
66
+ Next:
67
+ pibo debug web targets
68
+ pibo debug web snapshot --preset session-list
69
+ pibo debug web watch --preset chat-shell --duration 5000
70
+ pibo debug web report streaming-benchmark --from artifact.json
71
71
  `);
72
72
  }
73
73
  function printSnapshotHelp() {
74
- console.log(`pibo debug web snapshot - capture a scoped compact DOM snapshot
75
-
76
- Usage:
77
- pibo debug web snapshot --scope <selector> [--target id|ws] [--json] [--artifact]
78
- pibo debug web snapshot --preset session-list
79
-
80
- Presets:
81
- app | route-shell | sidebar | session-list | chat-shell | composer
82
-
83
- Next:
84
- pibo debug web diff --preset session-list
85
- pibo debug web watch --preset chat-shell --duration 5000
74
+ console.log(`pibo debug web snapshot - capture a scoped compact DOM snapshot
75
+
76
+ Usage:
77
+ pibo debug web snapshot --scope <selector> [--target id|ws] [--json] [--artifact]
78
+ pibo debug web snapshot --preset session-list
79
+
80
+ Presets:
81
+ app | route-shell | sidebar | session-list | chat-shell | composer
82
+
83
+ Next:
84
+ pibo debug web diff --preset session-list
85
+ pibo debug web watch --preset chat-shell --duration 5000
86
86
  `);
87
87
  }
88
88
  function printWatchHelp() {
89
- console.log(`pibo debug web watch - record compact render-state changes
90
-
91
- Usage:
92
- pibo debug web watch --scope <selector> [--duration ms] [--target id|ws] [--json] [--artifact]
93
- pibo debug web watch --preset chat-shell --duration 5000
94
-
95
- Defaults:
96
- duration=5000ms, max=30000ms, event budget=500
97
-
98
- Next:
99
- pibo debug web diff --preset chat-shell
100
- pibo debug web scenario new-session --manual
89
+ console.log(`pibo debug web watch - record compact render-state changes
90
+
91
+ Usage:
92
+ pibo debug web watch --scope <selector> [--duration ms] [--target id|ws] [--json] [--artifact]
93
+ pibo debug web watch --preset chat-shell --duration 5000
94
+
95
+ Defaults:
96
+ duration=5000ms, max=30000ms, event budget=500
97
+
98
+ Next:
99
+ pibo debug web diff --preset chat-shell
100
+ pibo debug web scenario new-session --manual
101
101
  `);
102
102
  }
103
103
  function printScenarioHelp() {
104
- console.log(`pibo debug web scenario - guided Chat Web debug workflows
105
-
106
- Usage:
107
- pibo debug web scenario new-session [--manual|--act] [--duration ms] [--json] [--artifact]
108
- pibo debug web scenario streaming-benchmark [--fixture|--backend-fixture] [--fixture-profile steady|jitter|burst|batch] [--fixture-mix text|reasoning-text|markdown|gfm-markdown|gfm-task-markdown|gfm-full-markdown] [--fixture-prelude-messages n] [--simulate-reconnect|--simulate-trace-catchup] [--duration ms] [--runs n] [--from artifact.json] [--provider-request-id pr_...|--provider-session-id ps_...|--provider-turn-id turn_...|--provider-selected-session] [--compare-url url|--compare-hosted|--compare-hosted-if-configured] [--assert] [--expect-regression text] [--negative-profile batch|overlay-drop] [--json] [--artifact]
109
-
110
- Defaults:
111
- new-session --manual waits while you click New Session yourself.
112
- new-session --act clicks the discovered New Session button after the watcher starts.
113
- streaming-benchmark enables debugStreaming for future events, observes assistant DOM increments, and snapshots window.__piboStreamingDebug.
114
- streaming-benchmark --fixture navigates the target to a deterministic in-browser stream fixture before measuring.
115
- streaming-benchmark --backend-fixture posts to /api/chat/debug/streaming-fixture and records EventSource metrics while the real app consumes deterministic /api/chat/events frames.
116
- streaming-benchmark --fixture-profile selects steady cadence, deterministic jitter, bursty timing, or intentional batch stress.
117
- streaming-benchmark --fixture-mix includes text-only, mixed reasoning/text, CommonMark Markdown, simple GFM Markdown, or full-parser GFM Markdown assistant deltas.
118
- streaming-benchmark --fixture-prelude-messages seeds completed live assistant messages before counters reset so large live overlays can be measured without changing fixture preservation denominators.
119
- streaming-benchmark --simulate-reconnect reloads the app with an EventSource probe, forces one live stream close, and verifies reconnect/transient ids.
120
- streaming-benchmark --simulate-trace-catchup suppresses backend live text deltas and verifies trace snapshot recovery.
121
- streaming-benchmark --runs repeats the same scenario and reports medians; --from compares against a prior benchmark artifact.
122
- streaming-benchmark --compare-url runs the same backend fixture at another Chat URL, for direct-vs-hosted SSE comparison.
123
- streaming-benchmark --compare-hosted uses PIBO_DEV_PUBLIC_URL or PIBO_DEV_BASE_URL from the environment or .env.developer-host as the compare URL.
124
- streaming-benchmark --compare-hosted-if-configured runs the hosted comparison when a dev URL is configured; otherwise it records a warning and keeps the primary benchmark.
125
- streaming-benchmark --provider-request-id attaches provider/Pi telemetry delta counts, byte stats, gap stats, parse errors, first-text latency, and provider-to-transport preservation ratios from pibo debug telemetry.
126
- streaming-benchmark --provider-session-id, --provider-turn-id, or --provider-selected-session discovers the latest provider request from telemetry session/turn metadata after the benchmark window before attaching provider metrics.
127
- streaming-benchmark --assert exits non-zero when fixture/debug/DOM/provider preservation gates fail.
128
- streaming-benchmark --expect-regression marks a required regression substring for controlled negative benchmarks; unexpected or missing expected regressions still fail with --assert.
129
- streaming-benchmark --negative-profile batch expands to the backend batch reasoning/text fixture with required controlled regression assertions.
130
- streaming-benchmark --negative-profile overlay-drop preserves SSE/EventSource input but drops live-overlay text/reasoning enqueue for a controlled pipeline-preservation failure.
104
+ console.log(`pibo debug web scenario - guided Chat Web debug workflows
105
+
106
+ Usage:
107
+ pibo debug web scenario new-session [--manual|--act] [--duration ms] [--json] [--artifact]
108
+ pibo debug web scenario streaming-benchmark [--fixture|--backend-fixture] [--fixture-profile steady|jitter|burst|batch] [--fixture-mix text|reasoning-text|markdown|gfm-markdown|gfm-task-markdown|gfm-full-markdown] [--fixture-prelude-messages n] [--simulate-reconnect|--simulate-trace-catchup] [--duration ms] [--runs n] [--from artifact.json] [--provider-request-id pr_...|--provider-session-id ps_...|--provider-turn-id turn_...|--provider-selected-session] [--compare-url url|--compare-hosted|--compare-hosted-if-configured] [--assert] [--expect-regression text] [--negative-profile batch|overlay-drop] [--json] [--artifact]
109
+
110
+ Defaults:
111
+ new-session --manual waits while you click New Session yourself.
112
+ new-session --act clicks the discovered New Session button after the watcher starts.
113
+ streaming-benchmark enables debugStreaming for future events, observes assistant DOM increments, and snapshots window.__piboStreamingDebug.
114
+ streaming-benchmark --fixture navigates the target to a deterministic in-browser stream fixture before measuring.
115
+ streaming-benchmark --backend-fixture posts to /api/chat/debug/streaming-fixture and records EventSource metrics while the real app consumes deterministic /api/chat/events frames.
116
+ streaming-benchmark --fixture-profile selects steady cadence, deterministic jitter, bursty timing, or intentional batch stress.
117
+ streaming-benchmark --fixture-mix includes text-only, mixed reasoning/text, CommonMark Markdown, simple GFM Markdown, or full-parser GFM Markdown assistant deltas.
118
+ streaming-benchmark --fixture-prelude-messages seeds completed live assistant messages before counters reset so large live overlays can be measured without changing fixture preservation denominators.
119
+ streaming-benchmark --simulate-reconnect reloads the app with an EventSource probe, forces one live stream close, and verifies reconnect/transient ids.
120
+ streaming-benchmark --simulate-trace-catchup suppresses backend live text deltas and verifies trace snapshot recovery.
121
+ streaming-benchmark --runs repeats the same scenario and reports medians; --from compares against a prior benchmark artifact.
122
+ streaming-benchmark --compare-url runs the same backend fixture at another Chat URL, for direct-vs-hosted SSE comparison.
123
+ streaming-benchmark --compare-hosted uses PIBO_DEV_PUBLIC_URL or PIBO_DEV_BASE_URL from the environment or .env.developer-host as the compare URL.
124
+ streaming-benchmark --compare-hosted-if-configured runs the hosted comparison when a dev URL is configured; otherwise it records a warning and keeps the primary benchmark.
125
+ streaming-benchmark --provider-request-id attaches provider/Pi telemetry delta counts, byte stats, gap stats, parse errors, first-text latency, and provider-to-transport preservation ratios from pibo debug telemetry.
126
+ streaming-benchmark --provider-session-id, --provider-turn-id, or --provider-selected-session discovers the latest provider request from telemetry session/turn metadata after the benchmark window before attaching provider metrics.
127
+ streaming-benchmark --assert exits non-zero when fixture/debug/DOM/provider preservation gates fail.
128
+ streaming-benchmark --expect-regression marks a required regression substring for controlled negative benchmarks; unexpected or missing expected regressions still fail with --assert.
129
+ streaming-benchmark --negative-profile batch expands to the backend batch reasoning/text fixture with required controlled regression assertions.
130
+ streaming-benchmark --negative-profile overlay-drop preserves SSE/EventSource input but drops live-overlay text/reasoning enqueue for a controlled pipeline-preservation failure.
131
131
  `);
132
132
  }
133
133
  function printReportHelp() {
134
- console.log(`pibo debug web report - render saved debug artifacts
135
-
136
- Usage:
137
- pibo debug web report streaming-benchmark --from artifact.json [--compact] [--output report.md] [--json-output report.json] [--json] [--artifact]
138
-
139
- Reports:
140
- streaming-benchmark Summarize saved pibo debug web scenario streaming-benchmark JSON as Markdown.
141
- --compact Render reviewer-friendly Markdown tables instead of the detailed line report.
142
- --output Write the Markdown report to a specific file path.
143
- --json-output Write the normalized JSON report payload and compact rows to a specific file path.
144
-
145
- Next:
146
- pibo debug web scenario streaming-benchmark --backend-fixture --assert --artifact
134
+ console.log(`pibo debug web report - render saved debug artifacts
135
+
136
+ Usage:
137
+ pibo debug web report streaming-benchmark --from artifact.json [--compact] [--output report.md] [--json-output report.json] [--json] [--artifact]
138
+
139
+ Reports:
140
+ streaming-benchmark Summarize saved pibo debug web scenario streaming-benchmark JSON as Markdown.
141
+ --compact Render reviewer-friendly Markdown tables instead of the detailed line report.
142
+ --output Write the Markdown report to a specific file path.
143
+ --json-output Write the normalized JSON report payload and compact rows to a specific file path.
144
+
145
+ Next:
146
+ pibo debug web scenario streaming-benchmark --backend-fixture --assert --artifact
147
147
  `);
148
148
  }
149
149
  async function runTargets(options) {
@@ -204,13 +204,13 @@ async function runSnapshot(options) {
204
204
  }
205
205
  async function runDiff(options) {
206
206
  if (options.positionals[0] === "--help" || options.positionals[0] === "-h") {
207
- console.log(`pibo debug web diff - compare scoped render snapshots
208
-
209
- Usage:
210
- pibo debug web diff --scope <selector> [--from artifact.json]
211
- pibo debug web diff --preset session-list
212
-
213
- Default --from is the last snapshot captured by pibo debug web snapshot.
207
+ console.log(`pibo debug web diff - compare scoped render snapshots
208
+
209
+ Usage:
210
+ pibo debug web diff --scope <selector> [--from artifact.json]
211
+ pibo debug web diff --preset session-list
212
+
213
+ Default --from is the last snapshot captured by pibo debug web snapshot.
214
214
  `);
215
215
  return;
216
216
  }
@@ -511,9 +511,9 @@ async function prepareStreamingBenchmarkEventSourceProbe(client, targetUrl) {
511
511
  await client.send("Page.addScriptToEvaluateOnNewDocument", { source: streamingBenchmarkEventSourceProbeScript() }, 5_000);
512
512
  if (targetUrl)
513
513
  await navigateStreamingBenchmarkTarget(client, targetUrl);
514
- const state = await client.evaluate(`(() => {
515
- try { localStorage.setItem('pibo.chat.debugStreaming', '1'); } catch {}
516
- return { href: location.href };
514
+ const state = await client.evaluate(`(() => {
515
+ try { localStorage.setItem('pibo.chat.debugStreaming', '1'); } catch {}
516
+ return { href: location.href };
517
517
  })()`, 5_000);
518
518
  const url = new URL(state.href);
519
519
  if (url.protocol !== "http:" && url.protocol !== "https:")
@@ -544,9 +544,9 @@ async function navigateStreamingBenchmarkFixture(client, fixtureProfile, fixture
544
544
  await client.evaluate("new Promise((resolve) => { if (document.readyState === 'complete') resolve(true); else addEventListener('load', () => resolve(true), { once: true }); })", 5_000);
545
545
  }
546
546
  async function enableStreamingDebugForCurrentApp(client) {
547
- const state = await client.evaluate(`(() => {
548
- try { localStorage.setItem('pibo.chat.debugStreaming', '1'); } catch {}
549
- return { href: location.href, hasReset: typeof window.__piboStreamingDebugReset === 'function' };
547
+ const state = await client.evaluate(`(() => {
548
+ try { localStorage.setItem('pibo.chat.debugStreaming', '1'); } catch {}
549
+ return { href: location.href, hasReset: typeof window.__piboStreamingDebugReset === 'function' };
550
550
  })()`, 5_000);
551
551
  if (state.hasReset)
552
552
  return;
@@ -609,24 +609,24 @@ export async function runGatewayCli(argv = process.argv) {
609
609
  process.exitCode = 1;
610
610
  }
611
611
  function printGatewayHelp() {
612
- console.log(`pibo gateway - Gateway management
613
-
614
- Commands:
615
- web status Inspect the production gateway
616
- web start Start the production gateway
617
- web restart Safely restart the production gateway
618
- web doctor Check production gateway health
619
- dev status Inspect the dev gateway
620
- dev start Start the dev gateway
621
- dev restart Restart the dev gateway
622
- dev doctor Check dev gateway health
623
-
624
- Options:
625
- --force --confirm ${RESTART_CONFIRMATION_TOKEN}
626
- Force a production restart after explicit confirmation
627
-
628
- Next:
629
- pibo gateway web status
630
- pibo gateway dev status
612
+ console.log(`pibo gateway - Gateway management
613
+
614
+ Commands:
615
+ web status Inspect the production gateway
616
+ web start Start the production gateway
617
+ web restart Safely restart the production gateway
618
+ web doctor Check production gateway health
619
+ dev status Inspect the dev gateway
620
+ dev start Start the dev gateway
621
+ dev restart Restart the dev gateway
622
+ dev doctor Check dev gateway health
623
+
624
+ Options:
625
+ --force --confirm ${RESTART_CONFIRMATION_TOKEN}
626
+ Force a production restart after explicit confirmation
627
+
628
+ Next:
629
+ pibo gateway web status
630
+ pibo gateway dev status
631
631
  `);
632
632
  }
@@ -326,8 +326,10 @@ export class PiboGatewayServer {
326
326
  getProfiles: () => this.pluginRegistry.getProfileInfos(),
327
327
  createProfile: (name) => this.pluginRegistry.createProfile(name),
328
328
  getCapabilityCatalog: () => this.pluginRegistry.getCapabilityCatalog(),
329
- getRalphStopConditionDefinitions: () => this.pluginRegistry.getRalphStopConditionDefinitions(),
330
- getRalphStopConditionInfos: () => this.pluginRegistry.getRalphStopConditionInfos(),
329
+ getLoopStopConditionDefinitions: () => this.pluginRegistry.getLoopStopConditionDefinitions(),
330
+ getLoopStopConditionInfos: () => this.pluginRegistry.getLoopStopConditionInfos(),
331
+ getRalphStopConditionDefinitions: () => this.pluginRegistry.getLoopStopConditionDefinitions(),
332
+ getRalphStopConditionInfos: () => this.pluginRegistry.getLoopStopConditionInfos(),
331
333
  upsertProfile: (profile) => this.pluginRegistry.upsertProfile(profile),
332
334
  removeProfile: (name) => this.pluginRegistry.removeProfile(name),
333
335
  upsertContextFile: (contextFile) => this.pluginRegistry.upsertContextFile(contextFile),
@@ -6,7 +6,7 @@ import { createPiboChatWebPlugin } from "../plugins/chat-web.js";
6
6
  import { createPiboChatVscodeWebPlugin } from "../plugins/chat-vscode-web.js";
7
7
  import { createPiboContextFilesPlugin } from "../plugins/context-files.js";
8
8
  import { createPiboCronPlugin } from "../cron/plugin.js";
9
- import { createPiboRalphPlugin } from "../ralph/plugin.js";
9
+ import { createPiboLoopPlugin } from "../loops/plugin.js";
10
10
  import { createPiboDevAuthPlugin } from "../plugins/dev-auth.js";
11
11
  import { PiboPluginRegistry } from "../plugins/registry.js";
12
12
  import { createPiboWebHostPlugin } from "../plugins/web.js";
@@ -151,7 +151,7 @@ export function createWebPiboPluginRegistry(options = {}) {
151
151
  workspaceRoot: resolvedOptions.chat?.userSkillWorkspaceRoot,
152
152
  }),
153
153
  createPiboChatCustomAgentProfilesPlugin({ agentStorePath: resolvedOptions.chat?.agentStorePath }),
154
- createPiboRalphPlugin({ ralphStorePath: resolvedOptions.chat?.ralphStorePath, dataStorePath: resolvedOptions.chat?.dataStorePath, dataPayloadRootDir: resolvedOptions.chat?.dataPayloadRootDir }),
154
+ createPiboLoopPlugin({ loopStorePath: resolvedOptions.chat?.ralphStorePath, dataStorePath: resolvedOptions.chat?.dataStorePath, dataPayloadRootDir: resolvedOptions.chat?.dataPayloadRootDir }),
155
155
  createPiboContextFilesPlugin(resolvedOptions.contextFiles),
156
156
  createPiboChatWebPlugin(resolvedOptions.chat),
157
157
  createPiboChatVscodeWebPlugin(),
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ export { LOCAL_TUI_CHANNEL_NAME, LocalRoutedTuiClient, createLocalRoutedTuiClien
19
19
  export { createWebHostChannel, DEFAULT_WEB_CHANNEL_HOST, DEFAULT_WEB_CHANNEL_PORT, WEB_CHANNEL_NAME } from "./web/channel.js";
20
20
  export { sendGatewayEvent, sendGatewayMessageAndWaitForReply } from "./gateway/request.js";
21
21
  export { createSubagentToolDefinitions, createSubagentToolName, } from "./subagents/tool.js";
22
+ export { PiboSteeringUnavailableError } from "./core/events.js";
22
23
  export { InMemoryPiboSessionStore, createPiSessionId, createPiboSessionId, createPiboSession, } from "./sessions/store.js";
23
24
  export { runPiboCli } from "./cli.js";
24
25
  export { DEFAULT_PIBO_CONFIG_PATH, PIBO_CONFIG_KEYS, deletePiboConfigValue, getDefaultPiboConfigPath, getDisplayPiboConfigValue, getPiboConfigValue, isPiboConfigKeySecret, loadPiboConfig, redactPiboConfig, savePiboConfig, setPiboConfigValue, } from "./config/config.js";
@@ -0,0 +1,19 @@
1
+ export function goalActiveTimeSeconds(job) {
2
+ return Math.max(0, Math.floor(job.state.activeTimeSeconds ?? job.state.timeUsedSeconds ?? 0));
3
+ }
4
+ export function goalElapsedWallClockSeconds(job, now = new Date()) {
5
+ if (job.mode !== 'goal' || !job.state.goalStartedAt)
6
+ return 0;
7
+ const startedAt = Date.parse(job.state.goalStartedAt);
8
+ const endedAt = job.state.goalEndedAt ? Date.parse(job.state.goalEndedAt) : now.getTime();
9
+ if (!Number.isFinite(startedAt) || !Number.isFinite(endedAt))
10
+ return 0;
11
+ return Math.max(0, Math.floor((endedAt - startedAt) / 1000));
12
+ }
13
+ export function goalRemainingTokens(job) {
14
+ return job.tokenBudget === undefined ? undefined : Math.max(0, job.tokenBudget - (job.state.tokensUsed ?? 0));
15
+ }
16
+ export function goalCanStartNextTurn(job) {
17
+ const remaining = goalRemainingTokens(job);
18
+ return remaining === undefined || remaining > (job.tokenReserve ?? 0);
19
+ }
@@ -0,0 +1,8 @@
1
+ import { PiboLoopService } from './service.js';
2
+ import { createDefaultPiboLoopStore } from './store.js';
3
+ let currentLoopService;
4
+ export function getPiboLoopService() { return currentLoopService; }
5
+ export function createPiboLoopChannel(options = {}) {
6
+ return { name: 'pibo.loop', kind: 'custom', description: 'Runs continuous Loop Pibo agent jobs.', auth: { mode: 'trusted-local' }, start(context) { if (currentLoopService)
7
+ return; currentLoopService = new PiboLoopService({ ...options, context, store: createDefaultPiboLoopStore({ path: options.loopStorePath }) }); currentLoopService.start(); }, stop() { currentLoopService?.stop(); currentLoopService = undefined; } };
8
+ }
@@ -0,0 +1,224 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { Command } from 'commander';
3
+ import { goalActiveTimeSeconds, goalElapsedWallClockSeconds } from './accounting.js';
4
+ import { createDefaultPiboLoopStore } from './store.js';
5
+ import { createBuiltInLoopStopConditions } from './stopping.js';
6
+ import { DEFAULT_PIBO_PROFILE_NAME } from '../plugins/builtin.js';
7
+ import { getLoopJobTemplate, listLoopJobTemplates } from './templates.js';
8
+ import { parsePiboThinkingLevel } from '../core/thinking.js';
9
+ function printDiscovery(commandName = 'pibo loop', legacyRalph = false) {
10
+ console.log(`${commandName}
11
+
12
+ ${legacyRalph ? 'Legacy alias for Ralph-mode Pibo loops.' : 'Manage continuous Pibo agent loops. Goal mode is the default.'}
13
+
14
+ Commands:
15
+ status Show Loop status
16
+ list List Loop jobs
17
+ add Create a Loop job
18
+ edit Update a Loop job
19
+ conditions List registered built-in stop conditions
20
+ templates List built-in Loop job templates
21
+ policy Show, set, or clear a job stop policy
22
+ start Start a Loop job now
23
+ stop Stop after the current session finishes
24
+ cancel Abort the current session and stop
25
+ remove Delete a Loop job
26
+ runs List Loop runs
27
+
28
+ Next: ${commandName} add --help`);
29
+ }
30
+ function targetFromOptions(options) { if (options.room)
31
+ return { kind: 'room', roomId: options.room }; if (options.defaultChat)
32
+ return { kind: 'default-chat' }; throw new Error('Choose a target: --room <room-id> or --default-chat'); }
33
+ function maybeTargetFromOptions(options) { if (options.room || options.defaultChat)
34
+ return targetFromOptions(options); return undefined; }
35
+ function printJson(value) { console.log(JSON.stringify(value, null, 2)); }
36
+ function maxIterations(value) { if (value === undefined)
37
+ return undefined; const parsed = Number(value); if (!Number.isInteger(parsed) || parsed < 1)
38
+ throw new Error('--max-iterations must be a positive integer'); return parsed; }
39
+ function tokenBudget(value) { if (value === undefined)
40
+ return undefined; const parsed = Number(value); if (!Number.isInteger(parsed) || parsed < 1)
41
+ throw new Error('--token-budget must be a positive integer'); return parsed; }
42
+ function tokenReserve(value) { if (value === undefined)
43
+ return undefined; const parsed = Number(value); if (!Number.isInteger(parsed) || parsed < 0)
44
+ throw new Error('--token-reserve must be a non-negative integer'); return parsed; }
45
+ function loopMode(value) { if (value === undefined)
46
+ return undefined; if (value !== 'goal' && value !== 'ralph')
47
+ throw new Error('--mode must be goal or ralph'); return value; }
48
+ function parseModelOverride(value) {
49
+ if (value === undefined)
50
+ return undefined;
51
+ const slash = value.indexOf('/');
52
+ if (slash <= 0 || slash === value.length - 1)
53
+ throw new Error('--model must use provider/model syntax, for example openai/gpt-5');
54
+ const provider = value.slice(0, slash).trim();
55
+ const id = value.slice(slash + 1).trim();
56
+ if (!provider || !id)
57
+ throw new Error('--model must use provider/model syntax, for example openai/gpt-5');
58
+ return { provider, id };
59
+ }
60
+ function parseThinkingOverride(value) { return value === undefined ? undefined : parsePiboThinkingLevel(value); }
61
+ function applyRuntimeCreateOptions(input, options) {
62
+ const modelOverride = parseModelOverride(options.model);
63
+ const thinkingLevel = parseThinkingOverride(options.thinking);
64
+ if (modelOverride)
65
+ input.modelOverride = modelOverride;
66
+ if (thinkingLevel)
67
+ input.thinkingLevel = thinkingLevel;
68
+ if (options.fast !== undefined)
69
+ input.fastMode = options.fast;
70
+ }
71
+ function applyRuntimePatchOptions(patch, options) {
72
+ if (options.model !== undefined && options.clearModel)
73
+ throw new Error('Choose either --model or --clear-model, not both');
74
+ if (options.thinking !== undefined && options.clearThinking)
75
+ throw new Error('Choose either --thinking or --clear-thinking, not both');
76
+ if (options.fast !== undefined && options.clearFast)
77
+ throw new Error('Choose either --fast/--no-fast or --clear-fast, not both');
78
+ if (options.clearModel)
79
+ patch.modelOverride = null;
80
+ else if (options.model !== undefined)
81
+ patch.modelOverride = parseModelOverride(options.model);
82
+ if (options.clearThinking)
83
+ patch.thinkingLevel = null;
84
+ else if (options.thinking !== undefined)
85
+ patch.thinkingLevel = parseThinkingOverride(options.thinking);
86
+ if (options.clearFast)
87
+ patch.fastMode = null;
88
+ else if (options.fast !== undefined)
89
+ patch.fastMode = options.fast;
90
+ }
91
+ function templatePatch(id) {
92
+ if (!id)
93
+ return {};
94
+ const template = getLoopJobTemplate(id);
95
+ if (!template)
96
+ throw new Error(`Unknown Loop template: ${id}`);
97
+ return { mode: template.job.mode, name: template.job.name, description: template.job.description, prompt: template.job.prompt, maxIterations: template.job.maxIterations ?? null, stopPolicy: template.job.stopPolicy ?? null };
98
+ }
99
+ function compactResourceText(value, max = 80) { return value.length <= max ? value : `${value.slice(0, max - 1)}…`; }
100
+ function shellToken(value) { return /^[A-Za-z0-9._:@/-]+$/.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`; }
101
+ export function formatLoopResourceSummary(resources) {
102
+ if (!resources)
103
+ return '-';
104
+ const parts = [
105
+ resources.workerId ? `worker=${resources.workerId}` : undefined,
106
+ resources.cleanupState ? `state=${resources.cleanupState}` : undefined,
107
+ resources.browserLeaseIds?.length ? `leases=${resources.browserLeaseIds.length}` : undefined,
108
+ resources.retainedUntil ? `retainedUntil=${resources.retainedUntil}` : undefined,
109
+ resources.dirtyReason ? `dirty=${compactResourceText(resources.dirtyReason)}` : undefined,
110
+ ].filter((part) => !!part);
111
+ if (resources.cleanupState === 'dirty')
112
+ parts.push(`next=${resources.workerId ? `pibo tools browser-use pool reap --worker-id ${shellToken(resources.workerId)} --json` : 'pibo compute reap --dry-run'}`);
113
+ else if (resources.cleanupState === 'retained')
114
+ parts.push('next=pibo compute reap --dry-run --include-dev');
115
+ return parts.length ? parts.join(';') : '-';
116
+ }
117
+ function formatLoopJobLine(job) {
118
+ const goal = job.mode === 'goal' ? job.state.goalStatus ?? (job.enabled ? 'active' : 'paused') : '-';
119
+ const budget = job.mode === 'goal' ? job.tokenBudget === undefined ? 'unbounded' : `soft:${job.state.tokensUsed ?? 0}/${job.tokenBudget};reserve=${job.tokenReserve ?? 0}` : '-';
120
+ const time = job.mode === 'goal' ? `activeAgent=${goalActiveTimeSeconds(job)}s;elapsedWall=${goalElapsedWallClockSeconds(job)}s;paused=included` : '-';
121
+ return `${job.id}\t${job.mode}\t${job.enabled ? 'running' : 'stopped'}\t${job.state.runningAt ? 'active' : '-'}\tgoal=${goal}\tbudget=${budget}\ttime=${time}\tresources=${formatLoopResourceSummary(job.resources)}\t${job.name}`;
122
+ }
123
+ function formatLoopRunLine(run) {
124
+ const accounting = run.accounting ? `tokens=${run.accounting.tokensUsed ?? 0};remainingBefore=${run.accounting.remainingTokensBefore ?? 'unbounded'};overshoot=${run.accounting.overshootTokens ?? 0};activeAgent=${run.accounting.activeTimeSeconds ?? 0}s` : '-';
125
+ return `${run.id}\t${run.jobId}\t${run.status}\t${run.piboSessionId ?? '-'}\t${run.completedAt ?? '-'}\taccounting=${accounting}\tresources=${formatLoopResourceSummary(run.resources)}`;
126
+ }
127
+ export async function runLoopCli(argv = process.argv, defaults = {}) {
128
+ const program = new Command();
129
+ program.name(defaults.commandName ?? 'pibo loop').description('Manage continuous Pibo loops').helpOption('-h, --help');
130
+ program.option('--store <path>', 'Loop store path');
131
+ program.command('status').description('Show Loop store status').option('--json', 'Print JSON').action((options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const status = store.status(); if (options.json)
132
+ printJson(status);
133
+ else {
134
+ console.log(`jobs\t${status.jobs}`);
135
+ console.log(`running\t${status.running}`);
136
+ } store.close(); });
137
+ program.command('list').description('List Loop jobs').option('--all', 'Include stopped jobs').option('--json', 'Print JSON').action((options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const jobs = store.listJobs({ includeDisabled: options.all }); if (options.json)
138
+ printJson(jobs);
139
+ else
140
+ for (const job of jobs)
141
+ console.log(formatLoopJobLine(job)); store.close(); });
142
+ program.command('add').description(defaults.mode === 'ralph' ? 'Create a Ralph job' : 'Create a Loop job').option('--template <id>', 'Built-in job template id').option('--mode <mode>', 'Loop mode: goal or ralph').option('--prompt <text>', 'Task prompt').option('--name <name>', 'Job name').option('--description <text>', 'Job description').option('--profile <profile>', 'Agent profile', DEFAULT_PIBO_PROFILE_NAME).option('--room <room-id>', 'Target room id').option('--default-chat', 'Target the shared default chat').option('--max-iterations <n>', 'Stop after n completed run attempts').option('--token-budget <n>', 'Set a soft Goal token budget; the final turn can overshoot').option('--token-reserve <n>', 'Require more than n tokens to remain before starting another Goal turn').option('--model <provider/model>', 'Runtime model override, for example openai/gpt-5').option('--thinking <level>', 'Runtime thinking level override: off, minimal, low, medium, high, xhigh, max').option('--fast', 'Enable runtime fast mode').option('--no-fast', 'Disable runtime fast mode')
143
+ .option('--start', 'Start immediately').option('--json', 'Print JSON').action((options) => { const base = templatePatch(options.template); const prompt = options.prompt ?? base.prompt; if (typeof prompt !== 'string' || !prompt.trim())
144
+ throw new Error('Choose --template <id> or provide --prompt <text>'); const input = { mode: loopMode(options.mode) ?? base.mode ?? defaults.mode ?? 'goal', name: options.name ?? base.name, description: options.description ?? base.description, enabled: options.start === true, target: targetFromOptions(options), profile: options.profile, prompt, maxIterations: options.maxIterations !== undefined ? maxIterations(options.maxIterations) : typeof base.maxIterations === 'number' ? base.maxIterations : undefined, tokenBudget: tokenBudget(options.tokenBudget), tokenReserve: tokenReserve(options.tokenReserve), stopPolicy: base.stopPolicy ?? undefined }; applyRuntimeCreateOptions(input, options); const store = createDefaultPiboLoopStore({ path: program.opts().store }); const job = store.createJob(input); if (options.json)
145
+ printJson(job);
146
+ else
147
+ console.log(`${job.id}\t${job.enabled ? 'running' : 'stopped'}\t${job.name}`); store.close(); });
148
+ program.command('edit').argument('<id>', defaults.mode === 'ralph' ? 'Ralph job id' : 'Loop job id').description(defaults.mode === 'ralph' ? 'Update a Ralph job' : 'Update a Loop job').option('--template <id>', 'Apply a built-in job template before explicit overrides').option('--mode <mode>', 'Set loop mode: goal or ralph').option('--prompt <text>', 'Task prompt').option('--name <name>', 'Job name').option('--description <text>', 'Job description').option('--profile <profile>', 'Agent profile').option('--room <room-id>', 'Target room id').option('--default-chat', 'Target the shared default chat').option('--max-iterations <n>', 'Stop after n completed run attempts').option('--token-budget <n>', 'Set soft Goal token budget').option('--clear-token-budget', 'Clear Goal token budget').option('--token-reserve <n>', 'Set pre-turn minimum remaining tokens').option('--clear-token-reserve', 'Clear Goal token reserve').option('--model <provider/model>', 'Set runtime model override, for example openai/gpt-5').option('--clear-model', 'Clear runtime model override').option('--thinking <level>', 'Set runtime thinking level override: off, minimal, low, medium, high, xhigh, max').option('--clear-thinking', 'Clear runtime thinking level override').option('--fast', 'Enable runtime fast mode').option('--no-fast', 'Disable runtime fast mode').option('--clear-fast', 'Clear runtime fast mode override').option('--json', 'Print JSON').action((id, options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const patch = { ...templatePatch(options.template) }; if (options.mode !== undefined)
149
+ patch.mode = loopMode(options.mode); if (options.name !== undefined)
150
+ patch.name = options.name; if (options.description !== undefined)
151
+ patch.description = options.description; if (options.profile !== undefined)
152
+ patch.profile = options.profile; if (options.prompt !== undefined)
153
+ patch.prompt = options.prompt; if (options.maxIterations !== undefined)
154
+ patch.maxIterations = maxIterations(options.maxIterations); if (options.tokenBudget !== undefined && options.clearTokenBudget)
155
+ throw new Error('Choose either --token-budget or --clear-token-budget, not both'); if (options.clearTokenBudget)
156
+ patch.tokenBudget = null;
157
+ else if (options.tokenBudget !== undefined)
158
+ patch.tokenBudget = tokenBudget(options.tokenBudget); if (options.tokenReserve !== undefined && options.clearTokenReserve)
159
+ throw new Error('Choose either --token-reserve or --clear-token-reserve, not both'); if (options.clearTokenReserve)
160
+ patch.tokenReserve = null;
161
+ else if (options.tokenReserve !== undefined)
162
+ patch.tokenReserve = tokenReserve(options.tokenReserve); applyRuntimePatchOptions(patch, options); const target = maybeTargetFromOptions(options); if (target)
163
+ patch.target = target; if (Object.keys(patch).length === 0)
164
+ throw new Error('No Loop job update fields provided'); const job = store.updateJob(id, patch); if (!job)
165
+ throw new Error('Loop job not found'); if (options.json)
166
+ printJson(job);
167
+ else
168
+ console.log(`${job.id}\tupdated\t${job.name}`); store.close(); });
169
+ program.command('conditions').description('List built-in Loop stop-condition types').option('--json', 'Print JSON').action((options) => { const conditions = createBuiltInLoopStopConditions().map((condition) => ({ type: condition.type, name: condition.name, description: condition.description, phases: condition.phases, defaultOptions: condition.defaultOptions, optionsSchema: condition.optionsSchema })); if (options.json)
170
+ printJson(conditions);
171
+ else
172
+ for (const condition of conditions)
173
+ console.log(`${condition.type} ${condition.phases.join(',')} ${condition.name}`); });
174
+ program.command('templates').description('List built-in Loop job templates').option('--json', 'Print JSON').action((options) => { const templates = listLoopJobTemplates(); if (options.json)
175
+ printJson(templates);
176
+ else
177
+ for (const template of templates)
178
+ console.log(`${template.id}\t${template.category}\t${template.name}\t${template.description}`); });
179
+ const policy = program.command('policy').description('Manage a Loop job stop policy');
180
+ policy.command('show').argument('<id>').option('--json', 'Print JSON').action((id, options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const job = store.getJob(id); if (!job)
181
+ throw new Error('Loop job not found'); const value = job.stopPolicy ?? null; if (options.json)
182
+ printJson(value);
183
+ else
184
+ console.log(value ? JSON.stringify(value, null, 2) : 'default'); store.close(); });
185
+ policy.command('set').argument('<id>').requiredOption('--file <path>', 'JSON stop policy file').option('--json', 'Print JSON').action((id, options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const stopPolicy = JSON.parse(readFileSync(options.file, 'utf8')); const job = store.updateJob(id, { stopPolicy }); if (!job)
186
+ throw new Error('Loop job not found'); if (options.json)
187
+ printJson(job);
188
+ else
189
+ console.log(`${job.id} policy-updated ${job.name}`); store.close(); });
190
+ policy.command('clear').argument('<id>').option('--json', 'Print JSON').action((id, options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const job = store.updateJob(id, { stopPolicy: null }); if (!job)
191
+ throw new Error('Loop job not found'); if (options.json)
192
+ printJson(job);
193
+ else
194
+ console.log(`${job.id} policy-cleared ${job.name}`); store.close(); });
195
+ program.command('start').argument('<id>').description('Mark a Loop job running; the gateway service starts the next session').option('--json', 'Print JSON').action((id, options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const job = store.updateJob(id, { enabled: true }); if (!job)
196
+ throw new Error('Loop job not found'); if (options.json)
197
+ printJson(job);
198
+ else
199
+ console.log(`${job.id}\trunning\t${job.name}`); store.close(); });
200
+ program.command('stop').argument('<id>').description('Stop after the current session finishes').option('--json', 'Print JSON').action((id, options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const job = store.requestStop(id); if (!job)
201
+ throw new Error('Loop job not found'); if (options.json)
202
+ printJson(job);
203
+ else
204
+ console.log(`${job.id}\tstopping\t${job.name}`); store.close(); });
205
+ program.command('cancel').argument('<id>').description('Abort the current session and stop').option('--json', 'Print JSON').action((id, options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const job = store.requestCancel(id); if (!job)
206
+ throw new Error('Loop job not found'); if (options.json)
207
+ printJson(job);
208
+ else
209
+ console.log(`${job.id}\tcancel-requested\t${job.name}`); store.close(); });
210
+ program.command('remove').argument('<id>').description('Delete a Loop job').option('--json', 'Print JSON').action((id, options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const removed = store.removeJob(id); if (options.json)
211
+ printJson({ removed });
212
+ else
213
+ console.log(removed ? 'removed' : 'not found'); store.close(); });
214
+ program.command('runs').description('List Loop runs').option('--job <id>', 'Filter by job').option('--json', 'Print JSON').action((options) => { const store = createDefaultPiboLoopStore({ path: program.opts().store }); const runs = store.listRuns({ jobId: options.job }); if (options.json)
215
+ printJson(runs);
216
+ else
217
+ for (const run of runs)
218
+ console.log(formatLoopRunLine(run)); store.close(); });
219
+ if (argv.length <= 2 || (argv.length === 3 && (argv[2] === '--help' || argv[2] === '-h'))) {
220
+ printDiscovery(defaults.commandName, defaults.mode === 'ralph');
221
+ return;
222
+ }
223
+ await program.parseAsync(argv);
224
+ }
@@ -0,0 +1,16 @@
1
+ import { definePiboPlugin } from '../plugins/registry.js';
2
+ import { createPiboLoopChannel } from './channel.js';
3
+ import { createBuiltInLoopStopConditions } from './stopping.js';
4
+ import { configurePiboGoalToolStorePath } from './tools.js';
5
+ export function createPiboLoopPlugin(options = {}) {
6
+ configurePiboGoalToolStorePath(options.loopStorePath);
7
+ return definePiboPlugin({
8
+ id: 'pibo.loop',
9
+ name: 'Pibo Loop',
10
+ register(api) {
11
+ for (const condition of createBuiltInLoopStopConditions())
12
+ api.registerLoopStopCondition(condition);
13
+ api.registerChannel(createPiboLoopChannel(options));
14
+ },
15
+ });
16
+ }