@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
@@ -30,6 +30,11 @@ function migratePiboRunControllerColumn(db) {
30
30
  db.exec("DROP INDEX IF EXISTS idx_pibo_runs_" + ["o", "wner_updated"].join(""));
31
31
  db.exec(`ALTER TABLE pibo_runs RENAME COLUMN ${legacyColumn} TO controller_pibo_session_id`);
32
32
  }
33
+ function ensurePiboRunColumn(db, name, definition) {
34
+ const columns = sqliteTableColumns(db, "pibo_runs");
35
+ if (!columns.has(name))
36
+ db.exec(`ALTER TABLE pibo_runs ADD COLUMN ${name} ${definition}`);
37
+ }
33
38
  export class PiboReliabilityStore {
34
39
  db;
35
40
  appendEventStatement;
@@ -46,105 +51,113 @@ export class PiboReliabilityStore {
46
51
  if (resolvedPath !== ":memory:")
47
52
  this.db.exec("PRAGMA journal_mode = WAL");
48
53
  migratePiboRunControllerColumn(this.db);
49
- this.db.exec(`
50
- CREATE TABLE IF NOT EXISTS pibo_event_stream (
51
- stream_id INTEGER PRIMARY KEY,
52
- topic TEXT NOT NULL,
53
- key TEXT,
54
- event_id TEXT NOT NULL,
55
- idempotency_key TEXT,
56
- created_at TEXT NOT NULL,
57
- retention_class TEXT NOT NULL,
58
- payload_json TEXT NOT NULL
59
- );
60
- CREATE UNIQUE INDEX IF NOT EXISTS idx_pibo_event_stream_event
61
- ON pibo_event_stream(topic, event_id);
62
- CREATE UNIQUE INDEX IF NOT EXISTS idx_pibo_event_stream_idempotency
63
- ON pibo_event_stream(topic, idempotency_key)
64
- WHERE idempotency_key IS NOT NULL;
65
- CREATE INDEX IF NOT EXISTS idx_pibo_event_stream_topic_stream
66
- ON pibo_event_stream(topic, stream_id);
67
-
68
- CREATE TABLE IF NOT EXISTS pibo_event_consumers (
69
- consumer TEXT NOT NULL,
70
- topic TEXT NOT NULL,
71
- last_stream_id INTEGER NOT NULL,
72
- updated_at TEXT NOT NULL,
73
- PRIMARY KEY(consumer, topic)
74
- );
75
-
76
- CREATE TABLE IF NOT EXISTS pibo_jobs (
77
- job_id TEXT PRIMARY KEY,
78
- queue TEXT NOT NULL,
79
- state TEXT NOT NULL,
80
- payload_json TEXT NOT NULL,
81
- run_at TEXT NOT NULL,
82
- priority INTEGER NOT NULL DEFAULT 0,
83
- worker_id TEXT,
84
- claim_expires_at TEXT,
85
- attempts INTEGER NOT NULL DEFAULT 0,
86
- max_attempts INTEGER NOT NULL DEFAULT 1,
87
- idempotency_key TEXT,
88
- created_at TEXT NOT NULL,
89
- updated_at TEXT NOT NULL,
90
- expires_at TEXT,
91
- last_error TEXT
92
- );
93
- CREATE UNIQUE INDEX IF NOT EXISTS idx_pibo_jobs_idempotency
94
- ON pibo_jobs(queue, idempotency_key)
95
- WHERE idempotency_key IS NOT NULL;
96
- CREATE INDEX IF NOT EXISTS idx_pibo_jobs_live_claim
97
- ON pibo_jobs(queue, state, run_at, priority DESC, created_at)
98
- WHERE state = 'pending';
99
- CREATE INDEX IF NOT EXISTS idx_pibo_jobs_expired_claim
100
- ON pibo_jobs(queue, state, claim_expires_at, priority DESC, created_at)
101
- WHERE state = 'running';
102
-
103
- CREATE TABLE IF NOT EXISTS pibo_dead_jobs (
104
- job_id TEXT PRIMARY KEY,
105
- queue TEXT NOT NULL,
106
- payload_json TEXT NOT NULL,
107
- attempts INTEGER NOT NULL,
108
- max_attempts INTEGER NOT NULL,
109
- idempotency_key TEXT,
110
- created_at TEXT NOT NULL,
111
- updated_at TEXT NOT NULL,
112
- expires_at TEXT,
113
- last_error TEXT,
114
- dead_at TEXT NOT NULL,
115
- dead_reason TEXT NOT NULL
116
- );
117
- CREATE INDEX IF NOT EXISTS idx_pibo_dead_jobs_queue_dead_at
118
- ON pibo_dead_jobs(queue, dead_at);
119
-
120
- CREATE TABLE IF NOT EXISTS pibo_runs (
121
- run_id TEXT PRIMARY KEY,
122
- kind TEXT NOT NULL,
123
- controller_pibo_session_id TEXT NOT NULL,
124
- status TEXT NOT NULL,
125
- completion_policy TEXT NOT NULL,
126
- consumed INTEGER NOT NULL DEFAULT 0,
127
- tool_name TEXT NOT NULL,
128
- summary TEXT,
129
- result_json TEXT,
130
- error TEXT,
131
- notified_status TEXT,
132
- acknowledged_status TEXT,
133
- created_at TEXT NOT NULL,
134
- updated_at TEXT NOT NULL,
135
- completed_at TEXT,
136
- job_id TEXT,
137
- retryable INTEGER NOT NULL DEFAULT 0,
138
- max_attempts INTEGER NOT NULL DEFAULT 1
139
- );
140
- CREATE INDEX IF NOT EXISTS idx_pibo_runs_controller_updated
141
- ON pibo_runs(controller_pibo_session_id, updated_at);
142
- CREATE INDEX IF NOT EXISTS idx_pibo_runs_status
143
- ON pibo_runs(status);
54
+ this.db.exec(`
55
+ CREATE TABLE IF NOT EXISTS pibo_event_stream (
56
+ stream_id INTEGER PRIMARY KEY,
57
+ topic TEXT NOT NULL,
58
+ key TEXT,
59
+ event_id TEXT NOT NULL,
60
+ idempotency_key TEXT,
61
+ created_at TEXT NOT NULL,
62
+ retention_class TEXT NOT NULL,
63
+ payload_json TEXT NOT NULL
64
+ );
65
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_pibo_event_stream_event
66
+ ON pibo_event_stream(topic, event_id);
67
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_pibo_event_stream_idempotency
68
+ ON pibo_event_stream(topic, idempotency_key)
69
+ WHERE idempotency_key IS NOT NULL;
70
+ CREATE INDEX IF NOT EXISTS idx_pibo_event_stream_topic_stream
71
+ ON pibo_event_stream(topic, stream_id);
72
+
73
+ CREATE TABLE IF NOT EXISTS pibo_event_consumers (
74
+ consumer TEXT NOT NULL,
75
+ topic TEXT NOT NULL,
76
+ last_stream_id INTEGER NOT NULL,
77
+ updated_at TEXT NOT NULL,
78
+ PRIMARY KEY(consumer, topic)
79
+ );
80
+
81
+ CREATE TABLE IF NOT EXISTS pibo_jobs (
82
+ job_id TEXT PRIMARY KEY,
83
+ queue TEXT NOT NULL,
84
+ state TEXT NOT NULL,
85
+ payload_json TEXT NOT NULL,
86
+ run_at TEXT NOT NULL,
87
+ priority INTEGER NOT NULL DEFAULT 0,
88
+ worker_id TEXT,
89
+ claim_expires_at TEXT,
90
+ attempts INTEGER NOT NULL DEFAULT 0,
91
+ max_attempts INTEGER NOT NULL DEFAULT 1,
92
+ idempotency_key TEXT,
93
+ created_at TEXT NOT NULL,
94
+ updated_at TEXT NOT NULL,
95
+ expires_at TEXT,
96
+ last_error TEXT
97
+ );
98
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_pibo_jobs_idempotency
99
+ ON pibo_jobs(queue, idempotency_key)
100
+ WHERE idempotency_key IS NOT NULL;
101
+ CREATE INDEX IF NOT EXISTS idx_pibo_jobs_live_claim
102
+ ON pibo_jobs(queue, state, run_at, priority DESC, created_at)
103
+ WHERE state = 'pending';
104
+ CREATE INDEX IF NOT EXISTS idx_pibo_jobs_expired_claim
105
+ ON pibo_jobs(queue, state, claim_expires_at, priority DESC, created_at)
106
+ WHERE state = 'running';
107
+
108
+ CREATE TABLE IF NOT EXISTS pibo_dead_jobs (
109
+ job_id TEXT PRIMARY KEY,
110
+ queue TEXT NOT NULL,
111
+ payload_json TEXT NOT NULL,
112
+ attempts INTEGER NOT NULL,
113
+ max_attempts INTEGER NOT NULL,
114
+ idempotency_key TEXT,
115
+ created_at TEXT NOT NULL,
116
+ updated_at TEXT NOT NULL,
117
+ expires_at TEXT,
118
+ last_error TEXT,
119
+ dead_at TEXT NOT NULL,
120
+ dead_reason TEXT NOT NULL
121
+ );
122
+ CREATE INDEX IF NOT EXISTS idx_pibo_dead_jobs_queue_dead_at
123
+ ON pibo_dead_jobs(queue, dead_at);
124
+
125
+ CREATE TABLE IF NOT EXISTS pibo_runs (
126
+ run_id TEXT PRIMARY KEY,
127
+ kind TEXT NOT NULL,
128
+ controller_pibo_session_id TEXT NOT NULL,
129
+ status TEXT NOT NULL,
130
+ completion_policy TEXT NOT NULL,
131
+ consumed INTEGER NOT NULL DEFAULT 0,
132
+ tool_name TEXT NOT NULL,
133
+ summary TEXT,
134
+ result_json TEXT,
135
+ error TEXT,
136
+ notified_status TEXT,
137
+ acknowledged_status TEXT,
138
+ created_at TEXT NOT NULL,
139
+ updated_at TEXT NOT NULL,
140
+ completed_at TEXT,
141
+ job_id TEXT,
142
+ retryable INTEGER NOT NULL DEFAULT 0,
143
+ max_attempts INTEGER NOT NULL DEFAULT 1,
144
+ timeout_ms INTEGER,
145
+ timeout_at TEXT,
146
+ timeout_phase TEXT,
147
+ service_warning TEXT
148
+ );
149
+ CREATE INDEX IF NOT EXISTS idx_pibo_runs_controller_updated
150
+ ON pibo_runs(controller_pibo_session_id, updated_at);
151
+ CREATE INDEX IF NOT EXISTS idx_pibo_runs_status
152
+ ON pibo_runs(status);
144
153
  `);
145
- this.appendEventStatement = this.db.prepare(`
146
- INSERT INTO pibo_event_stream (topic, key, event_id, idempotency_key, created_at, retention_class, payload_json)
147
- VALUES (?, ?, ?, ?, ?, ?, ?)
154
+ ensurePiboRunColumn(this.db, "timeout_ms", "INTEGER");
155
+ ensurePiboRunColumn(this.db, "timeout_at", "TEXT");
156
+ ensurePiboRunColumn(this.db, "timeout_phase", "TEXT");
157
+ ensurePiboRunColumn(this.db, "service_warning", "TEXT");
158
+ this.appendEventStatement = this.db.prepare(`
159
+ INSERT INTO pibo_event_stream (topic, key, event_id, idempotency_key, created_at, retention_class, payload_json)
160
+ VALUES (?, ?, ?, ?, ?, ?, ?)
148
161
  `);
149
162
  this.getEventByStreamIdStatement = this.db.prepare("SELECT * FROM pibo_event_stream WHERE stream_id = ?");
150
163
  this.getEventByTopicEventIdStatement = this.db.prepare("SELECT * FROM pibo_event_stream WHERE topic = ? AND event_id = ?");
@@ -202,12 +215,12 @@ export class PiboReliabilityStore {
202
215
  }
203
216
  const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
204
217
  const rows = this.db
205
- .prepare(`
206
- SELECT topic, key, retention_class, COUNT(*) AS count
207
- FROM pibo_event_stream
208
- ${where}
209
- GROUP BY topic, key, retention_class
210
- ORDER BY topic, key, retention_class
218
+ .prepare(`
219
+ SELECT topic, key, retention_class, COUNT(*) AS count
220
+ FROM pibo_event_stream
221
+ ${where}
222
+ GROUP BY topic, key, retention_class
223
+ ORDER BY topic, key, retention_class
211
224
  `)
212
225
  .all(...values);
213
226
  return rows.map((row) => ({
@@ -223,12 +236,12 @@ export class PiboReliabilityStore {
223
236
  }
224
237
  saveConsumerOffset(topic, consumer, streamId) {
225
238
  this.db
226
- .prepare(`
227
- INSERT INTO pibo_event_consumers (consumer, topic, last_stream_id, updated_at)
228
- VALUES (?, ?, ?, ?)
229
- ON CONFLICT(consumer, topic) DO UPDATE SET
230
- last_stream_id = MAX(pibo_event_consumers.last_stream_id, excluded.last_stream_id),
231
- updated_at = excluded.updated_at
239
+ .prepare(`
240
+ INSERT INTO pibo_event_consumers (consumer, topic, last_stream_id, updated_at)
241
+ VALUES (?, ?, ?, ?)
242
+ ON CONFLICT(consumer, topic) DO UPDATE SET
243
+ last_stream_id = MAX(pibo_event_consumers.last_stream_id, excluded.last_stream_id),
244
+ updated_at = excluded.updated_at
232
245
  `)
233
246
  .run(consumer, topic, streamId, now());
234
247
  }
@@ -260,23 +273,23 @@ export class PiboReliabilityStore {
260
273
  values.push(input.beforeStreamId);
261
274
  }
262
275
  if (!input.destructive) {
263
- clauses.push(`
264
- stream_id <= COALESCE(
265
- (SELECT MIN(last_stream_id) FROM pibo_event_consumers WHERE pibo_event_consumers.topic = pibo_event_stream.topic),
266
- stream_id
267
- )
276
+ clauses.push(`
277
+ stream_id <= COALESCE(
278
+ (SELECT MIN(last_stream_id) FROM pibo_event_consumers WHERE pibo_event_consumers.topic = pibo_event_stream.topic),
279
+ stream_id
280
+ )
268
281
  `);
269
282
  }
270
283
  const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
271
284
  const result = this.db
272
- .prepare(`
273
- DELETE FROM pibo_event_stream
274
- WHERE stream_id IN (
275
- SELECT stream_id FROM pibo_event_stream
276
- ${where}
277
- ORDER BY stream_id ASC
278
- LIMIT ${clampLimit(input.limit, 500)}
279
- )
285
+ .prepare(`
286
+ DELETE FROM pibo_event_stream
287
+ WHERE stream_id IN (
288
+ SELECT stream_id FROM pibo_event_stream
289
+ ${where}
290
+ ORDER BY stream_id ASC
291
+ LIMIT ${clampLimit(input.limit, 500)}
292
+ )
280
293
  `)
281
294
  .run(...values);
282
295
  return Number(result.changes ?? 0);
@@ -285,11 +298,11 @@ export class PiboReliabilityStore {
285
298
  const timestamp = now();
286
299
  const jobId = input.jobId ?? `job_${randomUUID()}`;
287
300
  const result = this.db
288
- .prepare(`
289
- INSERT OR IGNORE INTO pibo_jobs (
290
- job_id, queue, state, payload_json, run_at, priority, worker_id, claim_expires_at,
291
- attempts, max_attempts, idempotency_key, created_at, updated_at, expires_at, last_error
292
- ) VALUES (?, ?, 'pending', ?, ?, ?, NULL, NULL, 0, ?, ?, ?, ?, ?, NULL)
301
+ .prepare(`
302
+ INSERT OR IGNORE INTO pibo_jobs (
303
+ job_id, queue, state, payload_json, run_at, priority, worker_id, claim_expires_at,
304
+ attempts, max_attempts, idempotency_key, created_at, updated_at, expires_at, last_error
305
+ ) VALUES (?, ?, 'pending', ?, ?, ?, NULL, NULL, 0, ?, ?, ?, ?, ?, NULL)
293
306
  `)
294
307
  .run(jobId, input.queue, json(input.payload), input.runAt ?? timestamp, input.priority ?? 0, Math.max(1, input.maxAttempts ?? 1), input.idempotencyKey ?? null, timestamp, timestamp, input.expiresAt ?? null);
295
308
  if (Number(result.changes ?? 0) === 0 && input.idempotencyKey) {
@@ -313,20 +326,20 @@ export class PiboReliabilityStore {
313
326
  }
314
327
  this.moveExpiredJobs(timestamp);
315
328
  const rows = this.db
316
- .prepare(`
317
- UPDATE pibo_jobs
318
- SET state = 'running',
319
- worker_id = ?,
320
- claim_expires_at = ?,
321
- attempts = attempts + 1,
322
- updated_at = ?
323
- WHERE job_id IN (
324
- SELECT job_id FROM pibo_jobs
325
- WHERE ${clauses.join(" AND ")}
326
- ORDER BY priority DESC, run_at ASC, created_at ASC
327
- LIMIT ?
328
- )
329
- RETURNING *
329
+ .prepare(`
330
+ UPDATE pibo_jobs
331
+ SET state = 'running',
332
+ worker_id = ?,
333
+ claim_expires_at = ?,
334
+ attempts = attempts + 1,
335
+ updated_at = ?
336
+ WHERE job_id IN (
337
+ SELECT job_id FROM pibo_jobs
338
+ WHERE ${clauses.join(" AND ")}
339
+ ORDER BY priority DESC, run_at ASC, created_at ASC
340
+ LIMIT ?
341
+ )
342
+ RETURNING *
330
343
  `)
331
344
  .all(workerId, claimExpiresAt, timestamp, ...values, clampLimit(limit));
332
345
  return rows.map(jobFromRow);
@@ -335,18 +348,18 @@ export class PiboReliabilityStore {
335
348
  const timestamp = now();
336
349
  const claimExpiresAt = new Date(Date.now() + visibilityTimeoutMs).toISOString();
337
350
  const row = this.db
338
- .prepare(`
339
- UPDATE pibo_jobs
340
- SET state = 'running',
341
- worker_id = ?,
342
- claim_expires_at = ?,
343
- attempts = attempts + 1,
344
- updated_at = ?
345
- WHERE job_id = ?
346
- AND state = 'pending'
347
- AND run_at <= ?
348
- AND (expires_at IS NULL OR expires_at > ?)
349
- RETURNING *
351
+ .prepare(`
352
+ UPDATE pibo_jobs
353
+ SET state = 'running',
354
+ worker_id = ?,
355
+ claim_expires_at = ?,
356
+ attempts = attempts + 1,
357
+ updated_at = ?
358
+ WHERE job_id = ?
359
+ AND state = 'pending'
360
+ AND run_at <= ?
361
+ AND (expires_at IS NULL OR expires_at > ?)
362
+ RETURNING *
350
363
  `)
351
364
  .get(workerId, claimExpiresAt, timestamp, jobId, timestamp, timestamp);
352
365
  return row ? jobFromRow(row) : undefined;
@@ -354,13 +367,13 @@ export class PiboReliabilityStore {
354
367
  ack(jobId, workerId) {
355
368
  const timestamp = now();
356
369
  const result = this.db
357
- .prepare(`
358
- DELETE FROM pibo_jobs
359
- WHERE job_id = ?
360
- AND state = 'running'
361
- AND worker_id = ?
362
- AND claim_expires_at IS NOT NULL
363
- AND claim_expires_at > ?
370
+ .prepare(`
371
+ DELETE FROM pibo_jobs
372
+ WHERE job_id = ?
373
+ AND state = 'running'
374
+ AND worker_id = ?
375
+ AND claim_expires_at IS NOT NULL
376
+ AND claim_expires_at > ?
364
377
  `)
365
378
  .run(jobId, workerId, timestamp);
366
379
  return Number(result.changes ?? 0) > 0;
@@ -378,15 +391,15 @@ export class PiboReliabilityStore {
378
391
  const delayMs = input.delayMs ?? retryDelayMs(job.attempts, input);
379
392
  const runAt = new Date(asDate(input.now).getTime() + delayMs).toISOString();
380
393
  this.db
381
- .prepare(`
382
- UPDATE pibo_jobs
383
- SET state = 'pending',
384
- worker_id = NULL,
385
- claim_expires_at = NULL,
386
- run_at = ?,
387
- updated_at = ?,
388
- last_error = ?
389
- WHERE job_id = ?
394
+ .prepare(`
395
+ UPDATE pibo_jobs
396
+ SET state = 'pending',
397
+ worker_id = NULL,
398
+ claim_expires_at = NULL,
399
+ run_at = ?,
400
+ updated_at = ?,
401
+ last_error = ?
402
+ WHERE job_id = ?
390
403
  `)
391
404
  .run(runAt, timestamp, input.error ?? null, jobId);
392
405
  return true;
@@ -403,14 +416,14 @@ export class PiboReliabilityStore {
403
416
  const timestamp = now();
404
417
  const claimExpiresAt = new Date(Date.now() + Math.max(1, extendMs)).toISOString();
405
418
  const result = this.db
406
- .prepare(`
407
- UPDATE pibo_jobs
408
- SET claim_expires_at = ?, updated_at = ?
409
- WHERE job_id = ?
410
- AND state = 'running'
411
- AND worker_id = ?
412
- AND claim_expires_at IS NOT NULL
413
- AND claim_expires_at > ?
419
+ .prepare(`
420
+ UPDATE pibo_jobs
421
+ SET claim_expires_at = ?, updated_at = ?
422
+ WHERE job_id = ?
423
+ AND state = 'running'
424
+ AND worker_id = ?
425
+ AND claim_expires_at IS NOT NULL
426
+ AND claim_expires_at > ?
414
427
  `)
415
428
  .run(claimExpiresAt, timestamp, jobId, workerId, timestamp);
416
429
  return Number(result.changes ?? 0) > 0;
@@ -466,6 +479,7 @@ export class PiboReliabilityStore {
466
479
  const timestamp = now();
467
480
  const runId = input.runId ?? `run_${randomUUID()}`;
468
481
  const maxAttempts = Math.max(1, input.maxAttempts ?? 1);
482
+ const timeoutAt = input.timeoutMs === undefined ? undefined : new Date(Date.parse(timestamp) + input.timeoutMs).toISOString();
469
483
  const job = this.enqueue({
470
484
  queue: "runs",
471
485
  payload: {
@@ -473,18 +487,19 @@ export class PiboReliabilityStore {
473
487
  controllerPiboSessionId: input.controllerPiboSessionId,
474
488
  toolName: input.toolName,
475
489
  params: input.params,
490
+ timeoutMs: input.timeoutMs,
476
491
  },
477
492
  maxAttempts,
478
493
  });
479
494
  this.db
480
- .prepare(`
481
- INSERT INTO pibo_runs (
482
- run_id, kind, controller_pibo_session_id, status, completion_policy, consumed, tool_name,
483
- summary, result_json, error, notified_status, acknowledged_status, created_at, updated_at,
484
- completed_at, job_id, retryable, max_attempts
485
- ) VALUES (?, 'tool', ?, 'running', ?, 0, ?, ?, NULL, NULL, NULL, NULL, ?, ?, NULL, ?, ?, ?)
495
+ .prepare(`
496
+ INSERT INTO pibo_runs (
497
+ run_id, kind, controller_pibo_session_id, status, completion_policy, consumed, tool_name,
498
+ summary, result_json, error, notified_status, acknowledged_status, created_at, updated_at,
499
+ completed_at, job_id, retryable, max_attempts, timeout_ms, timeout_at, timeout_phase, service_warning
500
+ ) VALUES (?, 'tool', ?, 'running', ?, 0, ?, ?, NULL, NULL, NULL, NULL, ?, ?, NULL, ?, ?, ?, ?, ?, NULL, ?)
486
501
  `)
487
- .run(runId, input.controllerPiboSessionId, input.completionPolicy, input.toolName, `${input.toolName} run is running.`, timestamp, timestamp, job.jobId, input.retryable ? 1 : 0, maxAttempts);
502
+ .run(runId, input.controllerPiboSessionId, input.completionPolicy, input.toolName, `${input.toolName} run is running.`, timestamp, timestamp, job.jobId, input.retryable ? 1 : 0, maxAttempts, input.timeoutMs ?? null, timeoutAt ?? null, input.serviceWarning ?? null);
488
503
  this.claimJob(job.jobId, `run-registry:${process.pid}`, 24 * 60 * 60 * 1000);
489
504
  return this.requireRun(runId);
490
505
  }
@@ -494,24 +509,28 @@ export class PiboReliabilityStore {
494
509
  return undefined;
495
510
  const next = { ...existing, ...patch, updatedAt: patch.updatedAt ?? now() };
496
511
  this.db
497
- .prepare(`
498
- UPDATE pibo_runs SET
499
- status = ?,
500
- completion_policy = ?,
501
- consumed = ?,
502
- summary = ?,
503
- result_json = ?,
504
- error = ?,
505
- notified_status = ?,
506
- acknowledged_status = ?,
507
- updated_at = ?,
508
- completed_at = ?,
509
- job_id = ?,
510
- retryable = ?,
511
- max_attempts = ?
512
- WHERE run_id = ?
512
+ .prepare(`
513
+ UPDATE pibo_runs SET
514
+ status = ?,
515
+ completion_policy = ?,
516
+ consumed = ?,
517
+ summary = ?,
518
+ result_json = ?,
519
+ error = ?,
520
+ notified_status = ?,
521
+ acknowledged_status = ?,
522
+ updated_at = ?,
523
+ completed_at = ?,
524
+ job_id = ?,
525
+ retryable = ?,
526
+ max_attempts = ?,
527
+ timeout_ms = ?,
528
+ timeout_at = ?,
529
+ timeout_phase = ?,
530
+ service_warning = ?
531
+ WHERE run_id = ?
513
532
  `)
514
- .run(next.status, next.completionPolicy, next.consumed ? 1 : 0, next.summary ?? null, next.result ? JSON.stringify(next.result) : null, next.error ?? null, next.notifiedStatus ?? null, next.acknowledgedStatus ?? null, next.updatedAt, next.completedAt ?? null, next.jobId ?? null, next.retryable ? 1 : 0, next.maxAttempts, runId);
533
+ .run(next.status, next.completionPolicy, next.consumed ? 1 : 0, next.summary ?? null, next.result ? JSON.stringify(next.result) : null, next.error ?? null, next.notifiedStatus ?? null, next.acknowledgedStatus ?? null, next.updatedAt, next.completedAt ?? null, next.jobId ?? null, next.retryable ? 1 : 0, next.maxAttempts, next.timeoutMs ?? null, next.timeoutAt ?? null, next.timeoutPhase ?? null, next.serviceWarning ?? null, runId);
515
534
  return this.requireRun(runId);
516
535
  }
517
536
  getRun(runId) {
@@ -535,7 +554,7 @@ export class PiboReliabilityStore {
535
554
  }
536
555
  pruneRuns(input) {
537
556
  const rows = this.db
538
- .prepare("SELECT * FROM pibo_runs WHERE status IN ('completed', 'failed', 'cancelled') AND completed_at IS NOT NULL")
557
+ .prepare("SELECT * FROM pibo_runs WHERE status IN ('completed', 'failed', 'timed_out', 'cancelled') AND completed_at IS NOT NULL")
539
558
  .all();
540
559
  const ids = rows
541
560
  .map(runFromRow)
@@ -619,13 +638,13 @@ export class PiboReliabilityStore {
619
638
  }
620
639
  getLiveWorkerJob(jobId, workerId, timestamp) {
621
640
  return this.db
622
- .prepare(`
623
- SELECT * FROM pibo_jobs
624
- WHERE job_id = ?
625
- AND state = 'running'
626
- AND worker_id = ?
627
- AND claim_expires_at IS NOT NULL
628
- AND claim_expires_at > ?
641
+ .prepare(`
642
+ SELECT * FROM pibo_jobs
643
+ WHERE job_id = ?
644
+ AND state = 'running'
645
+ AND worker_id = ?
646
+ AND claim_expires_at IS NOT NULL
647
+ AND claim_expires_at > ?
629
648
  `)
630
649
  .get(jobId, workerId, timestamp);
631
650
  }
@@ -636,26 +655,26 @@ export class PiboReliabilityStore {
636
655
  }
637
656
  hasUnexpiredJobClaim(jobId, timestamp) {
638
657
  const row = this.db
639
- .prepare(`
640
- SELECT job_id FROM pibo_jobs
641
- WHERE job_id = ?
642
- AND state = 'running'
643
- AND claim_expires_at IS NOT NULL
644
- AND claim_expires_at > ?
658
+ .prepare(`
659
+ SELECT job_id FROM pibo_jobs
660
+ WHERE job_id = ?
661
+ AND state = 'running'
662
+ AND claim_expires_at IS NOT NULL
663
+ AND claim_expires_at > ?
645
664
  `)
646
665
  .get(jobId, timestamp);
647
666
  return row !== undefined;
648
667
  }
649
668
  releaseJobForRetry(jobId, timestamp) {
650
669
  this.db
651
- .prepare(`
652
- UPDATE pibo_jobs
653
- SET state = 'pending',
654
- worker_id = NULL,
655
- claim_expires_at = NULL,
656
- run_at = ?,
657
- updated_at = ?
658
- WHERE job_id = ?
670
+ .prepare(`
671
+ UPDATE pibo_jobs
672
+ SET state = 'pending',
673
+ worker_id = NULL,
674
+ claim_expires_at = NULL,
675
+ run_at = ?,
676
+ updated_at = ?
677
+ WHERE job_id = ?
659
678
  `)
660
679
  .run(timestamp, timestamp, jobId);
661
680
  }
@@ -666,11 +685,11 @@ export class PiboReliabilityStore {
666
685
  }
667
686
  moveJobToDead(row, error, reason, timestamp) {
668
687
  this.db
669
- .prepare(`
670
- INSERT OR REPLACE INTO pibo_dead_jobs (
671
- job_id, queue, payload_json, attempts, max_attempts, idempotency_key, created_at,
672
- updated_at, expires_at, last_error, dead_at, dead_reason
673
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
688
+ .prepare(`
689
+ INSERT OR REPLACE INTO pibo_dead_jobs (
690
+ job_id, queue, payload_json, attempts, max_attempts, idempotency_key, created_at,
691
+ updated_at, expires_at, last_error, dead_at, dead_reason
692
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
674
693
  `)
675
694
  .run(row.job_id, row.queue, row.payload_json, row.attempts, row.max_attempts, row.idempotency_key, row.created_at, timestamp, row.expires_at, error, timestamp, reason);
676
695
  this.db.prepare("DELETE FROM pibo_jobs WHERE job_id = ?").run(row.job_id);
@@ -760,6 +779,14 @@ function runFromRow(row) {
760
779
  output.completedAt = row.completed_at;
761
780
  if (row.job_id)
762
781
  output.jobId = row.job_id;
782
+ if (row.timeout_ms !== null)
783
+ output.timeoutMs = row.timeout_ms;
784
+ if (row.timeout_at)
785
+ output.timeoutAt = row.timeout_at;
786
+ if (row.timeout_phase === "startup" || row.timeout_phase === "lifetime")
787
+ output.timeoutPhase = row.timeout_phase;
788
+ if (row.service_warning)
789
+ output.serviceWarning = row.service_warning;
763
790
  return output;
764
791
  }
765
792
  function retryDelayMs(attempts, input) {