@mu-crew/mu 3.13.3 → 4.0.0

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 (119) hide show
  1. package/AGENTS.md +73 -35
  2. package/README.md +71 -57
  3. package/dist/chunk-5HECDBK3.js +4482 -0
  4. package/dist/chunk-5HECDBK3.js.map +1 -0
  5. package/dist/chunk-AW2NONYU.js +301 -0
  6. package/dist/chunk-AW2NONYU.js.map +1 -0
  7. package/dist/chunk-GQNHIPJN.js +4060 -0
  8. package/dist/chunk-GQNHIPJN.js.map +1 -0
  9. package/dist/{chunk-3BARPJLY.js → chunk-PZ5GOCWH.js} +16 -2
  10. package/dist/chunk-PZ5GOCWH.js.map +1 -0
  11. package/dist/chunk-S424D5EP.js +13766 -0
  12. package/dist/chunk-S424D5EP.js.map +1 -0
  13. package/dist/{cli-CKOMHBXV.js → cli-GB2CAVVW.js} +6776 -4350
  14. package/dist/cli-GB2CAVVW.js.map +1 -0
  15. package/dist/cli.js +2 -3
  16. package/dist/cli.js.map +1 -1
  17. package/dist/errors-KHOYKD7D.js +65 -0
  18. package/dist/extension/mu-pi.js +1617 -836
  19. package/dist/extension/mu-pi.js.map +1 -1
  20. package/dist/index.d.ts +4015 -2561
  21. package/dist/index.js +614 -297
  22. package/dist/index.js.map +1 -1
  23. package/dist/tui-ALPHYTA2.js +1773 -0
  24. package/dist/tui-ALPHYTA2.js.map +1 -0
  25. package/docs/ARCHITECTURE.md +117 -66
  26. package/docs/VOCABULARY.md +112 -49
  27. package/docs/guide/README.md +3 -3
  28. package/docs/guide/backends.md +8 -4
  29. package/docs/guide/cleanup.md +61 -33
  30. package/docs/guide/delegate.md +141 -66
  31. package/docs/guide/dispatch.md +148 -53
  32. package/docs/guide/getting-started.md +37 -44
  33. package/docs/guide/other-agent-clis.md +102 -0
  34. package/docs/guide/recovery.md +31 -50
  35. package/docs/guide/remote.md +115 -45
  36. package/docs/guide/sql.md +104 -24
  37. package/docs/guide/stop-a-worker.md +30 -37
  38. package/docs/guide/sync.md +11 -3
  39. package/docs/guide/upgrade.md +93 -43
  40. package/docs/reference/env.md +23 -32
  41. package/docs/reference/naming.md +12 -7
  42. package/docs/reference/surface-contract.md +153 -0
  43. package/package.json +33 -11
  44. package/prompts/mu-debug.md +3 -1
  45. package/prompts/mu-refute.md +2 -1
  46. package/prompts/mu-research.md +3 -1
  47. package/prompts/mu-review.md +2 -1
  48. package/prompts/mu-rules-audit.md +5 -4
  49. package/prompts/mu-sweep.md +2 -1
  50. package/prompts/mu-tournament.md +4 -2
  51. package/prompts/mu-until.md +3 -1
  52. package/prompts/ultrathink.md +1 -1
  53. package/skills/mu/SKILL.md +60 -291
  54. package/skills/mu/recipes/adversarial-review.md +12 -103
  55. package/skills/mu/recipes/brief.md +14 -61
  56. package/skills/mu/recipes/course-correct.md +23 -0
  57. package/skills/mu/recipes/findings.md +17 -111
  58. package/skills/mu/recipes/models.md +17 -92
  59. package/skills/mu/recipes/move.md +14 -0
  60. package/skills/mu/recipes/orchestrator-loop.md +20 -119
  61. package/skills/mu/recipes/recovery.md +8 -29
  62. package/skills/mu/recipes/remote-workers.md +16 -318
  63. package/skills/mu/recipes/tasks-or-calls.md +15 -78
  64. package/skills/mu/recipes/test-health.md +19 -77
  65. package/skills/mu/recipes/worker.md +17 -24
  66. package/dist/chunk-45KQLMEL.js +0 -2908
  67. package/dist/chunk-45KQLMEL.js.map +0 -1
  68. package/dist/chunk-G434JKV2.js +0 -641
  69. package/dist/chunk-G434JKV2.js.map +0 -1
  70. package/dist/chunk-KUGUFBWH.js +0 -12527
  71. package/dist/chunk-KUGUFBWH.js.map +0 -1
  72. package/dist/chunk-QITLNZEM.js +0 -87
  73. package/dist/chunk-QITLNZEM.js.map +0 -1
  74. package/dist/chunk-VIEYQMQA.js +0 -393
  75. package/dist/chunk-VIEYQMQA.js.map +0 -1
  76. package/dist/chunk-WI6AWCMK.js +0 -19
  77. package/dist/chunk-WI6AWCMK.js.map +0 -1
  78. package/dist/cli-CKOMHBXV.js.map +0 -1
  79. package/dist/devtools-LS4ZEPWM.js +0 -3772
  80. package/dist/devtools-LS4ZEPWM.js.map +0 -1
  81. package/dist/tui-LFJ5P4Y2.js +0 -40956
  82. package/dist/tui-LFJ5P4Y2.js.map +0 -1
  83. package/dist/tui-sync-worker.js +0 -17
  84. package/dist/tui-sync-worker.js.map +0 -1
  85. package/dist/yank-XDOONQSL.js +0 -13
  86. package/dist/yank-XDOONQSL.js.map +0 -1
  87. package/docs/ROADMAP.md +0 -123
  88. package/docs/VISION.md +0 -298
  89. package/docs/architecture/control-socket.md +0 -142
  90. package/docs/architecture/dag.md +0 -117
  91. package/docs/architecture/mux.md +0 -167
  92. package/docs/architecture/ops-log.md +0 -160
  93. package/docs/architecture/sdk.md +0 -187
  94. package/docs/architecture/sync.md +0 -159
  95. package/docs/architecture/tui.md +0 -165
  96. package/docs/guide/tui.md +0 -98
  97. package/docs/history/CHANGELOG-pre-3.md +0 -4866
  98. package/docs/img/tui-dashboard.png +0 -0
  99. package/docs/specs/2026-09-28-agent-state-from-murmur.md +0 -333
  100. package/docs/specs/2026-09-29-task-substates.md +0 -247
  101. package/docs/specs/2026-10-03-keep-driving-nudge.md +0 -163
  102. package/docs/specs/2026-10-21-fast-and-slow-gates.md +0 -229
  103. package/skills/mu/recipes/backlog-triage.md +0 -45
  104. package/skills/mu/recipes/codemode-driver.md +0 -118
  105. package/skills/mu/recipes/deep-research.md +0 -69
  106. package/skills/mu/recipes/drift-audit.md +0 -75
  107. package/skills/mu/recipes/fan-out.md +0 -42
  108. package/skills/mu/recipes/hypothesis-panel.md +0 -48
  109. package/skills/mu/recipes/long-run.md +0 -32
  110. package/skills/mu/recipes/loop-until-done.md +0 -45
  111. package/skills/mu/recipes/plan.md +0 -67
  112. package/skills/mu/recipes/refute.md +0 -55
  113. package/skills/mu/recipes/review-panel.md +0 -65
  114. package/skills/mu/recipes/rules-audit.md +0 -89
  115. package/skills/mu/recipes/tournament.md +0 -44
  116. package/skills/mu/recipes/ultrathink.md +0 -86
  117. package/skills/mu/recipes/watcher.md +0 -28
  118. package/skills/mu/recipes/waves.md +0 -21
  119. /package/dist/{chunk-3BARPJLY.js.map → errors-KHOYKD7D.js.map} +0 -0
@@ -0,0 +1,4482 @@
1
+ import { createRequire as __muCreateRequire } from "node:module"; const require = __muCreateRequire(import.meta.url);
2
+ import {
3
+ DelegateCallConflictError,
4
+ DelegateNotFoundError,
5
+ DelegateStateError,
6
+ IN_FLIGHT_STATES,
7
+ TERMINAL_DELEGATE_STATES,
8
+ ambientFlush,
9
+ ambientIngest,
10
+ authorisedReady,
11
+ autoPermitted,
12
+ countUnpublishedOps,
13
+ currentCallers,
14
+ deliveredOf,
15
+ endAttempt,
16
+ finishDelegate,
17
+ foreignVersionMessage,
18
+ getAutoConfig,
19
+ getDelegateCall,
20
+ listDelegates,
21
+ listDelegatesOf,
22
+ liveAgentToOutput,
23
+ localMachineId,
24
+ markDelegate,
25
+ openHumanNeeds,
26
+ ownSegmentForeignLine,
27
+ pendingAttempts,
28
+ pinnedNotes,
29
+ planAttempt,
30
+ projectLog,
31
+ readAttempt,
32
+ recordHumanNeeded,
33
+ registerDelegateCall,
34
+ reserveAttempt,
35
+ reserveRetry,
36
+ tallyCalls,
37
+ verdictTally
38
+ } from "./chunk-GQNHIPJN.js";
39
+ import {
40
+ AgentLockBusyError,
41
+ AgentNotFoundError,
42
+ AgentReplacedError,
43
+ AgentRestartedError,
44
+ AgentSendRefusedError,
45
+ CTL_DEFAULT_TIMEOUT_MS,
46
+ CtlWriteVetoedError,
47
+ DEFAULT_SUBSTATE,
48
+ DELEGATE_TOKEN,
49
+ NoMultiplexerError,
50
+ RemoteRecoveryRefusedError,
51
+ SYNCED_ENTITIES,
52
+ SpawnAttemptBusyError,
53
+ SpawnAttemptGuardError,
54
+ TaskNotFoundError,
55
+ WorkstreamNotFoundError,
56
+ activeMux,
57
+ addNote,
58
+ agentCtlSocket,
59
+ agentKey,
60
+ agentOwed,
61
+ assertRecoveryProfile,
62
+ assertRecoveryUnchanged,
63
+ assertRecoveryUsable,
64
+ beginWait,
65
+ checkCaller,
66
+ checkMessage,
67
+ closeTask,
68
+ closedToken,
69
+ compareHlc,
70
+ compareStrings,
71
+ ctlProbe,
72
+ cursorKey,
73
+ delegateOutcome,
74
+ deleteAgent,
75
+ dropOwnedRefs,
76
+ emitEvent,
77
+ endWait,
78
+ expectsCtl,
79
+ fenceAgentRef,
80
+ getAgent,
81
+ getReceipt,
82
+ getTask,
83
+ getTaskNote,
84
+ getTaskOwner,
85
+ git,
86
+ hasDoneMark,
87
+ isConsumed,
88
+ isValidTaskId,
89
+ listBlocked,
90
+ listInProgress,
91
+ listReady,
92
+ listTasks,
93
+ listTasksByOwner,
94
+ localPaneAlive,
95
+ locksDir,
96
+ logStallOnce,
97
+ newMsgId,
98
+ observeAgentStates,
99
+ openDb,
100
+ parseHlc,
101
+ pinnedByOthers,
102
+ readCursors,
103
+ readLiveAgents,
104
+ readWaitRegistration,
105
+ recordCursors,
106
+ recordSend,
107
+ recoveryGone,
108
+ recoveryHolds,
109
+ registerOwnedWait,
110
+ releaseTask,
111
+ resolveCliCommand,
112
+ runLeasedTemplate,
113
+ savedIdentity,
114
+ say,
115
+ selectRemoteRecovery,
116
+ sendViaTransport,
117
+ setAgentDone,
118
+ setRecoveryCollectedHead,
119
+ settledCloseInput,
120
+ spawnAutoAttempt,
121
+ spawnForTask,
122
+ speaksMuCtl,
123
+ taskIdFor,
124
+ taskRevision,
125
+ tryResolveLiveWorkstreamId,
126
+ watchAgentRun,
127
+ withAgentLock,
128
+ withCaptureSuppressed,
129
+ withFileLock,
130
+ withHostChannel,
131
+ withOpContext
132
+ } from "./chunk-S424D5EP.js";
133
+
134
+ // src/rebuild.ts
135
+ import { existsSync, mkdirSync, rmSync, statSync } from "fs";
136
+ import { dirname } from "path";
137
+ var RebuildTargetExistsError = class extends Error {
138
+ constructor(path) {
139
+ super(say("rebuild.target_exists", path));
140
+ this.path = path;
141
+ this.name = "RebuildTargetExistsError";
142
+ }
143
+ path;
144
+ };
145
+ var RebuildTargetIsSourceError = class extends Error {
146
+ constructor(path) {
147
+ super(say("rebuild.target_is_source", path));
148
+ this.path = path;
149
+ this.name = "RebuildTargetIsSourceError";
150
+ }
151
+ path;
152
+ };
153
+ var UNREBUILDABLE_TABLES = [
154
+ "agents",
155
+ "task_auto",
156
+ "worktrees",
157
+ "delegate_calls",
158
+ "wait_cursors",
159
+ "wait_registrations",
160
+ // The condition sets behind `mu wait --resume <id>`: a handle is worthless on a rebuilt DB.
161
+ "wait_resumes",
162
+ "agent_settles",
163
+ "host_profiles",
164
+ "host_channels",
165
+ "spawn_receipts",
166
+ // The stall actions a wait took (steer exclusion, release record): no op carries them.
167
+ "wait_action_receipts",
168
+ // A remote worker's worktree, kept when its row was deleted: no op carries it.
169
+ "remote_recovery",
170
+ // The human task and its reason rebuild from ops; WHO recorded it does
171
+ // not. Ownership is never inferred from a note author.
172
+ "human_needs"
173
+ ];
174
+ var LOG_ONLY_ENTITIES = /* @__PURE__ */ new Set(["message", "event", "broadcast"]);
175
+ function isProjectable(entity) {
176
+ if (LOG_ONLY_ENTITIES.has(entity)) return false;
177
+ return SYNCED_ENTITIES.includes(entity);
178
+ }
179
+ function rebuildInto(source, opts) {
180
+ const { targetPath } = opts;
181
+ const sourceFile = source.pragma("database_list").find(
182
+ (row) => row.name === "main"
183
+ )?.file;
184
+ if (sourceFile !== void 0 && sourceFile.length > 0) {
185
+ const sameFile = sourceFile === targetPath || existsSync(targetPath) && existsSync(sourceFile) && sameInode(sourceFile, targetPath);
186
+ if (sameFile) throw new RebuildTargetIsSourceError(targetPath);
187
+ }
188
+ if (existsSync(targetPath) && opts.force !== true) {
189
+ throw new RebuildTargetExistsError(targetPath);
190
+ }
191
+ mkdirSync(dirname(targetPath), { recursive: true });
192
+ const snapshot2 = source.transaction(() => {
193
+ const identity2 = source.prepare(
194
+ "SELECT machine_id, hostname, created_at, last_wall, last_counter FROM machine_identity WHERE id = 1"
195
+ ).get();
196
+ if (!identity2) throw new Error(say("rebuild.no_identity"));
197
+ const opRows2 = source.prepare(
198
+ `SELECT hlc, machine_id, group_id, actor, intent, entity, key, op, payload, created_at
199
+ FROM ops`
200
+ ).all();
201
+ opRows2.sort((a, b) => compareHlc(a.hlc, b.hlc));
202
+ const peerRows2 = source.prepare("SELECT machine_id, last_applied_seq, last_seen_at FROM sync_peers").all();
203
+ const machineLocalLost2 = [];
204
+ for (const table of UNREBUILDABLE_TABLES) {
205
+ const row = source.prepare(`SELECT COUNT(*) AS n FROM ${table}`).get();
206
+ if (row.n > 0) machineLocalLost2.push({ table, rows: row.n });
207
+ }
208
+ return { identity: identity2, opRows: opRows2, peerRows: peerRows2, machineLocalLost: machineLocalLost2 };
209
+ });
210
+ const { identity, opRows, peerRows, machineLocalLost } = snapshot2();
211
+ if (opts.force === true) {
212
+ for (const suffix of ["", "-wal", "-shm"]) rmSync(`${targetPath}${suffix}`, { force: true });
213
+ }
214
+ const target = openDb({ path: targetPath });
215
+ try {
216
+ target.prepare(
217
+ `UPDATE machine_identity
218
+ SET machine_id = @machineId, hostname = @hostname, created_at = @createdAt,
219
+ last_wall = @lastWall, last_counter = @lastCounter
220
+ WHERE id = 1`
221
+ ).run({
222
+ machineId: identity.machine_id,
223
+ hostname: identity.hostname,
224
+ createdAt: identity.created_at,
225
+ lastWall: identity.last_wall,
226
+ lastCounter: identity.last_counter
227
+ });
228
+ const insertOp = target.prepare(
229
+ `INSERT OR IGNORE INTO ops
230
+ (hlc, machine_id, group_id, actor, intent, entity, key, op, payload, created_at)
231
+ VALUES (@hlc, @machineId, @groupId, @actor, @intent, @entity, @key, @op, @payload, @createdAt)`
232
+ );
233
+ const insertPeer = target.prepare(
234
+ `INSERT OR REPLACE INTO sync_peers (machine_id, last_applied_seq, last_seen_at)
235
+ VALUES (@machineId, @lastAppliedSeq, @lastSeenAt)`
236
+ );
237
+ const logOnlyByEntity = {};
238
+ let opsProjected = 0;
239
+ let opsChangedRows = 0;
240
+ const replay = target.transaction(() => {
241
+ withCaptureSuppressed(target, () => {
242
+ for (const row of opRows) {
243
+ insertOp.run({
244
+ hlc: row.hlc,
245
+ machineId: row.machine_id,
246
+ groupId: row.group_id,
247
+ actor: row.actor,
248
+ intent: row.intent,
249
+ entity: row.entity,
250
+ key: row.key,
251
+ op: row.op,
252
+ payload: row.payload,
253
+ createdAt: row.created_at
254
+ });
255
+ if (isProjectable(row.entity)) opsProjected += 1;
256
+ else logOnlyByEntity[row.entity] = (logOnlyByEntity[row.entity] ?? 0) + 1;
257
+ }
258
+ advanceClockPast(target, opRows);
259
+ opsChangedRows = projectLog(target);
260
+ for (const peer of peerRows) {
261
+ insertPeer.run({
262
+ machineId: peer.machine_id,
263
+ lastAppliedSeq: peer.last_applied_seq,
264
+ lastSeenAt: peer.last_seen_at
265
+ });
266
+ }
267
+ });
268
+ });
269
+ replay.immediate();
270
+ const rebuiltRows = {};
271
+ for (const table of ["workstreams", "tasks", "task_edges", "task_notes", "ops"]) {
272
+ const row = target.prepare(`SELECT COUNT(*) AS n FROM ${table}`).get();
273
+ rebuiltRows[table] = row.n;
274
+ }
275
+ return {
276
+ targetPath,
277
+ opsCopied: rebuiltRows.ops ?? 0,
278
+ opsProjected,
279
+ opsChangedRows,
280
+ logOnlyByEntity,
281
+ rebuiltRows,
282
+ machineLocalLost,
283
+ machineId: identity.machine_id
284
+ };
285
+ } finally {
286
+ target.close();
287
+ }
288
+ }
289
+ function advanceClockPast(target, rows) {
290
+ let newest;
291
+ for (const row of rows) {
292
+ let h;
293
+ try {
294
+ h = parseHlc(row.hlc);
295
+ } catch {
296
+ continue;
297
+ }
298
+ if (!newest || h.wallMs > newest.wallMs || h.wallMs === newest.wallMs && h.counter > newest.counter) {
299
+ newest = h;
300
+ }
301
+ }
302
+ if (!newest) return;
303
+ target.prepare(
304
+ `UPDATE machine_identity SET last_wall = @wall, last_counter = @counter
305
+ WHERE id = 1 AND (last_wall < @wall OR (last_wall = @wall AND last_counter < @counter))`
306
+ ).run({ wall: newest.wallMs, counter: newest.counter });
307
+ }
308
+ function sameInode(a, b) {
309
+ try {
310
+ const sa = statSync(a);
311
+ const sb = statSync(b);
312
+ return sa.ino === sb.ino && sa.dev === sb.dev;
313
+ } catch {
314
+ return false;
315
+ }
316
+ }
317
+
318
+ // src/drift.ts
319
+ import { mkdtempSync, rmSync as rmSync2 } from "fs";
320
+ import { tmpdir } from "os";
321
+ import { join } from "path";
322
+ var DRIFT_REPORT_CAP = 20;
323
+ function checkCheapDriftInvariant(db) {
324
+ const started = Date.now();
325
+ const unexplained = [];
326
+ const probes = [
327
+ {
328
+ table: "workstreams",
329
+ sql: `SELECT w.name AS key FROM workstreams w
330
+ WHERE NOT EXISTS (
331
+ SELECT 1 FROM ops o WHERE o.entity = 'workstream' AND o.key = w.name)`
332
+ },
333
+ {
334
+ table: "tasks",
335
+ sql: `SELECT w.name || '/' || t.local_id AS key
336
+ FROM tasks t JOIN workstreams w ON w.id = t.workstream_id
337
+ WHERE NOT EXISTS (
338
+ SELECT 1 FROM ops o
339
+ WHERE o.entity = 'task' AND o.key = w.name || '/' || t.local_id)`
340
+ },
341
+ {
342
+ table: "task_edges",
343
+ sql: `SELECT wf.name || '/' || f.local_id || '->' || wt.name || '/' || t.local_id AS key
344
+ FROM task_edges e
345
+ JOIN tasks f ON f.id = e.from_task_id
346
+ JOIN tasks t ON t.id = e.to_task_id
347
+ JOIN workstreams wf ON wf.id = f.workstream_id
348
+ JOIN workstreams wt ON wt.id = t.workstream_id
349
+ WHERE NOT EXISTS (
350
+ SELECT 1 FROM ops o
351
+ WHERE o.entity = 'edge'
352
+ AND o.key = wf.name || '/' || f.local_id || '->' || wt.name || '/' || t.local_id)`
353
+ },
354
+ {
355
+ // Notes are keyed <task-key>#<origin-id> and the origin id is the
356
+ // AUTHORING machine's surrogate id, which need not match ours (see
357
+ // src/apply.ts § syncNotes). So match on the task-key PREFIX
358
+ // rather than the exact key: the question here is "does the log
359
+ // know about a note on this task at all", not "which note".
360
+ //
361
+ // Express the prefix as an indexed half-open range. A correlated
362
+ // `LIKE task_key || '#%'` made SQLite scan every note op for every
363
+ // live note (seconds on the dogfood DB); '#'..'$' contains exactly
364
+ // the strings beginning with `task_key || '#'` because task ids
365
+ // cannot contain either delimiter.
366
+ //
367
+ // The answer is per TASK, so probe each task that has notes once
368
+ // (2.3k probes, not 10.7k on the dogfood DB), then list the notes
369
+ // of the tasks that fail. MATERIALIZED stops SQLite flattening the
370
+ // probe back into a per-note subquery.
371
+ table: "task_notes",
372
+ sql: `WITH bare AS MATERIALIZED (
373
+ SELECT t.id AS task_id, w.name || '/' || t.local_id AS task_key
374
+ FROM tasks t
375
+ JOIN workstreams w ON w.id = t.workstream_id
376
+ WHERE t.id IN (SELECT task_id FROM task_notes)
377
+ AND NOT EXISTS (
378
+ SELECT 1 FROM ops o
379
+ WHERE o.entity = 'note'
380
+ AND o.key >= w.name || '/' || t.local_id || '#'
381
+ AND o.key < w.name || '/' || t.local_id || '$'))
382
+ SELECT b.task_key || '#' || n.id AS key
383
+ FROM bare b
384
+ JOIN task_notes n ON n.task_id = b.task_id
385
+ ORDER BY n.task_id, n.id`
386
+ }
387
+ ];
388
+ for (const probe of probes) {
389
+ const rows = db.prepare(probe.sql).all();
390
+ for (const row of rows) unexplained.push({ table: probe.table, key: row.key });
391
+ }
392
+ return {
393
+ clean: unexplained.length === 0,
394
+ unexplainedRows: unexplained.slice(0, DRIFT_REPORT_CAP),
395
+ totalUnexplained: unexplained.length,
396
+ elapsedMs: Date.now() - started
397
+ };
398
+ }
399
+ var COMPARED = {
400
+ workstreams: ["created_at", "torn_down_at"],
401
+ tasks: [
402
+ "title",
403
+ "status",
404
+ "substate",
405
+ "impact",
406
+ "effort_days",
407
+ "scored",
408
+ "dispatch_host",
409
+ "kind",
410
+ "wave",
411
+ "created_at",
412
+ "updated_at"
413
+ ],
414
+ task_notes: ["author", "content", "created_at", "kind"],
415
+ task_edges: ["created_at"]
416
+ };
417
+ var SNAPSHOT_SQL = {
418
+ workstreams: `SELECT w.name AS key, w.created_at, w.torn_down_at
419
+ FROM workstreams w`,
420
+ tasks: `SELECT w.name || '/' || t.local_id AS key,
421
+ t.title, t.status, t.substate, t.impact, t.effort_days, t.scored,
422
+ t.dispatch_host, t.kind, t.wave, t.created_at, t.updated_at
423
+ FROM tasks t JOIN workstreams w ON w.id = t.workstream_id`,
424
+ // Notes are a GROW-ONLY SET whose local surrogate id is not portable,
425
+ // so identity for diffing is (task, author, content, created_at) —
426
+ // exactly the identity syncNotes (src/apply.ts) folds notes by. Using the
427
+ // '#<id>' key here would report drift on every note that replayed to a
428
+ // different rowid, which is normal and not drift.
429
+ //
430
+ // char(31) (ASCII Unit Separator) joins the parts: it cannot occur in a
431
+ // workstream/task id and is vanishingly unlikely in note prose, so two
432
+ // distinct notes cannot collide into one composite key. A literal NUL
433
+ // would be the textbook choice but SQLite truncates TEXT at NUL.
434
+ task_notes: `SELECT w.name || '/' || t.local_id || char(31) ||
435
+ COALESCE(n.author, '') || char(31) || n.content || char(31) ||
436
+ n.created_at AS key,
437
+ n.author, n.content, n.created_at, n.kind
438
+ FROM task_notes n
439
+ JOIN tasks t ON t.id = n.task_id
440
+ JOIN workstreams w ON w.id = t.workstream_id`,
441
+ task_edges: `SELECT wf.name || '/' || f.local_id || '->' || wt.name || '/' || t.local_id AS key,
442
+ e.created_at
443
+ FROM task_edges e
444
+ JOIN tasks f ON f.id = e.from_task_id
445
+ JOIN tasks t ON t.id = e.to_task_id
446
+ JOIN workstreams wf ON wf.id = f.workstream_id
447
+ JOIN workstreams wt ON wt.id = t.workstream_id`
448
+ };
449
+ function snapshot(db, table) {
450
+ const sql = SNAPSHOT_SQL[table];
451
+ if (sql === void 0) throw new Error(`no drift snapshot defined for table ${table}`);
452
+ const out = /* @__PURE__ */ new Map();
453
+ for (const row of db.prepare(sql).all()) {
454
+ const key = row.key;
455
+ if (typeof key !== "string") continue;
456
+ out.set(key, row);
457
+ }
458
+ return out;
459
+ }
460
+ function show(v) {
461
+ if (v === null || v === void 0) return null;
462
+ return String(v);
463
+ }
464
+ function checkDrift(db) {
465
+ const started = Date.now();
466
+ const dir = mkdtempSync(join(tmpdir(), "mu-drift-"));
467
+ const target = join(dir, "rebuilt.db");
468
+ try {
469
+ rebuildInto(db, { targetPath: target });
470
+ const rebuilt = openDb({ path: target });
471
+ try {
472
+ const records = [];
473
+ const rowsCompared = {};
474
+ let total = 0;
475
+ const push = (record) => {
476
+ total += 1;
477
+ if (records.length < DRIFT_REPORT_CAP) records.push(record);
478
+ };
479
+ for (const table of Object.keys(SNAPSHOT_SQL)) {
480
+ const live = snapshot(db, table);
481
+ const expected = snapshot(rebuilt, table);
482
+ rowsCompared[table] = live.size;
483
+ for (const [key, liveRow] of live) {
484
+ const expectedRow = expected.get(key);
485
+ if (expectedRow === void 0) {
486
+ push({
487
+ table,
488
+ key,
489
+ field: "<row>",
490
+ live: "present",
491
+ expected: null,
492
+ presence: "missing-in-rebuild"
493
+ });
494
+ continue;
495
+ }
496
+ for (const field of COMPARED[table] ?? []) {
497
+ const a = liveRow[field];
498
+ const b = expectedRow[field];
499
+ if (show(a) !== show(b)) {
500
+ push({ table, key, field, live: show(a), expected: show(b) });
501
+ }
502
+ }
503
+ }
504
+ for (const key of expected.keys()) {
505
+ if (live.has(key)) continue;
506
+ push({
507
+ table,
508
+ key,
509
+ field: "<row>",
510
+ live: null,
511
+ expected: "present",
512
+ presence: "missing-in-live"
513
+ });
514
+ }
515
+ }
516
+ return {
517
+ clean: total === 0,
518
+ records,
519
+ totalDrift: total,
520
+ rowsCompared,
521
+ elapsedMs: Date.now() - started
522
+ };
523
+ } finally {
524
+ rebuilt.close();
525
+ }
526
+ } finally {
527
+ rmSync2(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 25 });
528
+ }
529
+ }
530
+ function formatDriftRecord(record) {
531
+ if (record.field === "<row>") {
532
+ return record.presence === "missing-in-live" ? `${record.table} ${record.key}: row MISSING from live tables (the log says it exists)` : `${record.table} ${record.key}: row present in live tables but the log cannot explain it`;
533
+ }
534
+ const live = record.live === null ? "NULL" : record.live;
535
+ const expected = record.expected === null ? "NULL" : record.expected;
536
+ return `${record.table} ${record.key}.${record.field}: live=${live} log=${expected}`;
537
+ }
538
+ function driftRemediation() {
539
+ return [
540
+ "Drift means the ops log and the live tables disagree. One of them is wrong.",
541
+ "",
542
+ " * If CAPTURE missed a mutation, the LIVE tables hold work the log never",
543
+ " recorded. Rebuilding would DISCARD it. Do not rebuild reflexively.",
544
+ " * If APPLY is lossy, the log is authoritative and a rebuild is the fix.",
545
+ "",
546
+ "Do this, in order:",
547
+ " 1. Back up first, before anything else: mu db backup /tmp/mu-drift-evidence.db",
548
+ " 2. Materialize what the log believes: mu rebuild /tmp/mu-rebuilt.db",
549
+ " 3. Compare the two by hand for the keys named above, and decide which",
550
+ " side is correct. `mu db query` against both files is the fastest way.",
551
+ " 4. Report it: drift is a capture/apply BUG, not operator error. The",
552
+ " named table/key/field above is the reproduction."
553
+ ];
554
+ }
555
+ var DriftDetectedError = class extends Error {
556
+ constructor(totalDrift, records) {
557
+ super(
558
+ `ops-log drift: ${totalDrift} divergence${totalDrift === 1 ? "" : "s"} between the live tables and the ops log`
559
+ );
560
+ this.totalDrift = totalDrift;
561
+ this.records = records;
562
+ this.name = "DriftDetectedError";
563
+ }
564
+ totalDrift;
565
+ records;
566
+ };
567
+
568
+ // src/fleet-hazards.ts
569
+ import { existsSync as existsSync2, lstatSync, readlinkSync, realpathSync, statfsSync } from "fs";
570
+ import { basename, dirname as dirname2, join as join2, resolve, sep } from "path";
571
+ var HazardFailError = class extends Error {
572
+ constructor(failedRows) {
573
+ super(
574
+ `doctor: ${failedRows.length} row${failedRows.length === 1 ? "" : "s"} failed (${failedRows.join(", ")}); see the remediation above`
575
+ );
576
+ this.failedRows = failedRows;
577
+ }
578
+ failedRows;
579
+ name = "HazardFailError";
580
+ };
581
+ var DbInsideSyncDirError = class extends Error {
582
+ constructor(dbPath, syncDir) {
583
+ super(
584
+ `DB ${dbPath} is inside MU_SYNC_DIR ${syncDir}: move it out (WAL files do not sync safely)`
585
+ );
586
+ this.dbPath = dbPath;
587
+ this.syncDir = syncDir;
588
+ }
589
+ dbPath;
590
+ syncDir;
591
+ name = "DbInsideSyncDirError";
592
+ };
593
+ function canonicalPath(path, hops = 0) {
594
+ const abs = resolve(path);
595
+ const tail = [];
596
+ let cur = abs;
597
+ for (; ; ) {
598
+ try {
599
+ return join2(realpathSync(cur), ...tail);
600
+ } catch {
601
+ try {
602
+ if (hops < 32 && lstatSync(cur).isSymbolicLink()) {
603
+ return join2(canonicalPath(resolve(dirname2(cur), readlinkSync(cur)), hops + 1), ...tail);
604
+ }
605
+ } catch {
606
+ }
607
+ const parent = dirname2(cur);
608
+ if (parent === cur) return abs;
609
+ tail.unshift(basename(cur));
610
+ cur = parent;
611
+ }
612
+ }
613
+ }
614
+ function isPathInside(child, parent) {
615
+ const c = canonicalPath(child);
616
+ const p = canonicalPath(parent);
617
+ if (c === p) return true;
618
+ return c.startsWith(p.endsWith(sep) ? p : p + sep);
619
+ }
620
+ function checkDbInsideSyncDir(dbPath, syncDir) {
621
+ if (syncDir === void 0 || syncDir.trim() === "") {
622
+ return {
623
+ name: "db-vs-sync",
624
+ severity: "ok",
625
+ detail: "MU_SYNC_DIR not set (no sync configured)"
626
+ };
627
+ }
628
+ if (!isPathInside(dbPath, syncDir)) {
629
+ return { name: "db-vs-sync", severity: "ok", detail: "DB is outside the sync dir" };
630
+ }
631
+ return {
632
+ name: "db-vs-sync",
633
+ severity: "fail",
634
+ detail: `DB is INSIDE MU_SYNC_DIR \u2014 this WILL corrupt it (${dbPath})`,
635
+ remediation: [
636
+ "The mu database must never live inside the synced folder.",
637
+ "",
638
+ "A live WAL-mode SQLite DB is three files (mu.db, -wal, -shm) whose mutual",
639
+ "consistency is the basis of durability. File-sync tools copy whole files,",
640
+ "out of order, and across machines: a stale -wal resurrected from a peer, or",
641
+ "a main file copied mid-checkpoint, yields a DB that OPENS FINE and is",
642
+ "silently corrupt. Two machines writing the same synced file is worse \u2014 last",
643
+ "writer wins on the whole FILE, so one machine's entire history disappears.",
644
+ "",
645
+ "mu syncs append-only per-machine SEGMENTS precisely to avoid this: one",
646
+ "writer per file, never contended, so any file-mover is adequate transport.",
647
+ "",
648
+ "Fix: move the DB out of the sync dir, e.g.",
649
+ " export MU_DB_PATH=$HOME/.local/state/mu-next/mu.db",
650
+ "and keep MU_SYNC_DIR pointing at the shared folder for segments only."
651
+ ]
652
+ };
653
+ }
654
+ var NETWORK_FS_MAGIC = /* @__PURE__ */ new Map([
655
+ // Every value here is transcribed from linux/magic.h, not recalled.
656
+ // An invented magic is worse than a missing one: it would either never
657
+ // match (silent no-op) or match the wrong filesystem and warn falsely.
658
+ [26985, "NFS"],
659
+ // NFS_SUPER_MAGIC
660
+ [4283649346, "CIFS/SMB"],
661
+ // CIFS_SUPER_MAGIC
662
+ [4266872130, "SMB2"],
663
+ // SMB2_SUPER_MAGIC
664
+ [20859, "SMBFS"],
665
+ // SMB_SUPER_MAGIC
666
+ [1702057286, "FUSE (may be sshfs/rclone/cloud drive)"],
667
+ // FUSE_SUPER_MAGIC
668
+ [12805120, "Ceph"]
669
+ // CEPH_SUPER_MAGIC
670
+ ]);
671
+ function classifyFsType(magic, platform = process.platform) {
672
+ if (platform !== "linux") {
673
+ return { kind: "unknown", label: `unrecognised (${platform})` };
674
+ }
675
+ const network = NETWORK_FS_MAGIC.get(magic);
676
+ if (network !== void 0) return { kind: "network", label: network };
677
+ return { kind: "local", label: `local (0x${magic.toString(16)})` };
678
+ }
679
+ function probeFilesystem(path) {
680
+ try {
681
+ let probeTarget = resolve(path);
682
+ while (!existsSync2(probeTarget)) {
683
+ const parent = resolve(probeTarget, "..");
684
+ if (parent === probeTarget) break;
685
+ probeTarget = parent;
686
+ }
687
+ const stats = statfsSync(probeTarget);
688
+ return classifyFsType(Number(stats.type));
689
+ } catch {
690
+ return { kind: "unknown", label: "probe failed" };
691
+ }
692
+ }
693
+ function checkNetworkMount(dbPath, probe = probeFilesystem(dbPath)) {
694
+ if (probe.kind === "network") {
695
+ return {
696
+ name: "db-filesystem",
697
+ severity: "warn",
698
+ detail: `DB is on a network mount (${probe.label}) \u2014 WAL locking is unreliable there`,
699
+ remediation: [
700
+ `The DB appears to live on ${probe.label}.`,
701
+ "",
702
+ "SQLite's WAL mode needs working POSIX advisory locks AND a shared-memory",
703
+ "file. NFS, SMB/CIFS and FUSE mounts (sshfs, rclone, cloud drives) provide",
704
+ "neither dependably. Expect 'database is locked' with no contention, or",
705
+ "silent corruption with real contention from parallel mu invocations.",
706
+ "",
707
+ "Fix: keep the DB on local disk and sync SEGMENTS instead \u2014",
708
+ " export MU_DB_PATH=$HOME/.local/state/mu-next/mu.db",
709
+ "That is what the segment design is for: append-only, single-writer files",
710
+ "that any file-mover can carry safely."
711
+ ]
712
+ };
713
+ }
714
+ if (probe.kind === "unknown") {
715
+ return {
716
+ name: "db-filesystem",
717
+ severity: "ok",
718
+ detail: `filesystem not classifiable on this platform (${probe.label}); assuming local`
719
+ };
720
+ }
721
+ return { name: "db-filesystem", severity: "ok", detail: `DB is on ${probe.label}` };
722
+ }
723
+ function findCaseCollisions(db) {
724
+ const rows = db.prepare("SELECT name FROM workstreams ORDER BY name").all();
725
+ const byFolded = /* @__PURE__ */ new Map();
726
+ for (const row of rows) {
727
+ const folded = row.name.toLowerCase();
728
+ const bucket = byFolded.get(folded);
729
+ if (bucket === void 0) byFolded.set(folded, [row.name]);
730
+ else bucket.push(row.name);
731
+ }
732
+ const collisions = [];
733
+ for (const [folded, names] of byFolded) {
734
+ if (names.length > 1) collisions.push({ folded, names });
735
+ }
736
+ return collisions;
737
+ }
738
+ function checkCaseCollisions(db) {
739
+ const collisions = findCaseCollisions(db);
740
+ if (collisions.length === 0) {
741
+ return {
742
+ name: "name-case",
743
+ severity: "ok",
744
+ detail: "no case-colliding workstream names"
745
+ };
746
+ }
747
+ const shown = collisions.slice(0, 5).map((c) => c.names.join(" / ")).join(", ");
748
+ return {
749
+ name: "name-case",
750
+ severity: "warn",
751
+ detail: `${collisions.length} case-colliding workstream name(s): ${shown}`,
752
+ remediation: [
753
+ "These workstream names differ only by letter case:",
754
+ ...collisions.map((c) => ` ${c.names.join(" / ")}`),
755
+ "",
756
+ "They coexist on Linux (ext4 is case-sensitive) but COLLIDE on macOS (APFS",
757
+ "is case-insensitive) and on Windows (NTFS). Because a workstream name IS a",
758
+ "tmux session name and seeds every worktree path, a Mac in the fleet sees",
759
+ "one session and one directory where Linux sees two \u2014 so the fleet reaches",
760
+ "different states depending on which machine applies an op first.",
761
+ "",
762
+ "Fix: rename one side to a distinct name before adding a Mac to the fleet.",
763
+ "There is no in-place rename verb; back the DB up, re-init under the new",
764
+ "name, and tear down the old one:",
765
+ " mu db backup <file>",
766
+ " mu workstream init <new-name>",
767
+ " mu workstream teardown <old-name> # preview; then apply with --if <rev>"
768
+ ]
769
+ };
770
+ }
771
+ function checkFleetHazards(db, opts = { dbPath: "" }) {
772
+ const syncDir = opts.syncDir !== void 0 ? opts.syncDir : process.env.MU_SYNC_DIR;
773
+ return [
774
+ checkDbInsideSyncDir(opts.dbPath, syncDir),
775
+ checkNetworkMount(opts.dbPath),
776
+ checkCaseCollisions(db)
777
+ ];
778
+ }
779
+ function checkSegmentVersion(db, syncDir) {
780
+ if (syncDir === void 0 || syncDir.trim() === "") {
781
+ return { name: "segment-version", severity: "ok", detail: "MU_SYNC_DIR not set" };
782
+ }
783
+ const found = ownSegmentForeignLine(db, syncDir);
784
+ if (found === null) {
785
+ return { name: "segment-version", severity: "ok", detail: "own segment readable by this mu" };
786
+ }
787
+ return {
788
+ name: "segment-version",
789
+ severity: "warn",
790
+ detail: foreignVersionMessage({ segmentPath: found.path, foreignVersion: found.version }),
791
+ remediation: [
792
+ `Line ${found.line} of the segment was written by a newer mu. This mu appends nothing`,
793
+ "and repairs nothing there; new ops stay in the local DB and are written",
794
+ "once mu is upgraded on this machine."
795
+ ]
796
+ };
797
+ }
798
+ var GUARD_LIST_CAP = 10;
799
+ function listIds(rows) {
800
+ const shown = rows.slice(0, GUARD_LIST_CAP).map((r) => r.id);
801
+ return rows.length > GUARD_LIST_CAP ? `${shown.join(", ")}, \u2026 (${rows.length} total)` : shown.join(", ");
802
+ }
803
+ function checkOrphanedAgents(db) {
804
+ const rows = db.prepare(
805
+ `SELECT w.name AS ws, a.name AS agent, a.pane_id AS pane
806
+ FROM agents a JOIN workstreams w ON w.id = a.workstream_id
807
+ WHERE w.torn_down_at IS NOT NULL
808
+ ORDER BY w.name, a.name`
809
+ ).all();
810
+ if (rows.length === 0) {
811
+ return {
812
+ name: "orphaned-agents",
813
+ severity: "ok",
814
+ detail: "no agent rows in torn-down workstreams"
815
+ };
816
+ }
817
+ const shown = rows.slice(0, GUARD_LIST_CAP).map((r) => `${r.ws}/${r.agent} (${r.pane})`);
818
+ return {
819
+ name: "orphaned-agents",
820
+ severity: "warn",
821
+ detail: `${rows.length} agent row(s) in torn-down workstreams: ${shown.join(", ")}${rows.length > GUARD_LIST_CAP ? ", \u2026" : ""}`,
822
+ remediation: [
823
+ "Their panes may still run. Per row: `mu agent close <agent> -w <ws>` (kills the pane; keeps the row while it lives)."
824
+ ]
825
+ };
826
+ }
827
+ function checkCloseGuards(db) {
828
+ const key = "w.name || '/' || t.local_id";
829
+ const openBlockers2 = db.prepare(
830
+ `SELECT ${key} AS id
831
+ FROM tasks t JOIN workstreams w ON w.id = t.workstream_id AND w.torn_down_at IS NULL
832
+ WHERE t.status = 'CLOSED' AND t.substate = 'done'
833
+ AND EXISTS (SELECT 1 FROM blockers b WHERE b.task_id = t.id)
834
+ ORDER BY 1`
835
+ ).all();
836
+ const noEvidence = db.prepare(
837
+ `SELECT ${key} AS id
838
+ FROM tasks t JOIN workstreams w ON w.id = t.workstream_id AND w.torn_down_at IS NULL
839
+ WHERE t.status = 'CLOSED'
840
+ AND NOT EXISTS (SELECT 1 FROM task_notes n WHERE n.task_id = t.id AND n.kind = 'close')
841
+ ORDER BY 1`
842
+ ).all();
843
+ const row = (name, found, what, fix) => found.length === 0 ? { name, severity: "ok", detail: `no ${what}` } : {
844
+ name,
845
+ severity: "warn",
846
+ detail: `${found.length} ${what}: ${listIds(found)}`,
847
+ remediation: [fix]
848
+ };
849
+ return [
850
+ row(
851
+ "closed-blockers",
852
+ openBlockers2,
853
+ "CLOSED/done task(s) with open blockers",
854
+ "A peer merged these states (the local guard refuses them). Inspect with `mu task tree <id>`; reopen or close the blockers."
855
+ ),
856
+ row(
857
+ "closed-evidence",
858
+ noEvidence,
859
+ "CLOSED task(s) with no close note",
860
+ 'A peer merged the close without its evidence note. Per listed task: `mu task open <ws>/<id>`, then `mu task close <ws>/<id> --evidence "<command + result>"` (close its open blockers first).'
861
+ )
862
+ ];
863
+ }
864
+ function checkUnpublishedOps(db, syncDir) {
865
+ if (syncDir === void 0 || syncDir.trim() === "") {
866
+ return { name: "unpublished-ops", severity: "ok", detail: "MU_SYNC_DIR not set" };
867
+ }
868
+ const n = countUnpublishedOps(db, syncDir);
869
+ if (n === 0) {
870
+ return { name: "unpublished-ops", severity: "ok", detail: "every own op is in the segment" };
871
+ }
872
+ return {
873
+ name: "unpublished-ops",
874
+ severity: "warn",
875
+ detail: `${n} own op(s) not yet in this machine's segment; peers cannot see them`,
876
+ remediation: [
877
+ "Run `mu sync`: it flushes the backlog. If it reports the segment as locked,",
878
+ "another mu process holds the lock (a stale one is broken after 30s)."
879
+ ]
880
+ };
881
+ }
882
+
883
+ // src/glyphs.ts
884
+ var AGENT_STATE_GLYPH = {
885
+ busy: "\uF04B",
886
+ // nf-fa-play
887
+ needs_input: "\uF186",
888
+ // nf-fa-moon_o
889
+ needs_permission: "\uF023",
890
+ // nf-fa-lock
891
+ unknown: "\uF059"
892
+ // nf-fa-question_circle
893
+ };
894
+ function agentStateGlyph(state) {
895
+ return AGENT_STATE_GLYPH[state] ?? GLYPH.unknown;
896
+ }
897
+ var GLYPH = {
898
+ /** Healthy, clean, passing, closed-successfully. */
899
+ ok: "\uF058",
900
+ // nf-fa-check_circle
901
+ /** Failed check. */
902
+ fail: "\uF057",
903
+ // nf-fa-times_circle
904
+ /** Needs attention but not broken (idle-but-assigned, stale warning). */
905
+ warn: "\uF071",
906
+ // nf-fa-warning
907
+ /** Task is blocked by an incoming edge. */
908
+ blocked: "\uF0C1",
909
+ // nf-fa-link (chain)
910
+ /** Task is parked (OPEN/parked): open, but set aside on purpose. */
911
+ parked: "\uF04C",
912
+ // nf-fa-pause
913
+ /** Track whose roots merged (diamond dependency). */
914
+ merge: "\uF074",
915
+ // nf-fa-random
916
+ /** Agent owns more than one task (pane-title multi-count slot). */
917
+ multi: "\uF055",
918
+ // nf-fa-plus_circle
919
+ /** Filter toggle: enabled / disabled. */
920
+ on: "\uF111",
921
+ // nf-fa-circle
922
+ off: "\uF10C",
923
+ // nf-fa-circle_o
924
+ /** Status outside the known enum. */
925
+ unknown: "\uF059"
926
+ // nf-fa-question_circle
927
+ };
928
+
929
+ // src/wait-types.ts
930
+ var WAIT_HEARTBEAT_CAP_MS = 15e5;
931
+ var DEFAULT_STUCK_AFTER_MS = 12e4;
932
+ var DEFAULT_CTL_STUCK_AFTER_MS = 5e3;
933
+ var WaitNothingError = class extends Error {
934
+ name = "WaitNothingError";
935
+ constructor() {
936
+ super("wait: nothing to wait for (no refs given and none owed to this caller)");
937
+ }
938
+ };
939
+ var WaveNotFoundError = class extends Error {
940
+ constructor(wave, yours) {
941
+ super(say("wait.wave_not_found", wave, yours));
942
+ this.wave = wave;
943
+ }
944
+ wave;
945
+ name = "WaveNotFoundError";
946
+ };
947
+ var WaitRefError = class extends Error {
948
+ name = "WaitRefError";
949
+ };
950
+
951
+ // src/wait-regex.ts
952
+ import { createContext, Script } from "vm";
953
+ var WAIT_REGEX_MAX_LEN = 200;
954
+ var WAIT_TEXT_MAX = 8192;
955
+ var WAIT_REGEX_TIMEOUT_MS = 50;
956
+ var MAX_COUNT = 1e3;
957
+ var WaitRegexError = class extends Error {
958
+ name = "WaitRegexError";
959
+ };
960
+ function quantifierAt(src, i) {
961
+ const c = src[i];
962
+ if (c === "*" || c === "+" || c === "?") return { len: 1, counts: [] };
963
+ if (c !== "{") return void 0;
964
+ const m = /^\{(\d+)(?:,(\d*))?\}/.exec(src.slice(i, i + 24));
965
+ if (m === null) return void 0;
966
+ const counts2 = [Number(m[1])];
967
+ if (m[2] !== void 0 && m[2] !== "") counts2.push(Number(m[2]));
968
+ return { len: m[0].length, counts: counts2 };
969
+ }
970
+ function refuse(why) {
971
+ throw new WaitRegexError(`--until-text: ${why}`);
972
+ }
973
+ function checkRegexGrammar(src) {
974
+ if (src === "") refuse("the pattern is empty");
975
+ if (src.length > WAIT_REGEX_MAX_LEN) {
976
+ refuse(`the pattern is ${src.length} characters; the limit is ${WAIT_REGEX_MAX_LEN}`);
977
+ }
978
+ if (/[\u0000-\u001f\u007f]/.test(src)) refuse("the pattern has control characters");
979
+ const stack = [];
980
+ let top = { quant: false, alt: false };
981
+ let inClass = false;
982
+ for (let i = 0; i < src.length; i++) {
983
+ const c = src[i];
984
+ if (c === "\\") {
985
+ const n = src[i + 1] ?? "";
986
+ if (/[1-9k]/.test(n)) refuse("backreferences are not supported");
987
+ i++;
988
+ continue;
989
+ }
990
+ if (inClass) {
991
+ if (c === "]") inClass = false;
992
+ continue;
993
+ }
994
+ if (c === "[") {
995
+ inClass = true;
996
+ continue;
997
+ }
998
+ if (c === "(") {
999
+ if (src[i + 1] === "?") {
1000
+ if (src[i + 2] !== ":") refuse("lookaround and named groups are not supported");
1001
+ i += 2;
1002
+ }
1003
+ stack.push(top);
1004
+ top = { quant: false, alt: false };
1005
+ continue;
1006
+ }
1007
+ if (c === ")") {
1008
+ const parent = stack.pop();
1009
+ if (parent === void 0) refuse("unbalanced `)`");
1010
+ const q2 = quantifierAt(src, i + 1);
1011
+ if (q2 !== void 0 && (top.quant || top.alt)) {
1012
+ refuse("a repeated group may not contain a repeat or an alternation");
1013
+ }
1014
+ parent.quant ||= top.quant || q2 !== void 0;
1015
+ parent.alt ||= top.alt;
1016
+ top = parent;
1017
+ continue;
1018
+ }
1019
+ if (c === "|") {
1020
+ top.alt = true;
1021
+ continue;
1022
+ }
1023
+ const q = quantifierAt(src, i);
1024
+ if (q !== void 0) {
1025
+ if (q.counts.some((n) => n > MAX_COUNT)) refuse(`a repeat count is over ${MAX_COUNT}`);
1026
+ top.quant = true;
1027
+ i += q.len - 1;
1028
+ }
1029
+ }
1030
+ if (stack.length > 0) refuse("unbalanced `(`");
1031
+ }
1032
+ var SCRIPT = new Script("new RegExp(src).test(text)");
1033
+ function compileTextMatcher(src) {
1034
+ checkRegexGrammar(src);
1035
+ try {
1036
+ new RegExp(src);
1037
+ } catch (e) {
1038
+ refuse(`not a valid regular expression (${e instanceof Error ? e.message : String(e)})`);
1039
+ }
1040
+ const ctx = createContext({ src, text: "" });
1041
+ return {
1042
+ source: src,
1043
+ test(text) {
1044
+ ctx.text = text.length > WAIT_TEXT_MAX ? text.slice(-WAIT_TEXT_MAX) : text;
1045
+ try {
1046
+ return SCRIPT.runInContext(ctx, { timeout: WAIT_REGEX_TIMEOUT_MS }) === true;
1047
+ } catch (e) {
1048
+ throw new WaitRegexError(
1049
+ `--until-text: matching exceeded ${WAIT_REGEX_TIMEOUT_MS} ms (${e instanceof Error ? e.message : String(e)}); use a simpler pattern`
1050
+ );
1051
+ }
1052
+ }
1053
+ };
1054
+ }
1055
+
1056
+ // src/close-block.ts
1057
+ var HEADER = /^CLOSE: task=(\S+) evidence="(.*)"$/;
1058
+ var TEMPLATE = /^CLOSE:[ \t]+task=<[^>\s]*>/;
1059
+ var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
1060
+ var FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
1061
+ function quotedLines(lines) {
1062
+ const quoted = [];
1063
+ let open;
1064
+ for (const line of lines) {
1065
+ if (open) {
1066
+ quoted.push(true);
1067
+ const c = FENCE_CLOSE.exec(line);
1068
+ const run2 = c?.[1] ?? "";
1069
+ if (run2[0] === open.ch && run2.length >= open.len) open = void 0;
1070
+ continue;
1071
+ }
1072
+ const o = FENCE_OPEN.exec(line);
1073
+ const run = o?.[1] ?? "";
1074
+ if (o && !(run[0] === "`" && (o[2] ?? "").includes("`"))) {
1075
+ open = { ch: run[0], len: run.length };
1076
+ quoted.push(true);
1077
+ } else {
1078
+ quoted.push(false);
1079
+ }
1080
+ }
1081
+ return { quoted, unclosed: open !== void 0 };
1082
+ }
1083
+ var isStart = (line) => line.startsWith("CLOSE:") && !TEMPLATE.test(line);
1084
+ var none = (reason) => ({ kind: "none", reason });
1085
+ var malformed = (reason) => ({ kind: "malformed", reason });
1086
+ function parseCloseBlock(text) {
1087
+ if (text.trim() === "") return none("empty message");
1088
+ const body = text.replace(/(?:\r?\n)+$/, "");
1089
+ const raw = body.split("\n");
1090
+ const offsets = [];
1091
+ let at = 0;
1092
+ for (const l of raw) {
1093
+ offsets.push(at);
1094
+ at += l.length + 1;
1095
+ }
1096
+ const lines = raw.map((l) => l.replace(/\r$/, ""));
1097
+ const { quoted, unclosed } = quotedLines(lines);
1098
+ if (unclosed) return none("the message ends inside a code fence");
1099
+ let start = -1;
1100
+ for (let i = 0; i < lines.length; i++) {
1101
+ if (!quoted[i] && isStart(lines[i] ?? "")) start = i;
1102
+ }
1103
+ if (start < 0) return none("no CLOSE: line at column 0");
1104
+ const m = HEADER.exec(lines[start] ?? "");
1105
+ if (!m) return malformed('the CLOSE: line is not `CLOSE: task=<id> evidence="<text>"`');
1106
+ const task = m[1] ?? "";
1107
+ if (!isValidTaskId(task)) return malformed("task= is not a valid task id");
1108
+ const evidence = unescapeEvidence(m[2] ?? "");
1109
+ if (evidence === void 0) return malformed("evidence has a bare or unterminated quote");
1110
+ if (evidence.trim() === "") return malformed("evidence is empty");
1111
+ if (start + 1 >= lines.length || lines[start + 1] !== "REPORT:") {
1112
+ return malformed("the line after CLOSE: is not exactly REPORT:");
1113
+ }
1114
+ const report = body.slice(offsets[start + 2] ?? body.length);
1115
+ if (report.trim() === "") return malformed("the report is empty");
1116
+ const normalized = body.length < text.length ? `${report}
1117
+ ` : report;
1118
+ return { kind: "valid", task, evidence, report: normalized };
1119
+ }
1120
+ function unescapeEvidence(body) {
1121
+ let out = "";
1122
+ for (let i = 0; i < body.length; i++) {
1123
+ const c = body[i];
1124
+ if (c === '"') return void 0;
1125
+ if (c !== "\\") {
1126
+ out += c;
1127
+ continue;
1128
+ }
1129
+ const n = body[i + 1];
1130
+ if (n === '"' || n === "\\") {
1131
+ out += n;
1132
+ i++;
1133
+ } else if (n === "n") {
1134
+ out += "\n";
1135
+ i++;
1136
+ } else if (n === void 0) {
1137
+ return void 0;
1138
+ } else {
1139
+ out += c;
1140
+ }
1141
+ }
1142
+ return out;
1143
+ }
1144
+ var CLOSE_MALFORMED = "no close (malformed)";
1145
+ var CLOSE_TRUNCATED = "CLOSE block not applied: the final message was truncated; close with mu task close";
1146
+ var no = (reason, task = null) => ({
1147
+ applied: false,
1148
+ task,
1149
+ reason
1150
+ });
1151
+ function applySettledClose(db, input) {
1152
+ if (!input.settled) return no("not settled");
1153
+ if (input.lastError !== void 0) return no("run ended on an error");
1154
+ return withOpContext(
1155
+ db,
1156
+ { intent: "close.apply", actor: input.agent, group: "new" },
1157
+ () => applyInTxn(db, input)
1158
+ );
1159
+ }
1160
+ function applyInTxn(db, input) {
1161
+ const agent = db.prepare(
1162
+ `SELECT a.cli FROM agents a JOIN workstreams w ON w.id = a.workstream_id
1163
+ WHERE a.id = ? AND a.name = ? AND w.name = ? AND w.torn_down_at IS NULL`
1164
+ ).get(input.agentId, input.agent, input.workstream);
1165
+ if (!agent) return no("unknown agent");
1166
+ if (agent.cli !== "pi") return no("not a pi agent");
1167
+ const seen = db.prepare("SELECT 1 FROM agent_settles WHERE agent_id = ? AND incarnation = ? AND runs = ?").get(input.agentId, input.incarnation, input.runs);
1168
+ if (seen) return no("already applied");
1169
+ const record = (taskId) => {
1170
+ db.prepare(
1171
+ "INSERT INTO agent_settles (agent_id, incarnation, runs, task_id) VALUES (?, ?, ?, ?)"
1172
+ ).run(input.agentId, input.incarnation, input.runs, taskId);
1173
+ };
1174
+ const diagnose = (reason) => {
1175
+ const open = db.prepare(
1176
+ `SELECT t.local_id FROM tasks t JOIN workstreams w ON w.id = t.workstream_id
1177
+ WHERE t.owner_id = ? AND t.status = 'IN_PROGRESS' AND w.name = ?`
1178
+ ).all(input.agentId, input.workstream);
1179
+ const only = open.length === 1 ? open[0]?.local_id ?? null : null;
1180
+ if (only !== null) {
1181
+ addNote(db, only, reason, {
1182
+ workstream: input.workstream,
1183
+ author: input.agent,
1184
+ group: "inherit"
1185
+ });
1186
+ }
1187
+ record(null);
1188
+ return no(reason, only);
1189
+ };
1190
+ if (input.lastTextTruncated === true) return diagnose(CLOSE_TRUNCATED);
1191
+ const block = parseCloseBlock(input.lastText);
1192
+ if (block.kind === "none") return no(`no CLOSE block: ${block.reason}`);
1193
+ if (block.kind === "malformed") return diagnose(`${CLOSE_MALFORMED}: ${block.reason}`);
1194
+ const task = db.prepare(
1195
+ `SELECT t.id, t.status, t.owner_id, t.kind FROM tasks t JOIN workstreams w ON w.id = t.workstream_id
1196
+ WHERE w.name = ? AND w.torn_down_at IS NULL AND t.local_id = ?`
1197
+ ).get(input.workstream, block.task);
1198
+ if (task?.kind === "human") {
1199
+ record(task.id);
1200
+ return no(
1201
+ `human task: close it with \`mu task close ${block.task} -w ${input.workstream}\``,
1202
+ block.task
1203
+ );
1204
+ }
1205
+ if (task?.kind === "delegate") {
1206
+ record(task.id);
1207
+ return no("delegate task: its call records the answer and closes it", block.task);
1208
+ }
1209
+ if (!task || task.owner_id !== input.agentId) {
1210
+ record(null);
1211
+ return no("task is not owned by this agent", block.task);
1212
+ }
1213
+ if (task.status === "CLOSED") {
1214
+ record(task.id);
1215
+ return no("task already closed", block.task);
1216
+ }
1217
+ closeTask(db, block.task, {
1218
+ workstream: input.workstream,
1219
+ evidence: block.evidence,
1220
+ report: block.report,
1221
+ author: input.agent,
1222
+ group: "inherit"
1223
+ });
1224
+ record(task.id);
1225
+ return { applied: true, task: block.task, reason: "closed" };
1226
+ }
1227
+
1228
+ // src/wait-db.ts
1229
+ function closeSettledRun(db, ref, r, enabled) {
1230
+ if (!enabled || r.settled !== true) return void 0;
1231
+ const row = db.prepare(
1232
+ `SELECT a.id FROM agents a JOIN workstreams w ON w.id = a.workstream_id
1233
+ WHERE a.name = ? AND w.name = ?`
1234
+ ).get(ref.id, ref.workstream);
1235
+ const facts = settledCloseInput({
1236
+ workstream: ref.workstream,
1237
+ name: ref.id,
1238
+ agentId: r.agentId ?? row?.id ?? 0,
1239
+ status: r.status,
1240
+ settled: true,
1241
+ ...r.incarnation !== void 0 ? { incarnation: r.incarnation } : {},
1242
+ ...r.runs !== void 0 ? { runs: r.runs } : {},
1243
+ ...r.lastText !== void 0 ? { lastText: r.lastText } : {},
1244
+ ...r.lastTextTruncated !== void 0 ? { lastTextTruncated: r.lastTextTruncated } : {},
1245
+ ...r.lastError !== void 0 ? { lastError: r.lastError } : {}
1246
+ });
1247
+ if (facts === void 0) return void 0;
1248
+ try {
1249
+ const res = applySettledClose(db, facts);
1250
+ return { applied: res.applied, task: res.task, reason: res.reason };
1251
+ } catch (e) {
1252
+ return { applied: false, task: null, reason: e instanceof Error ? e.message : String(e) };
1253
+ }
1254
+ }
1255
+ function agentExists(db, ref) {
1256
+ return db.prepare(
1257
+ `SELECT 1 FROM agents a JOIN workstreams ws ON ws.id = a.workstream_id
1258
+ WHERE a.name = ? AND ws.name = ?`
1259
+ ).get(ref.id, ref.workstream) !== void 0;
1260
+ }
1261
+ function openBlockers(db, umbrella) {
1262
+ return db.prepare(BLOCKERS_SQL).all(umbrella);
1263
+ }
1264
+ var BLOCKERS_SQL = `
1265
+ WITH RECURSIVE reach(node) AS (
1266
+ SELECT e.from_task_id FROM task_edges e WHERE e.to_task_id = ?
1267
+ UNION
1268
+ SELECT e.from_task_id FROM task_edges e
1269
+ JOIN reach r ON e.to_task_id = r.node
1270
+ JOIN tasks t ON t.id = r.node
1271
+ WHERE t.status <> 'CLOSED'
1272
+ )
1273
+ SELECT t.local_id AS id, w.name AS ws FROM reach r
1274
+ JOIN tasks t ON t.id = r.node JOIN workstreams w ON w.id = t.workstream_id
1275
+ ORDER BY w.name, t.local_id`;
1276
+ function agentRowId(db, ref) {
1277
+ const row = db.prepare(
1278
+ `SELECT a.id FROM agents a JOIN workstreams ws ON ws.id = a.workstream_id
1279
+ WHERE a.name = ? AND ws.name = ?`
1280
+ ).get(ref.id, ref.workstream);
1281
+ return row?.id ?? 0;
1282
+ }
1283
+ function opsHead(db) {
1284
+ return db.prepare("SELECT COALESCE(MAX(seq), 0) AS s FROM ops").get().s;
1285
+ }
1286
+ function reapFlip(db, ref, since) {
1287
+ const key = `${ref.workstream}/${ref.id}`;
1288
+ const last = db.prepare(
1289
+ `SELECT seq, intent FROM ops WHERE entity = 'task' AND key = ?
1290
+ AND (json_extract(payload, '$.status') IS NOT NULL OR json_extract(payload, '$.substate') IS NOT NULL)
1291
+ ORDER BY seq DESC LIMIT 1`
1292
+ ).get(key);
1293
+ if (last === void 0 || last.intent !== "task.reap" || last.seq <= since) return void 0;
1294
+ const owner = db.prepare(
1295
+ `SELECT json_extract(payload, '$.owner_id') AS id, actor FROM ops
1296
+ WHERE entity = 'task' AND key = ? AND seq < ? AND json_extract(payload, '$.owner_id') IS NOT NULL
1297
+ ORDER BY seq DESC LIMIT 1`
1298
+ ).get(key, last.seq);
1299
+ return {
1300
+ seq: last.seq,
1301
+ ownerId: Number(owner?.id ?? 0),
1302
+ ...owner?.actor != null ? { ownerName: owner.actor } : {}
1303
+ };
1304
+ }
1305
+ function taskOwnerRowId(db, ref) {
1306
+ const row = db.prepare(
1307
+ `SELECT COALESCE(t.owner_id, 0) AS id FROM tasks t JOIN workstreams ws ON ws.id = t.workstream_id
1308
+ WHERE t.local_id = ? AND ws.name = ?`
1309
+ ).get(ref.id, ref.workstream);
1310
+ return row?.id ?? 0;
1311
+ }
1312
+
1313
+ // src/wait-item.ts
1314
+ var refKey = (r) => cursorKey(r.kind, r.workstream, r.id);
1315
+ var registered = (r) => ({
1316
+ kind: r.kind,
1317
+ workstream: r.workstream,
1318
+ id: r.id
1319
+ });
1320
+ var URGENCY = {
1321
+ dead: 0,
1322
+ input: 1,
1323
+ stall: 2,
1324
+ delegate: 3
1325
+ };
1326
+ var urgency = (k) => k === void 0 ? 9 : URGENCY[k] ?? 4;
1327
+ var isCompletion = (i) => i.kind === "closed" || i.kind === "settled" || i.kind === "pane-text" || i.kind === "exited" || i.kind === "delegate" || i.kind === "dead" && i.ref.kind === "agent";
1328
+ var exitFor = (k) => k === "dead" ? 6 : k === "input" || k === "stall" ? 7 : k === "interrupted" ? 130 : 0;
1329
+ var inputToken = (r) => ({
1330
+ incarnation: `input:${r.incarnation ?? ""}`,
1331
+ runs: r.blocked?.since ?? 0
1332
+ });
1333
+ var agentSnapshot = (t) => {
1334
+ const r = t.latched?.reading ?? t.reading;
1335
+ const status = t.latched?.kind === "dead" || t.latched?.kind === "exited" ? null : r?.status ?? "unknown";
1336
+ return {
1337
+ ref: t.ref,
1338
+ status,
1339
+ ...r?.agentId !== void 0 ? { agentId: r.agentId } : {},
1340
+ ...r?.paneId !== void 0 ? { paneId: r.paneId } : {},
1341
+ ...r?.settled !== void 0 ? { settled: r.settled } : {},
1342
+ ...r?.lastTextTruncated !== void 0 ? { lastTextTruncated: r.lastTextTruncated } : {},
1343
+ ...r?.textSource !== void 0 ? { textSource: r.textSource } : {},
1344
+ ...r?.incarnation !== void 0 ? { incarnation: r.incarnation } : {},
1345
+ ...r?.runs !== void 0 ? { runs: r.runs } : {},
1346
+ ...r?.lastText !== void 0 ? { lastText: r.lastText } : {},
1347
+ ...r?.lastError !== void 0 ? { lastError: r.lastError } : {},
1348
+ ...r?.sessionFile !== void 0 ? { sessionFile: r.sessionFile } : {},
1349
+ ...r?.blocked !== void 0 ? { blocked: r.blocked } : {},
1350
+ ...t.latched?.kind === "exited" && r?.exit !== void 0 ? { exit: r.exit } : {},
1351
+ ...status === "unknown" && r?.reason !== void 0 ? { unknownReason: r.reason } : {},
1352
+ ...t.baseline !== void 0 ? { afterRuns: t.baseline } : {},
1353
+ wasBusy: t.wasBusy,
1354
+ ...t.unknownBaseline ? { unknownBaseline: true } : {},
1355
+ fired: t.latched?.kind === "settled" || t.latched?.kind === "pane-text" || t.latched?.kind === "exited",
1356
+ dead: t.latched?.kind === "dead",
1357
+ ...t.close !== void 0 ? { close: t.close } : {}
1358
+ };
1359
+ };
1360
+ var delegateItem = (call, isTarget, suppressible, order) => {
1361
+ const ref = { kind: "delegate", workstream: call.workstream, id: call.callId };
1362
+ const terminal = TERMINAL_DELEGATE_STATES.includes(call.state);
1363
+ const item = {
1364
+ key: refKey(ref),
1365
+ ref,
1366
+ snap: {
1367
+ ref,
1368
+ status: call.state,
1369
+ verdict: call.verdict,
1370
+ owner: call.agent,
1371
+ // Which finding this is: the delegate's own task, the task it judges, its wave.
1372
+ callTask: call.task,
1373
+ ...call.record !== null ? { record: `${call.record.workstream}/${call.record.task}` } : {},
1374
+ ...call.wave !== null ? { wave: call.wave } : {},
1375
+ fired: terminal,
1376
+ dead: false
1377
+ },
1378
+ target: isTarget,
1379
+ suppressible,
1380
+ order,
1381
+ done: terminal,
1382
+ ready: false
1383
+ };
1384
+ if (terminal) {
1385
+ item.kind = "delegate";
1386
+ item.token = DELEGATE_TOKEN;
1387
+ }
1388
+ return item;
1389
+ };
1390
+ var sameDeath = (a, b) => {
1391
+ const task = a.ref.kind === "task" ? a : b;
1392
+ const agent = task === a ? b : a;
1393
+ const t = task.death;
1394
+ const g = agent.death;
1395
+ if (a.kind !== "dead" || b.kind !== "dead" || t === void 0 || g === void 0) return false;
1396
+ if (task.ref.kind !== "task" || agent.ref.kind !== "agent") return false;
1397
+ if (g.rowId !== 0) return g.rowId === t.rowId;
1398
+ return t.name === agent.ref.id && (t.workstream === void 0 || t.workstream === agent.ref.workstream);
1399
+ };
1400
+ var failedGate = (i) => i.kind === "exited" && i.snap.exit !== void 0 && i.snap.exit !== 0;
1401
+ var snapshotsOf = (items, extra = []) => {
1402
+ const targets = items.filter((i) => i.target);
1403
+ const base = targets.length > 0 ? targets : items;
1404
+ const shown = [...base];
1405
+ for (const e of extra) if (!shown.includes(e)) shown.push(e);
1406
+ return shown.map((i) => i.snap);
1407
+ };
1408
+
1409
+ // src/wait-latch.ts
1410
+ function rearmLatch(t, baseline, watching) {
1411
+ t.latched = void 0;
1412
+ t.close = void 0;
1413
+ t.handle = void 0;
1414
+ t.result = void 0;
1415
+ t.needWatch = watching;
1416
+ t.baseline = baseline;
1417
+ t.wasBusy = false;
1418
+ }
1419
+ function supersededBy(t, r) {
1420
+ const held = t.latched?.kind === "settled" ? t.latched.reading : void 0;
1421
+ if (held === void 0 || r.runs === void 0) return void 0;
1422
+ const differs = (a, b) => a !== void 0 && b !== void 0 && a !== b;
1423
+ if (differs(held.agentId, r.agentId) || differs(held.incarnation, r.incarnation)) return 0;
1424
+ if (held.runs !== void 0 && r.runs > held.runs) return held.runs;
1425
+ return void 0;
1426
+ }
1427
+
1428
+ // src/wait.ts
1429
+ var sleepSeam;
1430
+ async function waitForEvents(db, input, opts) {
1431
+ const callers = [...new Set(opts.callers ?? [])];
1432
+ for (const c of callers) checkCaller(c);
1433
+ const all = opts.mode === "all";
1434
+ const ownedScope = opts.eventScope !== "refs";
1435
+ const stuckExplicit = opts.stuckAfterMs;
1436
+ const stuckEnabled = stuckExplicit !== 0 && opts.status !== "OPEN" && opts.status !== "IN_PROGRESS";
1437
+ const stuckAfter = (viaCtl) => stuckExplicit ?? (viaCtl ? DEFAULT_CTL_STUCK_AFTER_MS : DEFAULT_STUCK_AFTER_MS);
1438
+ const watchDeaths = callers.length > 0 || ownedScope;
1439
+ const scope = opts.scope;
1440
+ const inScope = (workstream) => scope === void 0 || workstream === scope;
1441
+ const callInScope = (c) => scope === void 0 || c.workstream === scope || c.record?.workstream === scope;
1442
+ const observeOwners = (watchDeaths || stuckEnabled) && opts.readAgents !== void 0;
1443
+ const consumes = ownedScope && callers.length > 0;
1444
+ const target = opts.status ?? "CLOSED";
1445
+ const discover = consumes && target === "CLOSED";
1446
+ const matcher = typeof opts.until === "object" ? compileTextMatcher(opts.until.text) : void 0;
1447
+ const applyClose = opts.applyClose !== false;
1448
+ const pollMs = opts.pollMs ?? 1e3;
1449
+ const requested = opts.timeoutMs ?? 0;
1450
+ const timeoutMs = opts.uncapped ? requested : requested === 0 ? WAIT_HEARTBEAT_CAP_MS : Math.min(requested, WAIT_HEARTBEAT_CAP_MS);
1451
+ const deadline = timeoutMs > 0 ? Date.now() + timeoutMs : Number.POSITIVE_INFINITY;
1452
+ const sinceSeq = opts.sinceSeq ?? opsHead(db);
1453
+ for (const ref of input) {
1454
+ if (ref.kind === "task") {
1455
+ if (getTask(db, ref.id, ref.workstream) === void 0) {
1456
+ throw new TaskNotFoundError(`${ref.workstream}/${ref.id}`);
1457
+ }
1458
+ } else if (ref.kind === "agent") {
1459
+ if (!agentExists(db, ref)) throw new AgentNotFoundError(ref.id);
1460
+ } else {
1461
+ const call = getDelegateCall(db, ref.id);
1462
+ if (call === void 0 || call.workstream !== ref.workstream) {
1463
+ throw new DelegateNotFoundError(ref.id);
1464
+ }
1465
+ if (callers.length > 0 && !callers.includes(call.caller)) {
1466
+ throw new WaitRefError(`delegate call ${ref.id} belongs to another caller`);
1467
+ }
1468
+ if (call.state === "refused") {
1469
+ throw new WaitRefError(
1470
+ `delegate call ${ref.id} was refused (it never started): reissue it, mu_delegate_reissue`
1471
+ );
1472
+ }
1473
+ }
1474
+ }
1475
+ const umbrella = opts.blockersOf === void 0 ? void 0 : taskIdFor(db, opts.blockersOf.id, opts.blockersOf.workstream);
1476
+ if (opts.blockersOf !== void 0 && umbrella === null) {
1477
+ throw new TaskNotFoundError(`${opts.blockersOf.workstream}/${opts.blockersOf.id}`);
1478
+ }
1479
+ const explicit = /* @__PURE__ */ new Map();
1480
+ for (const r of input) explicit.set(refKey(r), r);
1481
+ if (opts.wave !== void 0 && callers.length === 0) {
1482
+ throw new WaitRefError("wait: a wave belongs to callers; none given (no all-caller fallback)");
1483
+ }
1484
+ if (input.length === 0 && !opts.wave && !opts.blockersOf && callers.length === 0) {
1485
+ throw new WaitNothingError();
1486
+ }
1487
+ if (opts.wave !== void 0) {
1488
+ const mine = listDelegatesOf(db, callers, opts.wave.workstream);
1489
+ if (!mine.some((c) => c.wave === opts.wave?.name)) {
1490
+ throw new WaveNotFoundError(
1491
+ opts.wave.name,
1492
+ mine.map((c) => c.wave)
1493
+ );
1494
+ }
1495
+ }
1496
+ const begun = /* @__PURE__ */ new Map();
1497
+ if (consumes) {
1498
+ for (const c of callers) {
1499
+ const b = beginWait(db, c);
1500
+ if (b !== void 0) begun.set(c, b);
1501
+ }
1502
+ }
1503
+ const cursorsBy = new Map(
1504
+ (consumes ? callers : []).map((c) => [c, readCursors(db, c)])
1505
+ );
1506
+ const ownerOfKey = /* @__PURE__ */ new Map();
1507
+ const baseOfCkey = /* @__PURE__ */ new Map();
1508
+ const urgentKey = (kind, key) => {
1509
+ const k = `${kind}|${key}`;
1510
+ baseOfCkey.set(k, key);
1511
+ return k;
1512
+ };
1513
+ const ownerOf = (key) => ownerOfKey.get(baseOfCkey.get(key) ?? key) ?? callers[0];
1514
+ const cursorOf = (key) => {
1515
+ const o = ownerOf(key);
1516
+ return o === void 0 ? void 0 : cursorsBy.get(o)?.get(key);
1517
+ };
1518
+ const baselineOf = /* @__PURE__ */ new Map();
1519
+ const ownedKeys = /* @__PURE__ */ new Set();
1520
+ const watchAbort = new AbortController();
1521
+ let stopReason;
1522
+ let stopNow = () => {
1523
+ };
1524
+ const stopP = new Promise((resolve4) => {
1525
+ stopNow = (why) => {
1526
+ stopReason ??= why;
1527
+ resolve4();
1528
+ };
1529
+ });
1530
+ const onAbort = () => {
1531
+ watchAbort.abort();
1532
+ stopNow("abort");
1533
+ };
1534
+ if (opts.signal?.aborted) onAbort();
1535
+ else opts.signal?.addEventListener("abort", onAbort, { once: true });
1536
+ const deadlineTimer = deadline === Number.POSITIVE_INFINITY ? void 0 : setTimeout(() => stopNow("deadline"), Math.max(0, deadline - Date.now()));
1537
+ const within = (p) => Promise.race([
1538
+ p.then((v) => ({ lost: false, v })),
1539
+ stopP.then(() => ({ lost: true }))
1540
+ ]);
1541
+ let fatal;
1542
+ let wake = () => {
1543
+ };
1544
+ let woken = new Promise((resolve4) => {
1545
+ wake = resolve4;
1546
+ });
1547
+ const tracks = /* @__PURE__ */ new Map();
1548
+ const ownerReadings = /* @__PURE__ */ new Map();
1549
+ const openLocal = /* @__PURE__ */ new Set();
1550
+ const priorTask = /* @__PURE__ */ new Map();
1551
+ let firstRefresh = true;
1552
+ const delivered = [];
1553
+ const discoverSpawned = () => {
1554
+ if (!discover) return;
1555
+ const rows = db.prepare(
1556
+ `SELECT a.name AS agent, a.id AS agentId, a.spawner AS spawner, aws.name AS agentWs,
1557
+ t.local_id AS task, tws.name AS taskWs
1558
+ FROM agents a
1559
+ JOIN workstreams aws ON aws.id = a.workstream_id
1560
+ JOIN tasks t ON t.owner_id = a.id AND t.status = 'IN_PROGRESS' AND t.kind = 'work'
1561
+ JOIN workstreams tws ON tws.id = t.workstream_id AND tws.torn_down_at IS NULL
1562
+ WHERE a.spawner IN (${callers.map(() => "?").join(",")})
1563
+ ORDER BY a.id, t.id`
1564
+ ).all(...callers);
1565
+ const bySpawner = /* @__PURE__ */ new Map();
1566
+ for (const r of rows) {
1567
+ if (!inScope(r.taskWs) && !inScope(r.agentWs)) continue;
1568
+ const list = bySpawner.get(r.spawner) ?? [];
1569
+ list.push(
1570
+ {
1571
+ kind: "agent",
1572
+ workstream: r.agentWs,
1573
+ id: r.agent,
1574
+ baseline: { afterRuns: 0, agentId: r.agentId }
1575
+ },
1576
+ { kind: "task", workstream: r.taskWs, id: r.task }
1577
+ );
1578
+ bySpawner.set(r.spawner, list);
1579
+ }
1580
+ for (const [spawner, refs] of bySpawner) {
1581
+ const reg = readWaitRegistration(db, spawner);
1582
+ const have = new Set((reg?.refs ?? []).map((r) => refKey(r)));
1583
+ const fresh = refs.filter((r) => !have.has(refKey(r)));
1584
+ if (fresh.length === 0) continue;
1585
+ try {
1586
+ registerOwnedWait(db, spawner, fresh);
1587
+ } catch {
1588
+ }
1589
+ }
1590
+ };
1591
+ const ownedRefs = () => {
1592
+ if (!consumes) return [];
1593
+ const out = /* @__PURE__ */ new Map();
1594
+ ownedKeys.clear();
1595
+ for (const c of callers) {
1596
+ const reg = readWaitRegistration(db, c);
1597
+ if (reg === void 0) continue;
1598
+ const done = reg.refs.filter(
1599
+ (r) => r.kind === "agent" && !agentExists(db, r) && hasDoneMark(db, c, r)
1600
+ );
1601
+ if (done.length > 0) dropOwnedRefs(db, c, done);
1602
+ for (const r of reg.refs) {
1603
+ if (done.includes(r)) continue;
1604
+ if (r.kind !== "delegate" && !inScope(r.workstream)) continue;
1605
+ const key = refKey(r);
1606
+ ownedKeys.add(key);
1607
+ if (!out.has(key)) {
1608
+ out.set(key, { ...r });
1609
+ ownerOfKey.set(key, c);
1610
+ }
1611
+ const b = reg.baselines[key];
1612
+ if (b !== void 0) {
1613
+ const old = baselineOf.get(key);
1614
+ if (old === void 0 || old.incarnation === b.incarnation && b.afterRuns < old.afterRuns) {
1615
+ baselineOf.set(key, b);
1616
+ }
1617
+ }
1618
+ }
1619
+ }
1620
+ return [...out.values()];
1621
+ };
1622
+ const track = (ref, isExplicit) => {
1623
+ const key = refKey(ref);
1624
+ let t = tracks.get(key);
1625
+ if (t === void 0) {
1626
+ t = {
1627
+ ref,
1628
+ explicit: isExplicit,
1629
+ wasBusy: false,
1630
+ baseline: ref.afterRuns,
1631
+ handle: void 0,
1632
+ needWatch: opts.watchAgent !== void 0,
1633
+ result: void 0,
1634
+ reading: void 0,
1635
+ latched: void 0,
1636
+ close: void 0,
1637
+ muted: false,
1638
+ unknownBaseline: false
1639
+ };
1640
+ tracks.set(key, t);
1641
+ } else if (isExplicit) t.explicit = true;
1642
+ return t;
1643
+ };
1644
+ const setupWatches = async () => {
1645
+ const watch = opts.watchAgent;
1646
+ const want = [...tracks.values()].filter((t) => t.needWatch && t.latched === void 0);
1647
+ if (watch === void 0 || want.length === 0) return;
1648
+ for (const t of want) t.needWatch = false;
1649
+ const got = await within(
1650
+ Promise.all(
1651
+ want.map((t) => {
1652
+ const key = refKey(t.ref);
1653
+ const cur = cursorOf(key);
1654
+ const base = baselineOf.get(key);
1655
+ return watch(
1656
+ { ...t.ref, ...t.baseline !== void 0 ? { afterRuns: t.baseline } : {} },
1657
+ watchAbort.signal,
1658
+ {
1659
+ owed: ownedKeys.has(key),
1660
+ ...cur !== void 0 ? { cursor: cur } : {},
1661
+ ...base !== void 0 ? { baseline: base } : {}
1662
+ }
1663
+ );
1664
+ })
1665
+ )
1666
+ );
1667
+ if (got.lost) return;
1668
+ got.v.forEach((h, i) => {
1669
+ const t = want[i];
1670
+ if (t === void 0 || h === void 0) return;
1671
+ t.handle = h;
1672
+ t.reading = h.initial;
1673
+ if (t.baseline === void 0 && h.afterRuns !== void 0) t.baseline = h.afterRuns;
1674
+ if (h.unknownBaseline !== void 0) {
1675
+ t.unknownBaseline = true;
1676
+ const key = refKey(t.ref);
1677
+ const owner = ownerOf(key);
1678
+ if (owner !== void 0 && consumes)
1679
+ fenceAgentRef(db, owner, registered(t.ref), h.unknownBaseline);
1680
+ }
1681
+ if (h.initial.status === "busy") t.wasBusy = true;
1682
+ void h.settled.then(
1683
+ (r) => {
1684
+ if (t.handle === h) t.result = r;
1685
+ wake();
1686
+ },
1687
+ (error) => {
1688
+ fatal ??= { error };
1689
+ wake();
1690
+ }
1691
+ );
1692
+ });
1693
+ };
1694
+ const settle = (t, r) => {
1695
+ const consumed = !t.explicit && r.runs !== void 0 && isConsumed(cursorOf(refKey(t.ref)), { incarnation: r.incarnation ?? "", runs: r.runs });
1696
+ const close = consumed ? void 0 : closeSettledRun(db, t.ref, r, applyClose);
1697
+ const textOk = matcher === void 0 || r.lastError === void 0 && (r.lastText ?? "").trim() !== "" && matcher.test(r.lastText ?? "");
1698
+ if (!textOk || consumed) {
1699
+ if (r.runs !== void 0) t.baseline = r.runs;
1700
+ t.wasBusy = false;
1701
+ t.handle = void 0;
1702
+ t.result = void 0;
1703
+ t.needWatch = opts.watchAgent !== void 0;
1704
+ return;
1705
+ }
1706
+ t.latched = { kind: "settled", reading: r };
1707
+ if (close !== void 0) t.close = close;
1708
+ };
1709
+ const rearm = (t, baseline) => rearmLatch(t, baseline, opts.watchAgent !== void 0);
1710
+ const advance = (t, r, fromWatch) => {
1711
+ if (t.latched !== void 0) return;
1712
+ const pinned = t.ref.incarnation;
1713
+ if (pinned !== void 0 && r.incarnation !== void 0 && r.incarnation !== pinned) {
1714
+ throw new AgentRestartedError(t.ref.id, t.ref.workstream, pinned, r.incarnation);
1715
+ }
1716
+ if (r.blocked === void 0 && t.muted) {
1717
+ t.muted = false;
1718
+ if (opts.watchAgent !== void 0 && r.status !== null) {
1719
+ t.needWatch = true;
1720
+ return;
1721
+ }
1722
+ }
1723
+ if (r.exit !== void 0) {
1724
+ t.latched = { kind: "exited", reading: r };
1725
+ } else if (r.status === null) {
1726
+ t.latched = { kind: "dead", reading: r };
1727
+ } else if (r.blocked !== void 0) {
1728
+ if (!t.explicit && isConsumed(cursorOf(urgentKey("input", refKey(t.ref))), inputToken(r))) {
1729
+ t.muted = true;
1730
+ t.wasBusy = true;
1731
+ t.handle = void 0;
1732
+ t.result = void 0;
1733
+ t.needWatch = false;
1734
+ } else t.latched = { kind: "input", reading: r };
1735
+ } else if (!fromWatch && matcher !== void 0 && r.textSource === "pane" && r.lastText !== void 0 && matcher.test(r.lastText)) {
1736
+ t.latched = { kind: "pane-text", reading: r };
1737
+ } else if (fromWatch) {
1738
+ if (r.status === "unknown") t.handle = void 0;
1739
+ else settle(t, r);
1740
+ } else if (r.status === "busy") {
1741
+ t.wasBusy = true;
1742
+ } else if (r.status !== "unknown" && t.wasBusy) {
1743
+ settle(t, r);
1744
+ }
1745
+ };
1746
+ const ownerOfAgent = (t) => ({
1747
+ name: t.ref.id,
1748
+ workstreamName: t.ref.workstream
1749
+ });
1750
+ const markOwed = () => {
1751
+ if (!consumes) return;
1752
+ for (const t of tracks.values()) {
1753
+ const key = refKey(t.ref);
1754
+ const caller = ownerOfKey.get(key);
1755
+ const r = t.latched === void 0 ? ownerReadings.get(agentKey(ownerOfAgent(t))) : void 0;
1756
+ if (caller === void 0 || !ownedKeys.has(key) || r === void 0) continue;
1757
+ const id = agentRowId(db, t.ref);
1758
+ if (id === 0) continue;
1759
+ const owed = agentOwed(db, id, r, baselineOf.get(key), t.unknownBaseline, t.wasBusy);
1760
+ if (owed !== "unsure")
1761
+ setAgentDone(db, t.ref.workstream, t.ref.id, owed === "done", [caller]);
1762
+ }
1763
+ };
1764
+ const refresh = async () => {
1765
+ if (opts.beforePoll) {
1766
+ const b = await within(opts.beforePoll());
1767
+ if (b.lost) return;
1768
+ }
1769
+ if (opts.sync !== false) await ambientIngest(db, { quiet: true });
1770
+ discoverSpawned();
1771
+ const owned = ownedRefs();
1772
+ for (const r of explicit.values()) if (r.kind === "agent") track(r, true);
1773
+ for (const r of owned) if (r.kind === "agent") track(r, explicit.has(refKey(r)));
1774
+ await setupWatches();
1775
+ if (stopReason !== void 0) return;
1776
+ const reads = /* @__PURE__ */ new Map();
1777
+ for (const t of tracks.values()) {
1778
+ if (t.latched === void 0 && t.handle === void 0 || t.latched?.kind === "settled") {
1779
+ reads.set(refKey(t.ref), t.ref);
1780
+ }
1781
+ }
1782
+ if (consumes && opts.readAgents !== void 0) {
1783
+ for (const r of owned) {
1784
+ const t = r.kind === "agent" ? tracks.get(refKey(r)) : void 0;
1785
+ if (t !== void 0 && t.latched === void 0) reads.set(refKey(r), r);
1786
+ }
1787
+ }
1788
+ if (observeOwners) {
1789
+ const taskRefs = [...explicit.values(), ...owned].filter((r) => r.kind === "task");
1790
+ for (const r of taskRefs) {
1791
+ const row = getTask(db, r.id, r.workstream);
1792
+ const o = row?.status === "IN_PROGRESS" ? getTaskOwner(db, r.id, r.workstream) : void 0;
1793
+ if (o !== void 0) {
1794
+ const ref = { kind: "agent", workstream: o.workstreamName, id: o.name };
1795
+ reads.set(refKey(ref), ref);
1796
+ }
1797
+ }
1798
+ }
1799
+ for (const t of tracks.values()) {
1800
+ const r = t.result;
1801
+ if (r === void 0 || t.handle === void 0) continue;
1802
+ t.result = void 0;
1803
+ t.reading = r;
1804
+ advance(t, r, true);
1805
+ }
1806
+ if (reads.size > 0) {
1807
+ if (opts.readAgents === void 0) throw new Error("wait: an agent reader is required");
1808
+ const got = await within(opts.readAgents([...reads.values()], watchAbort.signal));
1809
+ if (got.lost) return;
1810
+ ownerReadings.clear();
1811
+ for (const ref of reads.values()) {
1812
+ const key = agentKey({ name: ref.id, workstreamName: ref.workstream });
1813
+ let r = got.v.get(key);
1814
+ if (r === void 0) {
1815
+ r = watchDeaths && !agentExists(db, ref) ? { status: null, reason: "agent is no longer registered" } : { status: "unknown", reason: "state reader returned no row" };
1816
+ }
1817
+ ownerReadings.set(key, r);
1818
+ const t = tracks.get(refKey(ref));
1819
+ const held = t === void 0 ? void 0 : supersededBy(t, r);
1820
+ if (t !== void 0 && held !== void 0) {
1821
+ rearm(t, held);
1822
+ if (t.needWatch) continue;
1823
+ t.wasBusy = true;
1824
+ }
1825
+ if (t !== void 0 && t.latched === void 0 && t.handle === void 0) {
1826
+ t.reading = r;
1827
+ advance(t, r, false);
1828
+ }
1829
+ }
1830
+ }
1831
+ markOwed();
1832
+ if (firstRefresh) {
1833
+ firstRefresh = false;
1834
+ const watched = [...tracks.values()].filter((t) => t.explicit);
1835
+ if (watched.length > 0 && watched.every((t) => (t.latched?.reading ?? t.reading)?.status === "unknown")) {
1836
+ opts.onInitialUnknown?.(watched.map((t) => agentSnapshot(t)));
1837
+ }
1838
+ }
1839
+ };
1840
+ const ctlBrokenSince = /* @__PURE__ */ new Map();
1841
+ const stallEmitted = /* @__PURE__ */ new Set();
1842
+ const stallStates = /* @__PURE__ */ new Map();
1843
+ const stallAgeMs = (ownerKey, r) => {
1844
+ if (r === void 0 || r.status === null) return null;
1845
+ const alive = true;
1846
+ if (alive && (r.ctl === "missing" || r.ctl === "refused")) {
1847
+ const first = ctlBrokenSince.get(ownerKey) ?? Date.now();
1848
+ ctlBrokenSince.set(ownerKey, first);
1849
+ const age2 = Date.now() - first;
1850
+ return age2 >= stuckAfter(false) ? age2 : null;
1851
+ }
1852
+ ctlBrokenSince.delete(ownerKey);
1853
+ if (r.status !== "needs_input" || r.since === void 0 || r.since === null) return null;
1854
+ const viaCtl = r.source === "ctl" && r.ctl === "ok";
1855
+ if (viaCtl && r.pending === true) return null;
1856
+ const age = Date.now() - r.since;
1857
+ return age >= stuckAfter(viaCtl) ? age : null;
1858
+ };
1859
+ const markStall = (item, stall) => {
1860
+ const r = stall.owner;
1861
+ item.snap.stuck = true;
1862
+ item.snap.stuckWhy = r.ctl === "missing" || r.ctl === "refused" ? r.reason ?? `ctl ${r.ctl}` : r.status ?? "needs_input";
1863
+ item.snap.stuckSecs = stall.ageSecs;
1864
+ if (r.runs !== void 0) item.snap.ownerRuns = r.runs;
1865
+ item.kind = "stall";
1866
+ item.ckey = urgentKey("stall", item.key);
1867
+ item.token = { incarnation: `stall:${stall.token}`, runs: 1 };
1868
+ item.stall = stall;
1869
+ item.snap.owner ??= stall.ownerRef.name;
1870
+ item.snap.ownerWorkstream ??= stall.ownerRef.workstreamName;
1871
+ if (stall.close !== void 0) item.snap.close = stall.close;
1872
+ };
1873
+ const taskItem = (ref, want, isTarget, suppressible, order) => {
1874
+ const row = getTask(db, ref.id, ref.workstream);
1875
+ const status = row?.status ?? "OPEN";
1876
+ const ownerRef = row === void 0 ? void 0 : getTaskOwner(db, ref.id, ref.workstream);
1877
+ const ownerName = ownerRef?.name ?? row?.ownerName ?? null;
1878
+ const snap = {
1879
+ ref,
1880
+ status,
1881
+ substate: row?.substate ?? DEFAULT_SUBSTATE.OPEN,
1882
+ owner: ownerName,
1883
+ ...ownerRef !== void 0 ? { ownerWorkstream: ownerRef.workstreamName } : {},
1884
+ fired: false,
1885
+ dead: false
1886
+ };
1887
+ const item = {
1888
+ key: refKey(ref),
1889
+ ref,
1890
+ snap,
1891
+ target: isTarget,
1892
+ suppressible,
1893
+ order,
1894
+ done: false,
1895
+ ready: false
1896
+ };
1897
+ if (row === void 0) return item;
1898
+ if (watchDeaths && want === "CLOSED" && status !== "CLOSED") {
1899
+ const prior = priorTask.get(item.key);
1900
+ const ownerKey = ownerRef === void 0 ? void 0 : agentKey(ownerRef);
1901
+ const flip = status === "OPEN" ? reapFlip(db, ref, ownedKeys.has(item.key) ? 0 : sinceSeq) : void 0;
1902
+ const dead = ownerKey !== void 0 && ownerReadings.get(ownerKey)?.status === null || // The reaper flip seen live: IN_PROGRESS -> OPEN while the owner's row (that very row) is
1903
+ // gone. A release leaves the owner registered; a replaced agent is another row.
1904
+ prior?.status === "IN_PROGRESS" && status === "OPEN" && prior.owner !== null && prior.ownerId !== 0 && agentRowId(db, { workstream: prior.ownerWs, id: prior.owner }) !== prior.ownerId || flip !== void 0;
1905
+ if (dead) {
1906
+ snap.dead = true;
1907
+ item.kind = "dead";
1908
+ item.done = true;
1909
+ item.ckey = urgentKey("dead", item.key);
1910
+ const rowId = ownerRef !== void 0 ? taskOwnerRowId(db, ref) : prior?.ownerId || flip?.ownerId || 0;
1911
+ item.token = { incarnation: `dead:${rowId}`, runs: 1 };
1912
+ const name = ownerRef?.name ?? prior?.owner ?? flip?.ownerName;
1913
+ const workstream = ownerRef?.workstreamName ?? (prior?.owner != null ? prior.ownerWs : void 0);
1914
+ item.death = {
1915
+ rowId,
1916
+ ...name != null ? { name } : {},
1917
+ ...workstream !== void 0 ? { workstream } : {}
1918
+ };
1919
+ return item;
1920
+ }
1921
+ }
1922
+ if (stuckEnabled && status === "IN_PROGRESS" && row.kind === "work" && ownerRef !== void 0 && want === "CLOSED") {
1923
+ const ownerKey = agentKey(ownerRef);
1924
+ const r = ownerReadings.get(ownerKey);
1925
+ const watched = tracks.get(
1926
+ refKey({ kind: "agent", workstream: ownerRef.workstreamName, id: ownerRef.name })
1927
+ );
1928
+ const settleOwed = watched !== void 0 && // the settle is in hand but not delivered yet: under --first it goes first, the stall
1929
+ // (if the task is still open after its CLOSE block) is the NEXT wait's news. Under --all
1930
+ // a settle is no target and is never delivered before the end: its CLOSE is already
1931
+ // applied (settle()), so a task it left open (a malformed block) is a stall NOW.
1932
+ (!all && watched.latched?.kind === "settled" || watched.latched === void 0 && (watched.handle !== void 0 || watched.needWatch) && r?.runs !== void 0 && (watched.baseline ?? 0) < r.runs);
1933
+ const age = settleOwed ? null : stallAgeMs(ownerKey, r);
1934
+ if (age !== null && r !== void 0) {
1935
+ const token = [
1936
+ row.workstreamId,
1937
+ row.name,
1938
+ ownerKey,
1939
+ r.incarnation ?? "",
1940
+ r.runs ?? "",
1941
+ r.since ?? ""
1942
+ ].join("|");
1943
+ markStall(item, {
1944
+ token,
1945
+ owner: r,
1946
+ ownerRef,
1947
+ ageSecs: Math.round(age / 1e3),
1948
+ // The owner's settled run was refused its CLOSE (a malformed block): say why on the task.
1949
+ ...watched?.close?.applied === false ? { close: watched.close } : {}
1950
+ });
1951
+ if (!stallEmitted.has(token)) {
1952
+ stallEmitted.add(token);
1953
+ const identity = {
1954
+ agentId: r.agentId ?? taskOwnerRowId(db, ref),
1955
+ incarnation: `${r.incarnation ?? ""}${r.runs === void 0 ? `|since:${r.since ?? ""}` : ""}`,
1956
+ runs: r.runs ?? 0
1957
+ };
1958
+ logStallOnce(
1959
+ db,
1960
+ ref,
1961
+ identity,
1962
+ () => emitEvent(
1963
+ db,
1964
+ ref.workstream,
1965
+ "agent.stall",
1966
+ `agent stalled ${ownerRef.name} owns ${ref.id} for ${Math.round(age / 1e3)}s`,
1967
+ ownerRef.name
1968
+ )
1969
+ );
1970
+ }
1971
+ return item;
1972
+ }
1973
+ }
1974
+ if (status !== want) return item;
1975
+ if (matcher !== void 0) {
1976
+ const rep = db.prepare(
1977
+ `SELECT n.content FROM task_notes n JOIN tasks t ON t.id = n.task_id
1978
+ JOIN workstreams w ON w.id = t.workstream_id
1979
+ WHERE w.name = ? AND t.local_id = ? AND n.kind = 'report' ORDER BY n.id DESC LIMIT 1`
1980
+ ).get(ref.workstream, ref.id);
1981
+ if (row.substate !== "done" || rep === void 0 || !matcher.test(rep.content)) return item;
1982
+ }
1983
+ const note = db.prepare(
1984
+ `SELECT COALESCE(MAX(n.id), 0) AS n FROM task_notes n JOIN tasks t ON t.id = n.task_id
1985
+ JOIN workstreams w ON w.id = t.workstream_id WHERE w.name = ? AND t.local_id = ?`
1986
+ ).get(ref.workstream, ref.id);
1987
+ snap.fired = true;
1988
+ item.kind = "closed";
1989
+ item.done = true;
1990
+ item.token = closedToken(note.n);
1991
+ return item;
1992
+ };
1993
+ const agentItem = (t, isTarget, order) => {
1994
+ const l = t.latched;
1995
+ const r = l?.reading;
1996
+ const key = refKey(t.ref);
1997
+ let token;
1998
+ let ckey;
1999
+ let death;
2000
+ if (l?.kind === "settled" && r?.runs !== void 0) {
2001
+ token = { incarnation: r.incarnation ?? "", runs: r.runs };
2002
+ } else if (l?.kind === "input" && r !== void 0) {
2003
+ ckey = urgentKey("input", key);
2004
+ token = inputToken(r);
2005
+ } else if (l?.kind === "dead") {
2006
+ ckey = urgentKey("dead", key);
2007
+ const rowId = agentRowId(db, t.ref);
2008
+ token = { incarnation: `dead:${rowId}`, runs: 1 };
2009
+ death = { rowId: rowId || (r?.agentId ?? 0), name: t.ref.id, workstream: t.ref.workstream };
2010
+ } else if (l?.kind === "exited") {
2011
+ token = { incarnation: `exited:${agentRowId(db, t.ref)}`, runs: 1 };
2012
+ }
2013
+ return {
2014
+ key,
2015
+ ref: t.ref,
2016
+ snap: agentSnapshot(t),
2017
+ target: isTarget,
2018
+ suppressible: !t.explicit,
2019
+ order,
2020
+ done: l?.kind === "settled" || l?.kind === "pane-text" || l?.kind === "dead" || l?.kind === "exited",
2021
+ ready: false,
2022
+ ...l !== void 0 ? { kind: l.kind } : {},
2023
+ ...token !== void 0 ? { token } : {},
2024
+ ...ckey !== void 0 ? { ckey } : {},
2025
+ ...death !== void 0 ? { death } : {}
2026
+ };
2027
+ };
2028
+ const OPEN_TOKEN = { incarnation: "open", runs: 1 };
2029
+ const markOpen = (okey) => {
2030
+ if (cursorOf(okey) !== void 0) return;
2031
+ const owner = ownerOf(okey);
2032
+ if (owner === void 0) return;
2033
+ recordCursors(db, owner, [{ key: okey, token: OPEN_TOKEN }]);
2034
+ cursorsBy.get(owner)?.set(okey, OPEN_TOKEN);
2035
+ };
2036
+ let tally;
2037
+ const evaluate = () => {
2038
+ const items = [];
2039
+ const byKey = /* @__PURE__ */ new Map();
2040
+ let order = 0;
2041
+ const push = (it) => {
2042
+ const prev = byKey.get(it.key);
2043
+ if (prev !== void 0) {
2044
+ prev.target ||= it.target;
2045
+ prev.suppressible &&= it.suppressible;
2046
+ return;
2047
+ }
2048
+ byKey.set(it.key, it);
2049
+ items.push(it);
2050
+ };
2051
+ const owned = ownedRefs();
2052
+ const calls = /* @__PURE__ */ new Map();
2053
+ const addCall = (c, isTarget, suppressible) => {
2054
+ if (c === void 0 || c.state === "refused") return;
2055
+ const prev = calls.get(c.callId);
2056
+ calls.set(c.callId, {
2057
+ call: c,
2058
+ target: (prev?.target ?? false) || isTarget,
2059
+ suppressible: (prev?.suppressible ?? true) && suppressible
2060
+ });
2061
+ };
2062
+ for (const r of input) {
2063
+ if (r.kind === "task") push(taskItem(r, target, true, false, order++));
2064
+ else if (r.kind === "agent") push(agentItem(track(r, true), true, order++));
2065
+ else addCall(getDelegateCall(db, r.id), true, false);
2066
+ }
2067
+ if (opts.wave !== void 0) {
2068
+ const waveCalls = listDelegatesOf(db, callers, opts.wave.workstream, {
2069
+ wave: opts.wave.name
2070
+ });
2071
+ for (const c of waveCalls) addCall(c, true, !all);
2072
+ tally = tallyCalls(waveCalls);
2073
+ }
2074
+ for (const r of owned) {
2075
+ if (r.kind !== "delegate") continue;
2076
+ const c = getDelegateCall(db, r.id);
2077
+ if (c !== void 0 && callInScope(c)) addCall(c, false, true);
2078
+ }
2079
+ if (consumes) {
2080
+ for (const c of listDelegatesOf(db, callers)) {
2081
+ if (!callInScope(c)) continue;
2082
+ addCall(c, false, true);
2083
+ ownerOfKey.set(
2084
+ refKey({ kind: "delegate", workstream: c.workstream, id: c.callId }),
2085
+ c.caller
2086
+ );
2087
+ }
2088
+ }
2089
+ for (const { call, target: isTarget, suppressible } of calls.values()) {
2090
+ push(delegateItem(call, isTarget, suppressible, order++));
2091
+ }
2092
+ if (opts.blockersOf !== void 0 && umbrella !== void 0 && umbrella !== null) {
2093
+ const rows = openBlockers(db, umbrella);
2094
+ let open = false;
2095
+ let readyBlocker = false;
2096
+ for (const b of rows) {
2097
+ const ref = { kind: "task", workstream: b.ws, id: b.id };
2098
+ const it = taskItem(ref, "CLOSED", true, !all, order++);
2099
+ const okey = urgentKey("open", it.key);
2100
+ if (!it.done) {
2101
+ open = true;
2102
+ if (consumes) markOpen(okey);
2103
+ else openLocal.add(it.key);
2104
+ } else if (!all && !(consumes ? cursorOf(okey) !== void 0 : openLocal.has(it.key))) {
2105
+ it.suppressible = true;
2106
+ it.token = void 0;
2107
+ it.kind = void 0;
2108
+ } else if (it.token === void 0 || !isConsumed(cursorOf(it.key), it.token)) {
2109
+ readyBlocker = true;
2110
+ }
2111
+ push(it);
2112
+ }
2113
+ if (!open && !readyBlocker) {
2114
+ const ref = { kind: "task", ...opts.blockersOf };
2115
+ push({
2116
+ key: `blockers:${refKey(ref)}`,
2117
+ ref,
2118
+ snap: { ref, status: "CLOSED", fired: true, dead: false },
2119
+ target: true,
2120
+ suppressible: false,
2121
+ order: order++,
2122
+ kind: "closed",
2123
+ done: true,
2124
+ ready: false,
2125
+ // A state ("nothing is left to wait on"), never news: behind every real event.
2126
+ seen: true
2127
+ });
2128
+ }
2129
+ }
2130
+ for (const r of owned) {
2131
+ if (r.kind === "task") push(taskItem(r, target, false, true, order++));
2132
+ }
2133
+ for (const r of owned) {
2134
+ if (r.kind === "agent") push(agentItem(track(r, false), false, order++));
2135
+ }
2136
+ for (const it of items) {
2137
+ if (it.ref.kind !== "task") continue;
2138
+ const owner = it.snap.owner ?? null;
2139
+ const before = priorTask.get(it.key);
2140
+ const keep = owner === null && it.snap.status === "OPEN" ? before : void 0;
2141
+ priorTask.set(it.key, {
2142
+ status: it.snap.status ?? "OPEN",
2143
+ owner: owner ?? keep?.owner ?? null,
2144
+ ownerWs: it.snap.ownerWorkstream ?? keep?.ownerWs ?? it.ref.workstream,
2145
+ ownerId: owner === null ? keep?.ownerId ?? 0 : taskOwnerRowId(db, it.ref)
2146
+ });
2147
+ }
2148
+ const shown = [];
2149
+ for (const it of items) {
2150
+ const consumed = it.token !== void 0 && isConsumed(cursorOf(it.ckey ?? it.key), it.token);
2151
+ it.seen ||= consumed;
2152
+ const hidden = it.suppressible && consumed;
2153
+ it.hidden = hidden;
2154
+ it.ready = it.kind !== void 0 && !hidden;
2155
+ const urgent = it.kind === "input" || it.kind === "stall" || it.kind === "dead";
2156
+ if (hidden && urgent) it.done = false;
2157
+ if (hidden && !it.target && !urgent) continue;
2158
+ shown.push(it);
2159
+ }
2160
+ return shown;
2161
+ };
2162
+ const driveStalls = (items) => {
2163
+ const handler = opts.handleStall;
2164
+ for (const st of stallStates.values()) {
2165
+ if (st.job !== void 0 || st.result?.disposition !== "return" || st.carry === void 0) {
2166
+ continue;
2167
+ }
2168
+ const { key, stall } = st.carry;
2169
+ const gone = items.find((i) => i.key === key && i.kind !== "stall");
2170
+ if (gone !== void 0 && stall !== void 0) {
2171
+ markStall(gone, stall);
2172
+ gone.hidden = false;
2173
+ }
2174
+ }
2175
+ for (const it of items) {
2176
+ if (it.stall === void 0 || it.kind !== "stall" || it.hidden === true) continue;
2177
+ if (opts.onStall === "warn") {
2178
+ it.ready = false;
2179
+ continue;
2180
+ }
2181
+ const steering = opts.onStall === "steer" || opts.onStall === "release";
2182
+ const caller = ownerOf(it.key);
2183
+ if (!steering || handler === void 0 || caller === void 0) {
2184
+ it.ready = true;
2185
+ continue;
2186
+ }
2187
+ const { token, owner, ownerRef } = it.stall;
2188
+ let st = stallStates.get(token);
2189
+ if (st === void 0) {
2190
+ const own = db.prepare(
2191
+ `SELECT a.id, a.pane_id FROM agents a JOIN workstreams w ON w.id = a.workstream_id
2192
+ WHERE a.name = ? AND w.name = ?`
2193
+ ).get(ownerRef.name, ownerRef.workstreamName);
2194
+ const taskId = taskIdFor(db, it.ref.id, it.ref.workstream);
2195
+ if (own === void 0 || taskId === null) {
2196
+ it.ready = true;
2197
+ continue;
2198
+ }
2199
+ const ctx = {
2200
+ caller,
2201
+ generation: begun.get(caller)?.generation ?? null,
2202
+ ref: it.ref,
2203
+ taskId,
2204
+ revision: taskRevision(db, it.ref.workstream, it.ref.id),
2205
+ owner: {
2206
+ workstream: ownerRef.workstreamName,
2207
+ name: ownerRef.name,
2208
+ agentId: owner.agentId ?? own.id,
2209
+ paneId: owner.paneId ?? own.pane_id,
2210
+ status: owner.status,
2211
+ source: owner.source ?? "none",
2212
+ since: owner.since ?? null,
2213
+ settled: false,
2214
+ ...owner.ctl !== void 0 ? { ctl: owner.ctl } : {},
2215
+ ...owner.incarnation !== void 0 ? { incarnation: owner.incarnation } : {},
2216
+ ...owner.runs !== void 0 ? { runs: owner.runs } : {},
2217
+ ...owner.pending !== void 0 ? { pending: owner.pending } : {},
2218
+ ...owner.reason !== void 0 ? { reason: owner.reason } : {}
2219
+ },
2220
+ stallToken: token,
2221
+ deadlineMs: Number.isFinite(deadline) ? Math.max(0, deadline - Date.now()) : Number.MAX_SAFE_INTEGER,
2222
+ signal: watchAbort.signal
2223
+ };
2224
+ const fresh = {};
2225
+ const action = opts.onStall === "release" ? "release" : "steer";
2226
+ fresh.job = Promise.resolve().then(() => handler(ctx)).catch(
2227
+ (e) => ({
2228
+ disposition: "return",
2229
+ action,
2230
+ state: "unknown",
2231
+ reason: e instanceof Error ? e.message : String(e)
2232
+ })
2233
+ ).then((res) => {
2234
+ fresh.result = res;
2235
+ fresh.job = void 0;
2236
+ const t = tracks.get(
2237
+ refKey({
2238
+ kind: "agent",
2239
+ ...{ workstream: ownerRef.workstreamName, id: ownerRef.name }
2240
+ })
2241
+ );
2242
+ if (t !== void 0 && res.afterRuns !== void 0) {
2243
+ if (t.latched?.kind === "settled") rearm(t, res.afterRuns);
2244
+ else if (t.latched === void 0) {
2245
+ t.baseline = res.afterRuns;
2246
+ t.wasBusy = false;
2247
+ }
2248
+ }
2249
+ wake();
2250
+ });
2251
+ fresh.carry = { key: it.key, stall: it.stall };
2252
+ st = fresh;
2253
+ stallStates.set(token, st);
2254
+ }
2255
+ it.ready = st.job === void 0 && st.result?.disposition === "return";
2256
+ }
2257
+ };
2258
+ const select2 = (items) => {
2259
+ const rank = (a, b) => (
2260
+ // Never-returned events first: an event that was already returned once (an
2261
+ // unchanged dialog, a dead pane, a stall) never outranks one that was not.
2262
+ Number(a.seen === true) - Number(b.seen === true) || urgency(a.kind) - urgency(b.kind) || // Under --all a failed gate is the firing event, so its code is the exit.
2263
+ Number(failedGate(b)) - Number(failedGate(a)) || Number(b.target) - Number(a.target) || a.order - b.order
2264
+ );
2265
+ const ready = items.filter((i) => i.ready).sort(rank);
2266
+ if (!all) {
2267
+ const best2 = ready[0];
2268
+ if (best2 === void 0) return void 0;
2269
+ const same = (a, b) => a.kind === "settled" && a.ref.kind === "agent" && a.snap.close?.applied === true && a.snap.close.task === b.ref.id && a.ref.workstream === b.ref.workstream && b.ref.kind === "task" && b.kind === "closed";
2270
+ const companions = ready.filter(
2271
+ (i) => i !== best2 && (same(i, best2) || same(best2, i) || sameDeath(i, best2))
2272
+ );
2273
+ return { event: best2, fired: [best2, ...companions] };
2274
+ }
2275
+ const urgent = ready.find(
2276
+ (i) => i.kind === "input" || i.kind === "stall" || !i.target && (i.kind === "dead" || i.kind === "delegate")
2277
+ );
2278
+ if (urgent !== void 0) {
2279
+ const companions = ready.filter((i) => i !== urgent && sameDeath(i, urgent));
2280
+ return { event: urgent, fired: [urgent, ...companions] };
2281
+ }
2282
+ const targets = items.some((i) => i.target) ? items.filter((i) => i.target) : items;
2283
+ if (targets.length === 0 || !targets.every((i) => i.done)) return void 0;
2284
+ const sorted = [...targets].sort(rank);
2285
+ const best = sorted.find((i) => i.ready) ?? sorted[0];
2286
+ return best === void 0 ? void 0 : { event: best, fired: targets.filter((i) => i.done) };
2287
+ };
2288
+ const build = (kind, items, chosen, drained = false) => {
2289
+ const e = chosen?.event;
2290
+ for (const f of chosen?.fired ?? []) delivered.push(f);
2291
+ return {
2292
+ event: {
2293
+ kind,
2294
+ ref: e?.ref ?? null,
2295
+ ...e?.snap.incarnation !== void 0 ? { incarnation: e.snap.incarnation } : {},
2296
+ ...e?.snap.runs !== void 0 ? { runs: e.snap.runs } : {}
2297
+ },
2298
+ refs: snapshotsOf(items, chosen?.fired ?? []),
2299
+ delivered: (chosen?.fired ?? []).filter(isCompletion).map((i) => i.ref),
2300
+ timedOut: kind === "heartbeat" && !drained,
2301
+ interrupted: kind === "interrupted",
2302
+ exit: exitFor(kind) !== 0 ? exitFor(kind) : (chosen?.fired ?? []).find(failedGate)?.snap.exit ?? 0,
2303
+ cursor: {},
2304
+ ...e?.stall !== void 0 && stallStates.get(e.stall.token)?.result !== void 0 ? { action: stallStates.get(e.stall.token)?.result } : {},
2305
+ humanNeeds: callers.flatMap((c) => openHumanNeeds(db, c)),
2306
+ sinceSeq,
2307
+ ...tally !== void 0 ? { tally } : {},
2308
+ ...drained ? { drained: true } : {}
2309
+ };
2310
+ };
2311
+ const rethrow = () => {
2312
+ if (fatal === void 0) return;
2313
+ watchAbort.abort();
2314
+ throw fatal.error;
2315
+ };
2316
+ let committed = false;
2317
+ let applied = true;
2318
+ const commit = (event) => {
2319
+ if (!consumes) return true;
2320
+ const ok = db.transaction(() => {
2321
+ for (const c of callers) cursorsBy.set(c, readCursors(db, c));
2322
+ if (
2323
+ // Only news this wait saw as unconsumed can turn stale; the re-read hides it next time.
2324
+ event?.token !== void 0 && event.ready && event.suppressible && isConsumed(cursorOf(event.ckey ?? event.key), event.token)
2325
+ ) {
2326
+ return false;
2327
+ }
2328
+ const completed = delivered.filter(isCompletion);
2329
+ for (const c of callers) {
2330
+ const mine = completed.filter((i) => ownerOf(i.key) === c);
2331
+ const tokens = delivered.filter((i) => i.token !== void 0 && ownerOf(i.ckey ?? i.key) === c).flatMap(
2332
+ (i) => i.token === void 0 ? [] : [{ key: i.ckey ?? i.key, token: i.token }]
2333
+ );
2334
+ const b = begun.get(c);
2335
+ if (b !== void 0) {
2336
+ const ended = endWait(
2337
+ db,
2338
+ c,
2339
+ b.generation,
2340
+ mine.map((i) => ({
2341
+ ...registered(i.ref),
2342
+ ...i.kind === "settled" && i.snap.runs !== void 0 ? {
2343
+ runs: i.snap.runs,
2344
+ ...i.snap.incarnation !== void 0 ? { incarnation: i.snap.incarnation } : {}
2345
+ } : {}
2346
+ }))
2347
+ );
2348
+ if (!ended.applied) {
2349
+ applied = false;
2350
+ continue;
2351
+ }
2352
+ }
2353
+ recordCursors(db, c, tokens);
2354
+ }
2355
+ return true;
2356
+ }).immediate();
2357
+ if (ok) committed = true;
2358
+ else delivered.length = 0;
2359
+ return ok;
2360
+ };
2361
+ let result;
2362
+ try {
2363
+ for (; ; ) {
2364
+ if (stopReason === "abort") {
2365
+ result = build("interrupted", evaluate());
2366
+ break;
2367
+ }
2368
+ if (stopReason === "deadline" || Date.now() >= deadline) {
2369
+ const items2 = evaluate();
2370
+ driveStalls(items2);
2371
+ const chosen2 = select2(items2);
2372
+ const r = chosen2 ? build(chosen2.event.kind ?? "closed", items2, chosen2) : build("heartbeat", items2);
2373
+ if (!commit(chosen2?.event)) continue;
2374
+ result = r;
2375
+ break;
2376
+ }
2377
+ await refresh();
2378
+ rethrow();
2379
+ if (stopReason === "abort") continue;
2380
+ const items = evaluate();
2381
+ driveStalls(items);
2382
+ opts.onPicture?.(items.map((i) => i.snap));
2383
+ if (items.length === 0) throw new WaitNothingError();
2384
+ const chosen = select2(items);
2385
+ if (chosen !== void 0) {
2386
+ const r = build(chosen.event.kind ?? "closed", items, chosen);
2387
+ if (!commit(chosen.event)) continue;
2388
+ result = r;
2389
+ break;
2390
+ }
2391
+ if (!all && items.every((i) => i.done)) {
2392
+ result = build("heartbeat", items, void 0, true);
2393
+ break;
2394
+ }
2395
+ if (stopReason !== void 0) continue;
2396
+ const polled = watchDeaths || observeOwners || items.some((i) => i.ref.kind !== "agent") || [...tracks.values()].some((t) => t.latched === void 0 && t.handle === void 0);
2397
+ const left = deadline - Date.now();
2398
+ const ms = polled ? Math.min(pollMs, left) : left;
2399
+ if (ms <= 0) continue;
2400
+ let timer;
2401
+ const sleeper = opts.sleep ?? sleepSeam;
2402
+ const slept = !polled ? new Promise(() => {
2403
+ }) : sleeper ? sleeper(ms) : new Promise((resolve4) => {
2404
+ timer = setTimeout(resolve4, ms);
2405
+ });
2406
+ await Promise.race([woken, slept, stopP]);
2407
+ clearTimeout(timer);
2408
+ woken = new Promise((resolve4) => {
2409
+ wake = resolve4;
2410
+ });
2411
+ rethrow();
2412
+ }
2413
+ } finally {
2414
+ clearTimeout(deadlineTimer);
2415
+ opts.signal?.removeEventListener("abort", onAbort);
2416
+ watchAbort.abort();
2417
+ if (consumes) {
2418
+ if (!committed) commit();
2419
+ if (result !== void 0) {
2420
+ result.cursor = Object.fromEntries(callers.flatMap((c) => [...readCursors(db, c)]));
2421
+ if (!applied) result.superseded = true;
2422
+ }
2423
+ } else if (result !== void 0) result.cursor = {};
2424
+ if (opts.sync !== false) await ambientFlush(db, { quiet: true });
2425
+ }
2426
+ return result;
2427
+ }
2428
+
2429
+ // src/wait-action-receipts.ts
2430
+ var fromRaw = (r) => ({
2431
+ taskId: r.task_id,
2432
+ ownerId: r.owner_id,
2433
+ incarnation: r.incarnation,
2434
+ action: r.action,
2435
+ caller: r.caller,
2436
+ taskRef: r.task_ref,
2437
+ ownerRef: r.owner_ref,
2438
+ paneId: r.pane_id,
2439
+ previewRevision: r.preview_revision,
2440
+ msgId: r.msg_id,
2441
+ state: r.state,
2442
+ runs: r.runs,
2443
+ detail: r.detail,
2444
+ createdAt: r.created_at,
2445
+ updatedAt: r.updated_at
2446
+ });
2447
+ var EDGES = {
2448
+ reserved: ["sending", "refused", "released"],
2449
+ sending: ["sent", "unknown", "refused"],
2450
+ unknown: ["sent"],
2451
+ sent: [],
2452
+ refused: [],
2453
+ released: []
2454
+ };
2455
+ function getActionReceipt(db, key) {
2456
+ const row = db.prepare(
2457
+ `SELECT * FROM wait_action_receipts
2458
+ WHERE task_id = ? AND owner_id = ? AND incarnation = ? AND action = ?`
2459
+ ).get(key.taskId, key.ownerId, key.incarnation, key.action);
2460
+ return row === void 0 ? void 0 : fromRaw(row);
2461
+ }
2462
+ function reserveActionReceipt(db, key, info) {
2463
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2464
+ const res = db.prepare(
2465
+ `INSERT OR IGNORE INTO wait_action_receipts
2466
+ (task_id, owner_id, incarnation, action, caller, task_ref, owner_ref, pane_id,
2467
+ preview_revision, msg_id, state, created_at, updated_at)
2468
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'reserved', ?, ?)`
2469
+ ).run(
2470
+ key.taskId,
2471
+ key.ownerId,
2472
+ key.incarnation,
2473
+ key.action,
2474
+ info.caller,
2475
+ info.taskRef,
2476
+ info.ownerRef,
2477
+ info.paneId,
2478
+ info.previewRevision,
2479
+ info.msgId ?? null,
2480
+ now,
2481
+ now
2482
+ );
2483
+ const receipt = getActionReceipt(db, key);
2484
+ if (receipt === void 0) throw new Error("wait action receipt vanished after reservation");
2485
+ return { created: res.changes === 1, receipt };
2486
+ }
2487
+ function transitionActionReceipt(db, key, from, to, patch = {}) {
2488
+ if (!EDGES[from].includes(to)) {
2489
+ throw new Error(`wait action receipt: illegal transition ${from} -> ${to}`);
2490
+ }
2491
+ const res = db.prepare(
2492
+ `UPDATE wait_action_receipts
2493
+ SET state = ?, msg_id = COALESCE(?, msg_id), runs = COALESCE(?, runs),
2494
+ detail = COALESCE(?, detail), updated_at = ?
2495
+ WHERE task_id = ? AND owner_id = ? AND incarnation = ? AND action = ? AND state = ?`
2496
+ ).run(
2497
+ to,
2498
+ patch.msgId ?? null,
2499
+ patch.runs ?? null,
2500
+ patch.detail ?? null,
2501
+ (/* @__PURE__ */ new Date()).toISOString(),
2502
+ key.taskId,
2503
+ key.ownerId,
2504
+ key.incarnation,
2505
+ key.action,
2506
+ from
2507
+ );
2508
+ return res.changes === 1;
2509
+ }
2510
+
2511
+ // src/wait-actions.ts
2512
+ var refused = (reason, action) => ({
2513
+ disposition: "return",
2514
+ action,
2515
+ state: "refused",
2516
+ reason
2517
+ });
2518
+ function guardStall(db, ctx, callers, action) {
2519
+ const no2 = (reason) => ({ ok: false, reason });
2520
+ if (ctx.signal.aborted) return no2("the wait was aborted");
2521
+ if (!callers.includes(ctx.caller)) {
2522
+ return no2(`caller ${ctx.caller} is not one of this process's callers`);
2523
+ }
2524
+ if (ctx.generation !== null) {
2525
+ const reg = readWaitRegistration(db, ctx.caller);
2526
+ if (reg === void 0 || reg.generation !== ctx.generation) {
2527
+ return no2("the wait was superseded (generation changed)");
2528
+ }
2529
+ }
2530
+ const task = db.prepare(
2531
+ `SELECT t.local_id, w.name AS workstream, t.status, t.kind, t.owner_id
2532
+ FROM tasks t JOIN workstreams w ON w.id = t.workstream_id WHERE t.id = ?`
2533
+ ).get(ctx.taskId);
2534
+ if (task === void 0) return no2("the task no longer exists");
2535
+ if (task.local_id !== ctx.ref.id || task.workstream !== ctx.ref.workstream) {
2536
+ return no2("the ref does not name this task");
2537
+ }
2538
+ if (task.kind !== "work") return no2(`a ${task.kind} task is never ${action}d`);
2539
+ if (task.status !== "IN_PROGRESS") return no2(`the task is ${task.status}, not IN_PROGRESS`);
2540
+ if (task.owner_id === null || task.owner_id !== ctx.owner.agentId) {
2541
+ return no2("the task's owner is no longer the stalled agent");
2542
+ }
2543
+ const agent = db.prepare("SELECT a.name, a.pane_id, a.spawner FROM agents a WHERE a.id = ?").get(task.owner_id);
2544
+ if (agent === void 0) return no2("the owner is no longer registered");
2545
+ if (agent.pane_id !== ctx.owner.paneId) return no2("the owner's pane changed (pane mismatch)");
2546
+ if (agent.spawner === null || !callers.includes(agent.spawner)) {
2547
+ return no2("the owner was not spawned by this caller (not yours to act on)");
2548
+ }
2549
+ if (action === "steer") {
2550
+ if (ctx.owner.status !== "needs_input" || ctx.owner.ctl !== "ok") {
2551
+ return no2(`the owner is not idle on a healthy control socket (state ${ctx.owner.status})`);
2552
+ }
2553
+ if (ctx.owner.incarnation === void 0 || ctx.owner.incarnation === "") {
2554
+ return no2("the owner's pi named no incarnation: a steer could not be bound to it");
2555
+ }
2556
+ } else {
2557
+ const brokenCtl = ctx.owner.ctl === "missing" || ctx.owner.ctl === "refused";
2558
+ const needsAttention = ctx.owner.status === "needs_input" || brokenCtl && (ctx.owner.status === "unknown" || ctx.owner.status === "busy");
2559
+ if (!needsAttention) {
2560
+ return no2(`the owner is not waiting on anyone (runtime state ${ctx.owner.status})`);
2561
+ }
2562
+ }
2563
+ if (!autoPermitted(db, ctx.taskId)) {
2564
+ return no2("the task is not permitted for auto action on this machine (mu task auto)");
2565
+ }
2566
+ if (callers.some((c) => openHumanNeeds(db, c).length > 0)) {
2567
+ return no2("a human stop is open for this caller: leave the stall to the human");
2568
+ }
2569
+ const revision = taskRevision(db, task.workstream, task.local_id);
2570
+ if (revision !== ctx.revision) {
2571
+ return no2(
2572
+ `the task changed since the stall was seen (revision ${ctx.revision} -> ${revision})`
2573
+ );
2574
+ }
2575
+ return {
2576
+ ok: true,
2577
+ found: {
2578
+ localId: task.local_id,
2579
+ workstream: task.workstream,
2580
+ ownerId: task.owner_id,
2581
+ ownerName: agent.name,
2582
+ paneId: agent.pane_id
2583
+ }
2584
+ };
2585
+ }
2586
+ var keyOf = (ctx, ownerId, action) => ({
2587
+ taskId: ctx.taskId,
2588
+ ownerId,
2589
+ incarnation: ctx.owner.incarnation ?? "",
2590
+ action
2591
+ });
2592
+ var infoOf = (ctx, f) => ({
2593
+ caller: ctx.caller,
2594
+ taskRef: `${f.workstream}/${f.localId}`,
2595
+ ownerRef: `${ctx.owner.workstream}/${f.ownerName}`,
2596
+ paneId: f.paneId,
2597
+ previewRevision: ctx.revision
2598
+ });
2599
+ function releaseBody(db, ctx, callers) {
2600
+ const g = guardStall(db, ctx, callers, "release");
2601
+ if (!g.ok) return refused(g.reason, "release");
2602
+ const { found } = g;
2603
+ const key = keyOf(ctx, found.ownerId, "release");
2604
+ if (getActionReceipt(db, key) !== void 0) {
2605
+ return {
2606
+ disposition: "return",
2607
+ action: "release",
2608
+ state: "budget-exhausted",
2609
+ reason: "this owner incarnation was already released once for this task"
2610
+ };
2611
+ }
2612
+ const res = releaseTask(db, found.localId, {
2613
+ workstream: found.workstream,
2614
+ expectedOwnerId: found.ownerId,
2615
+ ifRevision: ctx.revision,
2616
+ author: ctx.caller,
2617
+ evidence: `stall: ${found.ownerName} needed attention (stall ${ctx.stallToken}); released for another worker`
2618
+ });
2619
+ if (!res.changed) return refused("the release changed nothing", "release");
2620
+ reserveActionReceipt(db, key, infoOf(ctx, found));
2621
+ transitionActionReceipt(db, key, "reserved", "released", {
2622
+ detail: `released from ${found.ownerName}`
2623
+ });
2624
+ return {
2625
+ disposition: "return",
2626
+ action: "release",
2627
+ state: "released",
2628
+ ...ctx.owner.incarnation !== void 0 ? { incarnation: ctx.owner.incarnation } : {},
2629
+ reason: `released ${found.localId} from ${found.ownerName}`
2630
+ };
2631
+ }
2632
+ async function releaseStall(db, ctx, opts = {}) {
2633
+ const callers = opts.callers ?? currentCallers();
2634
+ try {
2635
+ return withOpContext(
2636
+ db,
2637
+ { intent: "task.release", actor: ctx.caller, group: "new" },
2638
+ () => releaseBody(db, ctx, callers)
2639
+ );
2640
+ } catch (e) {
2641
+ return refused(
2642
+ `release failed, nothing changed: ${e instanceof Error ? e.message : String(e)}`,
2643
+ "release"
2644
+ );
2645
+ }
2646
+ }
2647
+ function releaseStallHandler(db, opts = {}) {
2648
+ return (ctx) => releaseStall(db, ctx, opts);
2649
+ }
2650
+ function steerText(workstream, taskId) {
2651
+ return `[mu] ${workstream}/${taskId} is still yours (IN_PROGRESS) and mu is waiting on it. If the work is done and verified, close it: mu task close ${taskId} -w ${workstream} --evidence "<what you verified>". If you are blocked or need an answer, say so in one line and stop.`;
2652
+ }
2653
+ var steerResult = (state, disposition, extra = {}) => ({ disposition, action: "steer", state, ...extra });
2654
+ function planSteer(db, ctx, callers) {
2655
+ return db.transaction(() => {
2656
+ const key = keyOf(ctx, ctx.owner.agentId, "steer");
2657
+ const have = getActionReceipt(db, key);
2658
+ if (have !== void 0 && have.state !== "reserved") {
2659
+ if (have.state === "sending" || have.state === "unknown") {
2660
+ if (have.msgId === null) {
2661
+ return {
2662
+ kind: "done",
2663
+ result: steerResult("unknown", "return", {
2664
+ reason: "a steer may have been written but its id was lost: not resent"
2665
+ })
2666
+ };
2667
+ }
2668
+ return {
2669
+ kind: "probe",
2670
+ msgId: have.msgId,
2671
+ key,
2672
+ paneId: have.paneId,
2673
+ state: have.state
2674
+ };
2675
+ }
2676
+ return {
2677
+ kind: "done",
2678
+ result: steerResult(have.state === "refused" ? "refused" : "budget-exhausted", "return", {
2679
+ reason: have.state === "refused" ? `pi refused the steer for this owner incarnation (${have.detail ?? "no reason"}); not retried` : "this owner incarnation was already nudged once for this task",
2680
+ ...have.msgId !== null ? { msgId: have.msgId } : {}
2681
+ })
2682
+ };
2683
+ }
2684
+ const g = guardStall(db, ctx, callers, "steer");
2685
+ if (!g.ok) return { kind: "done", result: refused(g.reason, "steer") };
2686
+ const msgId = have?.msgId ?? newMsgId();
2687
+ if (have === void 0) {
2688
+ reserveActionReceipt(db, key, { ...infoOf(ctx, g.found), msgId });
2689
+ }
2690
+ return { kind: "send", msgId, key, found: g.found };
2691
+ }).immediate();
2692
+ }
2693
+ async function probeSteer(db, ctx, plan, timeoutMs) {
2694
+ const unknown = (reason) => {
2695
+ if (plan.state === "sending") {
2696
+ try {
2697
+ transitionActionReceipt(db, plan.key, "sending", "unknown", { detail: reason });
2698
+ } catch {
2699
+ }
2700
+ }
2701
+ return steerResult("unknown", "return", { msgId: plan.msgId, reason });
2702
+ };
2703
+ const agent = getAgent(db, ctx.owner.name, ctx.owner.workstream);
2704
+ if (agent === void 0 || agent.incarnation !== plan.key.ownerId || agent.paneId !== plan.paneId) {
2705
+ return unknown("the owner was replaced: the earlier steer cannot be confirmed (not resent)");
2706
+ }
2707
+ if (ctx.signal.aborted) return unknown("the wait was aborted before the receipt probe");
2708
+ const check = await checkMessage(agentCtlSocket(db, agent), plan.msgId, timeoutMs);
2709
+ if (check.state !== "accepted" || check.incarnation !== plan.key.incarnation) {
2710
+ return unknown(
2711
+ check.state === "accepted" ? "the receipt came from another pi incarnation: not confirmed" : "the earlier steer is not confirmed (receipt absent or unreachable): not resent"
2712
+ );
2713
+ }
2714
+ try {
2715
+ transitionActionReceipt(db, plan.key, plan.state, "sent", {
2716
+ ...check.runs !== void 0 ? { runs: check.runs } : {},
2717
+ detail: "confirmed by receipt probe"
2718
+ });
2719
+ } catch {
2720
+ }
2721
+ if (check.runs !== void 0 && ctx.owner.runs !== void 0 && check.runs < ctx.owner.runs) {
2722
+ return steerResult("sent", "return", {
2723
+ msgId: plan.msgId,
2724
+ incarnation: plan.key.incarnation,
2725
+ afterRuns: check.runs,
2726
+ reason: "nudged earlier, owner idle again"
2727
+ });
2728
+ }
2729
+ return sentResult(plan.msgId, plan.key.incarnation, check.runs);
2730
+ }
2731
+ function sentResult(msgId, incarnation, runs) {
2732
+ return steerResult("sent", runs === void 0 ? "return" : "continue", {
2733
+ msgId,
2734
+ incarnation,
2735
+ ...runs !== void 0 ? { afterRuns: runs } : {},
2736
+ ...runs === void 0 ? { reason: "sent, but pi reported no run baseline to wait from" } : {}
2737
+ });
2738
+ }
2739
+ async function steerStall(db, ctx, opts = {}) {
2740
+ const callers = opts.callers ?? currentCallers();
2741
+ const timeoutMs = Math.min(CTL_DEFAULT_TIMEOUT_MS, Math.max(1, ctx.deadlineMs));
2742
+ let plan;
2743
+ try {
2744
+ plan = planSteer(db, ctx, callers);
2745
+ } catch (e) {
2746
+ return refused(`steer failed, nothing sent: ${errText(e)}`, "steer");
2747
+ }
2748
+ if (plan.kind === "done") return plan.result;
2749
+ if (plan.kind === "probe") return probeSteer(db, ctx, plan, timeoutMs);
2750
+ const { key, msgId, found } = plan;
2751
+ const agent = getAgent(db, found.ownerName, ctx.owner.workstream);
2752
+ if (agent === void 0 || agent.incarnation !== found.ownerId) {
2753
+ return refused("the owner is no longer registered", "steer");
2754
+ }
2755
+ let won = false;
2756
+ let vetoed;
2757
+ const beforeWrite = () => {
2758
+ const go = db.transaction(() => {
2759
+ const g = guardStall(db, ctx, callers, "steer");
2760
+ if (!g.ok) {
2761
+ vetoed = refused(g.reason, "steer");
2762
+ return false;
2763
+ }
2764
+ if (!transitionActionReceipt(db, key, "reserved", "sending", { msgId })) {
2765
+ vetoed = steerResult("budget-exhausted", "return", {
2766
+ reason: "another waiter owns this nudge"
2767
+ });
2768
+ return false;
2769
+ }
2770
+ return true;
2771
+ }).immediate();
2772
+ won = go;
2773
+ return go;
2774
+ };
2775
+ try {
2776
+ const sent = await sendViaTransport(agent, steerText(found.workstream, found.localId), {
2777
+ socket: agentCtlSocket(db, agent),
2778
+ mode: "steer",
2779
+ msgId,
2780
+ expectIncarnation: key.incarnation,
2781
+ ctlGuard: { signal: ctx.signal, timeoutMs, beforeWrite }
2782
+ });
2783
+ try {
2784
+ const patch = {
2785
+ ...sent.runs !== void 0 ? { runs: sent.runs } : {},
2786
+ detail: sent.receipt ?? "accepted"
2787
+ };
2788
+ if (!transitionActionReceipt(db, key, "sending", "sent", patch)) {
2789
+ transitionActionReceipt(db, key, "unknown", "sent", patch);
2790
+ }
2791
+ recordSend(
2792
+ db,
2793
+ agent,
2794
+ "steer",
2795
+ Buffer.byteLength(steerText(found.workstream, found.localId)),
2796
+ {
2797
+ transport: "ctl",
2798
+ state: sent.state
2799
+ },
2800
+ ctx.caller
2801
+ );
2802
+ } catch {
2803
+ }
2804
+ return sentResult(msgId, key.incarnation, sent.runs);
2805
+ } catch (e) {
2806
+ if (e instanceof CtlWriteVetoedError || !won) {
2807
+ return vetoed ?? refused(`steer not sent: ${errText(e)}`, "steer");
2808
+ }
2809
+ const definite = e instanceof AgentSendRefusedError || e instanceof AgentReplacedError;
2810
+ const to = definite ? "refused" : "unknown";
2811
+ try {
2812
+ transitionActionReceipt(db, key, "sending", to, { detail: errText(e) });
2813
+ } catch {
2814
+ }
2815
+ return steerResult(to, "return", { msgId, reason: errText(e) });
2816
+ }
2817
+ }
2818
+ var errText = (e) => e instanceof Error ? e.message : String(e);
2819
+ function steerStallHandler(db, opts = {}) {
2820
+ return (ctx) => steerStall(db, ctx, opts);
2821
+ }
2822
+ function waitWithActions(db, refs, opts) {
2823
+ if (opts.handleStall !== void 0) return waitForEvents(db, refs, opts);
2824
+ const actionOpts = { callers: opts.callers };
2825
+ const handleStall = opts.onStall === "steer" ? steerStallHandler(db, actionOpts) : opts.onStall === "release" ? releaseStallHandler(db, actionOpts) : void 0;
2826
+ return waitForEvents(db, refs, handleStall === void 0 ? opts : { ...opts, handleStall });
2827
+ }
2828
+
2829
+ // src/wait-ctl.ts
2830
+ function ctlAgentHooks(db, opts = {}) {
2831
+ return {
2832
+ readAgents: async (refs) => {
2833
+ const agents = refs.flatMap((r) => {
2834
+ const a = getAgent(db, r.id, r.workstream);
2835
+ return a === void 0 ? [] : [a];
2836
+ });
2837
+ const observed = await observeAgentStates(db, agents);
2838
+ if (opts.paneText !== true) return observed;
2839
+ const out = new Map(observed);
2840
+ for (const a of agents) {
2841
+ const key = `${a.workstreamName}/${a.name}`;
2842
+ const base = out.get(key);
2843
+ if (base === void 0 || base.status === null) continue;
2844
+ if (base.ctl === "ok") continue;
2845
+ try {
2846
+ const text = await (await activeMux()).capture(a.paneId, { lines: 200 });
2847
+ out.set(key, { ...base, lastText: text.slice(-WAIT_TEXT_MAX), textSource: "pane" });
2848
+ } catch {
2849
+ }
2850
+ }
2851
+ return out;
2852
+ },
2853
+ watchAgent: async (ref, signal, ctx) => {
2854
+ const agent = getAgent(db, ref.id, ref.workstream);
2855
+ if (agent === void 0) return void 0;
2856
+ const sock = agentCtlSocket(db, agent);
2857
+ let afterRuns = ref.afterRuns;
2858
+ let unknownBaseline;
2859
+ if (afterRuns === void 0 && (ctx.owed || ctx.baseline !== void 0 || ctx.cursor !== void 0)) {
2860
+ if (!expectsCtl(agent, sock)) return void 0;
2861
+ const probe = await ctlProbe(sock);
2862
+ if (probe.kind !== "ok") return void 0;
2863
+ const inc = probe.incarnation;
2864
+ const b = ctx.baseline;
2865
+ const baseOk = b !== void 0 && (b.agentId === void 0 || b.agentId === agent.incarnation) && (b.incarnation === void 0 || inc !== void 0 && b.incarnation === inc);
2866
+ const cursorOk = ctx.cursor !== void 0 && inc !== void 0 && ctx.cursor.incarnation === inc;
2867
+ const fromCursor = cursorOk && ctx.cursor !== void 0 ? ctx.cursor.runs : void 0;
2868
+ if (baseOk && b !== void 0) {
2869
+ afterRuns = Math.max(b.afterRuns, fromCursor ?? 0);
2870
+ } else if (fromCursor !== void 0) afterRuns = fromCursor;
2871
+ else if (ctx.owed) {
2872
+ afterRuns = probe.status.runs;
2873
+ unknownBaseline = {
2874
+ afterRuns,
2875
+ ...inc !== void 0 ? { incarnation: inc } : {},
2876
+ agentId: agent.incarnation
2877
+ };
2878
+ }
2879
+ }
2880
+ const handle = await watchAgentRun(agent, sock, signal, {
2881
+ ...afterRuns !== void 0 ? { afterRuns } : {},
2882
+ ...ref.incarnation !== void 0 ? { inc: ref.incarnation } : {}
2883
+ });
2884
+ return handle !== void 0 && unknownBaseline !== void 0 ? { ...handle, unknownBaseline } : handle;
2885
+ }
2886
+ };
2887
+ }
2888
+
2889
+ // src/next-recover.ts
2890
+ var OBSERVE_MS = 3e3;
2891
+ var idleOnCtl = (f) => f.status === "needs_input" && f.ctl === "ok" && f.pending !== true && f.blocked === void 0;
2892
+ var workerKey = (a) => `${a.workstreamName}/${a.name}`;
2893
+ var observeWorkers = (db) => async (agents, { signal, ms }) => {
2894
+ const states = await observeAgentStates(db, agents);
2895
+ const out = /* @__PURE__ */ new Map();
2896
+ await Promise.all(
2897
+ agents.map(async (agent) => {
2898
+ const key = workerKey(agent);
2899
+ const s = states.get(key);
2900
+ if (s === void 0) return;
2901
+ const facts = {
2902
+ status: s.status,
2903
+ ...s.ctl !== void 0 ? { ctl: s.ctl } : {},
2904
+ ...s.pending !== void 0 ? { pending: s.pending } : {},
2905
+ ...s.blocked !== void 0 ? { blocked: s.blocked } : {},
2906
+ ...s.incarnation !== void 0 ? { incarnation: s.incarnation } : {},
2907
+ ...s.runs !== void 0 ? { runs: s.runs } : {}
2908
+ };
2909
+ out.set(key, facts);
2910
+ const sock = agentCtlSocket(db, agent);
2911
+ if (!idleOnCtl(s) || s.runs === void 0 || s.runs < 1 || !expectsCtl(agent, sock)) return;
2912
+ const stop = new AbortController();
2913
+ const timer = setTimeout(() => stop.abort(), ms);
2914
+ const onAbort = () => stop.abort();
2915
+ signal?.addEventListener("abort", onAbort, { once: true });
2916
+ try {
2917
+ const watch = await watchAgentRun(agent, sock, stop.signal, {
2918
+ afterRuns: s.runs - 1,
2919
+ ...s.incarnation !== void 0 ? { inc: s.incarnation } : {}
2920
+ });
2921
+ if (watch === void 0) return;
2922
+ const settled = await watch.settled;
2923
+ if (settled.settled) {
2924
+ out.set(key, {
2925
+ ...facts,
2926
+ ...settled.runs !== void 0 ? { runs: settled.runs } : {},
2927
+ ...settled.incarnation !== void 0 ? { incarnation: settled.incarnation } : {},
2928
+ ...settled.lastError !== void 0 ? { lastError: settled.lastError } : {}
2929
+ });
2930
+ }
2931
+ } catch {
2932
+ } finally {
2933
+ clearTimeout(timer);
2934
+ signal?.removeEventListener("abort", onAbort);
2935
+ }
2936
+ })
2937
+ );
2938
+ return out;
2939
+ };
2940
+ function inFlightWorkers(db, workstream) {
2941
+ const rows = db.prepare(
2942
+ `SELECT t.id AS id, t.local_id AS local_id
2943
+ FROM task_auto a
2944
+ JOIN tasks t ON t.id = a.task_id
2945
+ JOIN workstreams w ON w.id = t.workstream_id AND w.name = ?
2946
+ WHERE a.enabled = 1 AND t.kind = 'work' AND t.dispatch_host = ?
2947
+ AND t.status = 'IN_PROGRESS' AND t.owner_id IS NOT NULL
2948
+ ORDER BY t.id`
2949
+ ).all(workstream, localMachineId(db));
2950
+ const out = [];
2951
+ for (const r of rows) {
2952
+ if (!autoPermitted(db, r.id)) continue;
2953
+ const delivered = deliveredOf(db, r.id);
2954
+ if (delivered === void 0) continue;
2955
+ const { receipt } = delivered;
2956
+ const id = savedIdentity(db, receipt);
2957
+ if (id.agent === void 0) continue;
2958
+ const owner = db.prepare("SELECT owner_id FROM tasks WHERE id = ?").get(r.id);
2959
+ if (owner.owner_id !== id.agent.incarnation) continue;
2960
+ out.push({
2961
+ task: r.local_id,
2962
+ taskId: r.id,
2963
+ agent: id.agent,
2964
+ runs: receipt.runs,
2965
+ incarnation: receipt.incarnation
2966
+ });
2967
+ }
2968
+ return out;
2969
+ }
2970
+ function exhaustedAfterDeath(db, workstream) {
2971
+ const rows = db.prepare(
2972
+ `SELECT t.id AS id, t.local_id AS local_id, a.max_runs AS max
2973
+ FROM task_auto a
2974
+ JOIN tasks t ON t.id = a.task_id
2975
+ JOIN workstreams w ON w.id = t.workstream_id AND w.name = ?
2976
+ WHERE a.enabled = 1 AND t.kind = 'work' AND t.dispatch_host = ?
2977
+ AND t.status = 'OPEN' AND a.runs >= a.max_runs
2978
+ ORDER BY t.id`
2979
+ ).all(workstream, localMachineId(db));
2980
+ const out = [];
2981
+ for (const r of rows) {
2982
+ if (!autoPermitted(db, r.id)) continue;
2983
+ const delivered = deliveredOf(db, r.id);
2984
+ if (delivered === void 0) continue;
2985
+ if (savedIdentity(db, delivered.receipt).stale === void 0) continue;
2986
+ out.push({
2987
+ task: r.local_id,
2988
+ reason: `its worker is gone and it used its ${r.max} run(s): raise the ceiling (mu task auto --max) or take it over`
2989
+ });
2990
+ }
2991
+ return out;
2992
+ }
2993
+ async function scanRecovery(db, workstream, observe, opts = {}) {
2994
+ const workers = inFlightWorkers(db, workstream);
2995
+ const out = [];
2996
+ if (workers.length > 0) {
2997
+ const facts = await observe(
2998
+ workers.map((w) => w.agent),
2999
+ { ...opts.signal !== void 0 ? { signal: opts.signal } : {}, ms: opts.ms ?? OBSERVE_MS }
3000
+ );
3001
+ for (const w of workers) {
3002
+ const f = facts.get(workerKey(w.agent));
3003
+ if (f === void 0) continue;
3004
+ if (f.status === null) {
3005
+ if (w.agent.host !== null) {
3006
+ out.push({
3007
+ kind: "attention",
3008
+ task: w.task,
3009
+ agent: w.agent.name,
3010
+ reason: `remote worker ${w.agent.name} on ${w.agent.host} has no pane: its tree is kept, not reaped (mu agent collect, then close it)`
3011
+ });
3012
+ } else {
3013
+ out.push({
3014
+ kind: "reap",
3015
+ task: w.task,
3016
+ agent: w.agent,
3017
+ why: "its pane is gone"
3018
+ });
3019
+ }
3020
+ continue;
3021
+ }
3022
+ if (f.lastError === void 0) continue;
3023
+ if (!idleOnCtl(f)) continue;
3024
+ const attention = (reason) => {
3025
+ out.push({ kind: "attention", task: w.task, agent: w.agent.name, reason });
3026
+ };
3027
+ if (w.agent.host !== null) {
3028
+ attention(`remote worker ${w.agent.name} stopped on an API error: not retried from here`);
3029
+ continue;
3030
+ }
3031
+ if (f.incarnation === void 0 || f.runs === void 0 || w.runs === null) {
3032
+ attention(
3033
+ `${w.agent.name} stopped on an API error but no run baseline or pi incarnation is known: not retried`
3034
+ );
3035
+ continue;
3036
+ }
3037
+ if (f.runs <= w.runs) continue;
3038
+ const auto = getAutoConfig(db, w.taskId);
3039
+ if (auto !== void 0 && auto.runs >= auto.maxRuns) {
3040
+ attention(
3041
+ `${w.agent.name} stopped on an API error and ${w.task} used its ${auto.maxRuns} run(s): raise the ceiling (mu task auto --max) or take it over`
3042
+ );
3043
+ continue;
3044
+ }
3045
+ out.push({
3046
+ kind: "retry",
3047
+ task: w.task,
3048
+ error: f.lastError,
3049
+ target: {
3050
+ agent: { incarnation: w.agent.incarnation, paneId: w.agent.paneId, name: w.agent.name },
3051
+ pi: f.incarnation,
3052
+ errorRuns: f.runs
3053
+ }
3054
+ });
3055
+ }
3056
+ }
3057
+ return out;
3058
+ }
3059
+ function liveWorkers(db, workstream) {
3060
+ const row = db.prepare(
3061
+ `SELECT COUNT(*) AS n FROM agents a
3062
+ JOIN workstreams w ON w.id = a.workstream_id AND w.name = ?
3063
+ WHERE EXISTS (SELECT 1 FROM tasks t WHERE t.owner_id = a.id AND t.status = 'IN_PROGRESS')
3064
+ OR EXISTS (SELECT 1 FROM spawn_receipts r JOIN tasks t ON t.id = r.task_id
3065
+ WHERE r.agent_id = a.id AND r.pane_id = a.pane_id
3066
+ AND r.attempt_key LIKE 'auto:%'
3067
+ AND r.state IN ('pane', 'claimed', 'sending', 'unknown')
3068
+ AND t.status != 'CLOSED')`
3069
+ ).get(workstream);
3070
+ return row.n;
3071
+ }
3072
+
3073
+ // src/next-refute.ts
3074
+ var REFUTE_WORKSTREAM = "scratch";
3075
+ var READ_MS = 3e3;
3076
+ var CHECK_TIMEOUT_S = 1800;
3077
+ var readChecker = async (db, agent, base, { signal, ms }) => {
3078
+ const live = (await observeAgentStates(db, [agent])).get(agentKey(agent));
3079
+ if (live === void 0) return { kind: "running" };
3080
+ if (live.status === null) return { kind: "died" };
3081
+ if (!idleOnCtl(live) || live.runs === void 0 || live.runs <= base.afterRuns) {
3082
+ return { kind: "running" };
3083
+ }
3084
+ const stop = new AbortController();
3085
+ const timer = setTimeout(() => stop.abort(), ms);
3086
+ const onAbort = () => stop.abort();
3087
+ signal?.addEventListener("abort", onAbort, { once: true });
3088
+ try {
3089
+ const watch = await watchAgentRun(agent, agentCtlSocket(db, agent), stop.signal, {
3090
+ afterRuns: base.afterRuns,
3091
+ ...base.inc !== null ? { inc: base.inc } : {}
3092
+ });
3093
+ const done = watch === void 0 ? void 0 : await watch.settled;
3094
+ if (done === void 0 || !done.settled) return { kind: "running" };
3095
+ const outcome = delegateOutcome({ fired: true, dead: false, ...done }, false);
3096
+ return { kind: "answer", outcome, text: done.lastText ?? done.lastError };
3097
+ } catch (err) {
3098
+ if (err instanceof AgentRestartedError) return { kind: "died" };
3099
+ throw err;
3100
+ } finally {
3101
+ clearTimeout(timer);
3102
+ signal?.removeEventListener("abort", onAbort);
3103
+ }
3104
+ };
3105
+ function nextCheck(db, workstream, caller, skip) {
3106
+ const rows = db.prepare(
3107
+ `SELECT t.id AS id, t.local_id AS local_id
3108
+ FROM task_auto a
3109
+ JOIN tasks t ON t.id = a.task_id
3110
+ JOIN workstreams w ON w.id = t.workstream_id AND w.name = ?
3111
+ WHERE t.status = 'CLOSED' AND t.substate = 'done' ORDER BY t.id`
3112
+ ).all(workstream);
3113
+ for (const r of rows) {
3114
+ if (!autoPermitted(db, r.id)) continue;
3115
+ const attempt = deliveredOf(db, r.id)?.receipt.attemptKey;
3116
+ if (attempt === void 0) continue;
3117
+ const callId = `refute:${workstream}:${r.local_id}:${attempt}`;
3118
+ if (skip.has(callId)) continue;
3119
+ const call = getDelegateCall(db, callId);
3120
+ if (call === void 0) return { callId, task: r.local_id, attempt, workstream };
3121
+ if (call.caller !== caller) continue;
3122
+ if (["starting", "running", "recording"].includes(call.state)) {
3123
+ return { callId, task: r.local_id, attempt, workstream, call };
3124
+ }
3125
+ }
3126
+ return void 0;
3127
+ }
3128
+ function checkerOf(db, c) {
3129
+ const taskId = taskIdFor(db, c.task, c.workstream);
3130
+ const receipt = taskId === null ? void 0 : getReceipt(db, taskId, `manual:${c.task}`);
3131
+ const agent = receipt === void 0 ? void 0 : savedIdentity(db, receipt).agent;
3132
+ return receipt !== void 0 && agent !== void 0 ? { receipt, agent } : void 0;
3133
+ }
3134
+ function checkerRefs(db, workstream, caller) {
3135
+ const refs = [];
3136
+ for (const c of liveCalls(db, workstream, caller)) {
3137
+ const held = checkerOf(db, c);
3138
+ if (held === void 0) continue;
3139
+ const { receipt, agent } = held;
3140
+ refs.push({
3141
+ kind: "agent",
3142
+ workstream: agent.workstreamName,
3143
+ id: agent.name,
3144
+ afterRuns: receipt.runs ?? 0,
3145
+ ...receipt.incarnation !== null ? { incarnation: receipt.incarnation } : {}
3146
+ });
3147
+ }
3148
+ return refs;
3149
+ }
3150
+ function liveCalls(db, workstream, caller) {
3151
+ return db.prepare(
3152
+ "SELECT call_id FROM delegate_calls WHERE caller = ? AND call_id LIKE ? AND state IN ('running','recording')"
3153
+ ).all(caller, `refute:${workstream}:%`).map((r) => getDelegateCall(db, r.call_id)).filter((c) => c !== void 0);
3154
+ }
3155
+ var stem = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 13).replace(/^-+|-+$/g, "");
3156
+ function checkerText(db, ws, task, attempt) {
3157
+ const { briefN, reportN } = pinnedNotes(db, ws, task);
3158
+ const part = (what, n) => n < 1 ? `${what}: (none)` : `${what} (note #${n}):
3159
+ ${getTaskNote(db, task, ws, n).content.trimEnd()}`;
3160
+ return [
3161
+ `You are an independent checker of the work on task ${task} (workstream ${ws}), attempt ${attempt}. Read only: do not edit, commit, merge, close or accept anything.`,
3162
+ part("Pinned brief", briefN),
3163
+ part("The worker's report", reportN),
3164
+ "Try to refute the claim that the work meets the brief: read the code and run the checks yourself. End your final message with a line starting `VERDICT:` and one of CONFIRMED, REFUTED, UNVERIFIED, then `EVIDENCE:` lines naming what you ran and saw."
3165
+ ].join("\n\n");
3166
+ }
3167
+ var planCheck = (c) => ({
3168
+ kind: "refute",
3169
+ task: c.task,
3170
+ key: c.callId,
3171
+ state: c.call?.state ?? "new"
3172
+ });
3173
+ async function runCheck(db, c, deps) {
3174
+ const base = { task: c.task, key: c.callId };
3175
+ const taskId = taskIdFor(db, c.task, c.workstream);
3176
+ const auto = taskId === null ? void 0 : getAutoConfig(db, taskId);
3177
+ let call = c.call;
3178
+ if (call === void 0) {
3179
+ const cli = deps.cli ?? auto?.cli ?? "pi";
3180
+ if (!speaksMuCtl(cli, resolveCliCommand(cli))) {
3181
+ return {
3182
+ kind: "skip",
3183
+ ...base,
3184
+ cli,
3185
+ reason: `cli ${cli} cannot report an answer over ctl: pass --refute-cli pi`
3186
+ };
3187
+ }
3188
+ try {
3189
+ call = registerDelegateCall(db, {
3190
+ callId: c.callId,
3191
+ caller: deps.caller,
3192
+ workstream: REFUTE_WORKSTREAM,
3193
+ wave: `refute-${c.workstream}`,
3194
+ record: { workstream: c.workstream, task: c.task },
3195
+ state: "starting",
3196
+ request: {
3197
+ text: checkerText(db, c.workstream, c.task, c.attempt),
3198
+ label: `refute-${c.task}`,
3199
+ cli,
3200
+ timeoutS: CHECK_TIMEOUT_S
3201
+ }
3202
+ }).call;
3203
+ } catch (err) {
3204
+ if (err instanceof DelegateCallConflictError) {
3205
+ return { kind: "skip", ...base, reason: "another mu registered this check first" };
3206
+ }
3207
+ throw err;
3208
+ }
3209
+ }
3210
+ if (call.state === "starting") return launch(db, call, base);
3211
+ return readOne(db, call, base, deps);
3212
+ }
3213
+ async function launch(db, call, base) {
3214
+ const cli = call.request.cli ?? "pi";
3215
+ try {
3216
+ const out = await spawnForTask(db, {
3217
+ task: call.task,
3218
+ text: call.request.text,
3219
+ spawn: {
3220
+ name: `delegate-refute-${stem(base.task)}`,
3221
+ workstream: call.workstream,
3222
+ cli,
3223
+ nextFree: true
3224
+ }
3225
+ });
3226
+ const agent = out.agent?.name ?? out.receipt.name;
3227
+ if (out.state === "unknown") {
3228
+ if (!out.replay)
3229
+ return {
3230
+ kind: "refute",
3231
+ ...base,
3232
+ agent,
3233
+ cli,
3234
+ state: "starting",
3235
+ reason: out.reason ?? "delivery unknown"
3236
+ };
3237
+ return end(
3238
+ db,
3239
+ call,
3240
+ base,
3241
+ "error",
3242
+ `delivery unknown after one probe (${out.reason ?? "no reply"}); pane ${agent} kept`
3243
+ );
3244
+ }
3245
+ try {
3246
+ markDelegate(db, call.callId, "running", { agent });
3247
+ } catch (err) {
3248
+ if (!(err instanceof DelegateStateError)) throw err;
3249
+ }
3250
+ return { kind: "refute", ...base, agent, cli, state: "running" };
3251
+ } catch (err) {
3252
+ if (err instanceof NoMultiplexerError) throw err;
3253
+ if (err instanceof SpawnAttemptBusyError) {
3254
+ return { kind: "skip", ...base, reason: "another mu is launching this checker" };
3255
+ }
3256
+ const reason = err instanceof Error ? err.message : String(err);
3257
+ const done = end(db, call, base, "error", `the checker could not be started: ${reason}`);
3258
+ return { ...done, kind: "failed", reason };
3259
+ }
3260
+ }
3261
+ function end(db, call, base, outcome, text, agent) {
3262
+ const r = finishDelegate(db, call.callId, {
3263
+ outcome,
3264
+ ...text !== void 0 ? { text } : {},
3265
+ elapsedMs: Math.max(0, Date.now() - Date.parse(call.updatedAt)),
3266
+ author: agent ?? call.agent ?? "next"
3267
+ });
3268
+ return {
3269
+ kind: "refute",
3270
+ ...base,
3271
+ ...call.agent !== null ? { agent: call.agent } : {},
3272
+ state: r.state,
3273
+ ...r.verdict !== null ? { verdict: r.verdict } : {},
3274
+ ...r.error !== void 0 ? { reason: r.error } : {}
3275
+ };
3276
+ }
3277
+ async function readOne(db, call, base, deps) {
3278
+ const held = checkerOf(db, call);
3279
+ if (held === void 0) {
3280
+ return end(db, call, base, "died", "the checker's agent is gone", call.agent ?? "next");
3281
+ }
3282
+ const { receipt, agent } = held;
3283
+ const got = await deps.read(
3284
+ db,
3285
+ agent,
3286
+ { afterRuns: receipt.runs ?? 0, inc: receipt.incarnation },
3287
+ { ...deps.signal !== void 0 ? { signal: deps.signal } : {}, ms: READ_MS }
3288
+ );
3289
+ if (got.kind === "running") {
3290
+ const late = Date.now() - Date.parse(call.updatedAt) > (call.request.timeoutS ?? CHECK_TIMEOUT_S) * 1e3;
3291
+ if (!late) return { kind: "refute", ...base, agent: agent.name, state: "running" };
3292
+ return end(db, call, base, "timeout", void 0, agent.name);
3293
+ }
3294
+ return end(
3295
+ db,
3296
+ call,
3297
+ base,
3298
+ got.kind === "died" ? "died" : got.outcome,
3299
+ got.kind === "answer" ? got.text : void 0,
3300
+ agent.name
3301
+ );
3302
+ }
3303
+
3304
+ // src/next.ts
3305
+ var NextArgError = class extends Error {
3306
+ name = "NextArgError";
3307
+ };
3308
+ var DEFAULT_NEXT_CAP = 5;
3309
+ var MAX_NEXT_CAP = 100;
3310
+ function checkCap(cap) {
3311
+ if (cap === void 0) return DEFAULT_NEXT_CAP;
3312
+ if (!Number.isInteger(cap) || cap < 1 || cap > MAX_NEXT_CAP) {
3313
+ throw new NextArgError(`--cap must be an integer from 1 to ${MAX_NEXT_CAP} (got ${cap})`);
3314
+ }
3315
+ return cap;
3316
+ }
3317
+ function checkWorkers(n) {
3318
+ if (n === void 0) return void 0;
3319
+ if (!Number.isInteger(n) || n < 1 || n > MAX_NEXT_CAP) {
3320
+ throw new NextArgError(`--workers must be an integer from 1 to ${MAX_NEXT_CAP} (got ${n})`);
3321
+ }
3322
+ return n;
3323
+ }
3324
+ function checkTimeout(ms) {
3325
+ if (ms === void 0) return WAIT_HEARTBEAT_CAP_MS;
3326
+ if (!Number.isFinite(ms) || ms < 0) {
3327
+ throw new NextArgError(`--timeout must be a number of seconds, 0 or more (got ${ms / 1e3})`);
3328
+ }
3329
+ return ms === 0 ? WAIT_HEARTBEAT_CAP_MS : Math.min(ms, WAIT_HEARTBEAT_CAP_MS);
3330
+ }
3331
+ function select(db, ws, done) {
3332
+ const pending = pendingAttempts(db, ws).find((t) => !done.has(t));
3333
+ if (pending !== void 0) return { task: pending, resume: true };
3334
+ const ready = authorisedReady(db, ws).find((t) => !done.has(t.name));
3335
+ return ready === void 0 ? void 0 : { task: ready.name, resume: false };
3336
+ }
3337
+ function needsPane(db, ws, c) {
3338
+ if (!c.resume) return true;
3339
+ const id = taskIdFor(db, c.task, ws);
3340
+ const receipt = id === null ? void 0 : readAttempt(db, id).receipt;
3341
+ return receipt?.state === "reserved";
3342
+ }
3343
+ function counts(m) {
3344
+ if (m.kind === "skip" || m.kind === "abandoned" || m.kind === "attention") return false;
3345
+ if (m.kind === "needs_human") return m.checked !== void 0;
3346
+ return true;
3347
+ }
3348
+ var HoldHumanStop = class extends Error {
3349
+ };
3350
+ var DEAD_OR_ERRORED = (r) => {
3351
+ const e = r.event;
3352
+ if (e.kind === "dead") return true;
3353
+ if (e.kind !== "settled" || e.ref === null) return false;
3354
+ const snap = r.refs.find(
3355
+ (s) => s.ref.kind === e.ref?.kind && s.ref.workstream === e.ref.workstream && s.ref.id === e.ref.id
3356
+ );
3357
+ return snap?.lastError !== void 0 && snap.close?.applied !== true;
3358
+ };
3359
+ var refText = (r) => r.kind === "delegate" ? `delegate:${r.id}` : `${r.kind}:${r.workstream}/${r.id}`;
3360
+ async function executeNext(db, opts) {
3361
+ const cap = checkCap(opts.cap);
3362
+ const timeoutMs = checkTimeout(opts.timeoutMs);
3363
+ const workers = checkWorkers(opts.workers);
3364
+ const refute = opts.refute === true;
3365
+ if (refute && opts.caller === "") {
3366
+ throw new NextArgError(
3367
+ "--refute needs a caller identity (a managed agent, or PI_SESSION_ID): a checker belongs to the caller that launched it"
3368
+ );
3369
+ }
3370
+ const ws = opts.workstream;
3371
+ const out = { workstream: ws, dry: opts.dry === true, cap, used: 0, moves: [] };
3372
+ const done = /* @__PURE__ */ new Set();
3373
+ const ingest = opts.ingest ?? (() => ambientIngest(db, { quiet: true }));
3374
+ const humanStop = () => {
3375
+ const needs = openHumanNeeds(db, opts.caller);
3376
+ if (needs.length === 0) return false;
3377
+ out.stopped = "human";
3378
+ out.human_needed = needs;
3379
+ return true;
3380
+ };
3381
+ const ctx = { db, ws, caller: opts.caller, humanStop, workers };
3382
+ const observe = opts.observe ?? observeWorkers(db);
3383
+ const deadline = Date.now() + timeoutMs;
3384
+ const aborted = () => {
3385
+ if (opts.signal?.aborted !== true) return false;
3386
+ out.stopped = "interrupted";
3387
+ return true;
3388
+ };
3389
+ const told = /* @__PURE__ */ new Set();
3390
+ const tell = (task, reason, agent) => {
3391
+ if (told.has(`${task}|${reason}`)) return;
3392
+ told.add(`${task}|${reason}`);
3393
+ out.moves.push({ kind: "attention", task, ...agent !== void 0 ? { agent } : {}, reason });
3394
+ };
3395
+ const attend = () => {
3396
+ for (const a of exhaustedAfterDeath(db, ws)) tell(a.task, a.reason);
3397
+ };
3398
+ const pass = async () => {
3399
+ let actions;
3400
+ for (; ; ) {
3401
+ if (aborted()) return;
3402
+ if (out.used >= cap) {
3403
+ if (out.stopped === void 0 && (select(db, ws, done) !== void 0 || (actions?.length ?? 0) > 0 || refute && nextCheck(db, ws, opts.caller, done) !== void 0)) {
3404
+ out.stopped = "cap";
3405
+ }
3406
+ return;
3407
+ }
3408
+ if (!out.dry) await ingest();
3409
+ if (aborted()) return;
3410
+ if (humanStop()) return;
3411
+ if (actions === void 0) {
3412
+ const found = out.dry ? [] : await scanRecovery(
3413
+ db,
3414
+ ws,
3415
+ observe,
3416
+ opts.signal !== void 0 ? { signal: opts.signal } : {}
3417
+ );
3418
+ if (aborted()) return;
3419
+ if (out.dry) out.inFlight = inFlightWorkers(db, ws).map((w) => w.task);
3420
+ actions = [];
3421
+ for (const a of found) {
3422
+ if (a.kind === "attention") tell(a.task, a.reason, a.agent);
3423
+ else actions.push(a);
3424
+ }
3425
+ }
3426
+ const act = actions.shift();
3427
+ if (act !== void 0 && !out.dry) {
3428
+ const move2 = await recover(ctx, act, observe, opts.signal);
3429
+ if (act.kind === "retry") done.add(act.task);
3430
+ out.moves.push(move2);
3431
+ if (counts(move2)) out.used++;
3432
+ if (move2.kind === "failed") {
3433
+ out.stopped = "failed";
3434
+ return;
3435
+ }
3436
+ continue;
3437
+ }
3438
+ const next = select(db, ws, done);
3439
+ if (next === void 0) {
3440
+ const check = refute ? nextCheck(db, ws, opts.caller, done) : void 0;
3441
+ if (check !== void 0) {
3442
+ done.add(check.callId);
3443
+ const move2 = out.dry ? planCheck(check) : await runCheck(db, check, {
3444
+ caller: opts.caller,
3445
+ read: opts.readChecker ?? readChecker,
3446
+ ...opts.refuteCli !== void 0 ? { cli: opts.refuteCli } : {},
3447
+ ...opts.signal !== void 0 ? { signal: opts.signal } : {}
3448
+ });
3449
+ out.moves.push(move2);
3450
+ if (counts(move2)) out.used++;
3451
+ if (move2.kind === "failed" && !out.dry) {
3452
+ out.stopped = "failed";
3453
+ return;
3454
+ }
3455
+ continue;
3456
+ }
3457
+ attend();
3458
+ return;
3459
+ }
3460
+ if (workers !== void 0 && needsPane(db, ws, next) && liveWorkers(db, ws) >= workers) {
3461
+ tell(
3462
+ next.task,
3463
+ `worker ceiling ${workers} reached (${liveWorkers(db, ws)} working): no new pane until one finishes`
3464
+ );
3465
+ out.stopped = "workers";
3466
+ return;
3467
+ }
3468
+ done.add(next.task);
3469
+ const move = out.dry ? planMove(db, ws, next.task, next.resume) : next.resume ? await resume(ctx, next.task) : await allocate(ctx, next.task);
3470
+ out.moves.push(move);
3471
+ if (counts(move)) out.used++;
3472
+ if (move.kind === "failed" && !out.dry) {
3473
+ out.stopped = "failed";
3474
+ return;
3475
+ }
3476
+ }
3477
+ };
3478
+ for (let waits = 0; ; ) {
3479
+ const before = out.used;
3480
+ await pass();
3481
+ const atCeiling = out.stopped === "workers";
3482
+ if (opts.hold !== true || out.dry || out.stopped !== void 0 && !atCeiling) return out;
3483
+ if (waits > 0 && out.used === before) return out;
3484
+ const inFlight = inFlightWorkers(db, ws);
3485
+ const checkers = refute ? checkerRefs(db, ws, opts.caller) : [];
3486
+ if (inFlight.length === 0 && checkers.length === 0) {
3487
+ out.held = { waits, event: "none", ref: null, exit: 0, timedOut: false, open: [] };
3488
+ return out;
3489
+ }
3490
+ const left = deadline - Date.now();
3491
+ if (left <= 0) {
3492
+ out.held = { waits, event: "heartbeat", ref: null, exit: 0, timedOut: true, open: [] };
3493
+ return out;
3494
+ }
3495
+ const refs = checkers.concat(
3496
+ inFlight.flatMap((w) => [
3497
+ { kind: "task", workstream: ws, id: w.task },
3498
+ {
3499
+ kind: "agent",
3500
+ workstream: w.agent.workstreamName,
3501
+ id: w.agent.name,
3502
+ ...w.runs !== null ? { afterRuns: w.runs } : {},
3503
+ ...w.incarnation !== null ? { incarnation: w.incarnation } : {}
3504
+ }
3505
+ ])
3506
+ );
3507
+ waits++;
3508
+ let res;
3509
+ try {
3510
+ res = await (opts.wait ?? waitWithActions)(db, refs, {
3511
+ callers: opts.caller === "" ? [] : [opts.caller],
3512
+ mode: "first",
3513
+ eventScope: "refs",
3514
+ timeoutMs: left,
3515
+ ...opts.signal !== void 0 ? { signal: opts.signal } : {},
3516
+ onStall: opts.onStall ?? "exit",
3517
+ ...ctlAgentHooks(db),
3518
+ beforePoll: async () => {
3519
+ if (humanStop()) throw new HoldHumanStop();
3520
+ }
3521
+ });
3522
+ } catch (err) {
3523
+ if (err instanceof HoldHumanStop) return out;
3524
+ if (err instanceof AgentRestartedError) {
3525
+ out.moves.push({ kind: "attention", task: "", reason: err.message });
3526
+ out.held = { waits, event: "restarted", ref: null, exit: 7, timedOut: false, open: [] };
3527
+ return out;
3528
+ }
3529
+ throw err;
3530
+ }
3531
+ out.held = {
3532
+ waits,
3533
+ event: res.event.kind,
3534
+ ref: res.event.ref === null ? null : refText(res.event.ref),
3535
+ exit: res.exit,
3536
+ timedOut: res.timedOut,
3537
+ open: res.refs.filter((x) => !x.fired && !x.dead).slice(0, 8).map((x) => ({
3538
+ ref: refText(x.ref),
3539
+ status: x.status,
3540
+ ...x.lastError !== void 0 ? { lastError: x.lastError.slice(0, 200) } : {}
3541
+ }))
3542
+ };
3543
+ if (res.interrupted) {
3544
+ out.stopped = "interrupted";
3545
+ return out;
3546
+ }
3547
+ const freed = atCeiling && res.event.kind === "closed";
3548
+ const checkerEvent = checkers.some(
3549
+ (c) => res.event.ref?.kind === "agent" && res.event.ref.id === c.id
3550
+ );
3551
+ if (res.event.kind === "heartbeat" || !DEAD_OR_ERRORED(res) && !freed && !checkerEvent) {
3552
+ return out;
3553
+ }
3554
+ if (atCeiling) out.stopped = void 0;
3555
+ done.clear();
3556
+ }
3557
+ }
3558
+ function planMove(db, ws, task, resume2) {
3559
+ const taskId = taskIdFor(db, task, ws);
3560
+ if (resume2 && taskId !== null) {
3561
+ const { record, receipt } = readAttempt(db, taskId);
3562
+ if (record !== void 0 && receipt !== void 0) {
3563
+ const base = { task, attempt: record.attempt, key: record.key, cli: record.cli };
3564
+ const end2 = unreconcilable(db, ws, task, receipt);
3565
+ if (end2 !== void 0) return { kind: end2.state, ...base, reason: end2.reason };
3566
+ return {
3567
+ kind: "replay",
3568
+ ...base,
3569
+ agent: receipt.name,
3570
+ state: receipt.state
3571
+ };
3572
+ }
3573
+ }
3574
+ const planned = planAttempt(db, ws, task);
3575
+ if (!planned.ok) return { kind: "skip", task, reason: planned.reason };
3576
+ const p = planned.plan;
3577
+ return { kind: "spawn", task, attempt: p.attempt, key: p.key, agent: p.name, cli: p.cli };
3578
+ }
3579
+ function unreconcilable(db, ws, task, receipt) {
3580
+ const t = getTask(db, task, ws);
3581
+ if (t === void 0) return { state: "abandoned", reason: "the task is gone" };
3582
+ if (t.status === "CLOSED") return { state: "abandoned", reason: "the task is closed" };
3583
+ const sent = receipt.state === "sending" || receipt.state === "unknown";
3584
+ const lost = (reason) => ({
3585
+ state: sent ? "needs_human" : "abandoned",
3586
+ reason
3587
+ });
3588
+ if (receipt.state === "reserved") {
3589
+ return t.ownerId === null ? void 0 : lost("the task is owned by another agent");
3590
+ }
3591
+ const id = savedIdentity(db, receipt);
3592
+ if (id.stale !== void 0) {
3593
+ return lost(`the agent of this attempt is ${id.stale}`);
3594
+ }
3595
+ if (sent && receipt.transport !== "ctl") {
3596
+ return lost("the send went through the pane: there is no receipt to ask");
3597
+ }
3598
+ return void 0;
3599
+ }
3600
+ function park(ctx, task, record, end2) {
3601
+ if (end2.state === "needs_human" && ctx.caller !== "") {
3602
+ recordHumanNeeded(ctx.db, {
3603
+ workstream: ctx.ws,
3604
+ caller: ctx.caller,
3605
+ reason: `attempt ${record.key} of task ${task} may have been delivered and cannot be confirmed (${end2.reason}); never resent`
3606
+ });
3607
+ }
3608
+ const taskId = taskIdFor(ctx.db, task, ctx.ws);
3609
+ if (taskId !== null) endAttempt(ctx.db, taskId, record.key, end2);
3610
+ return {
3611
+ kind: end2.state,
3612
+ task,
3613
+ attempt: record.attempt,
3614
+ key: record.key,
3615
+ cli: record.cli,
3616
+ reason: end2.reason
3617
+ };
3618
+ }
3619
+ function guardOf(ctx, task, key) {
3620
+ let refusedAt;
3621
+ const guard = (phase) => {
3622
+ const stop = (why) => {
3623
+ refusedAt = phase;
3624
+ throw new SpawnAttemptGuardError(task, key, why);
3625
+ };
3626
+ if (ctx.humanStop()) stop("this caller has an open human need");
3627
+ const taskId = taskIdFor(ctx.db, task, ctx.ws);
3628
+ if (taskId === null || !autoPermitted(ctx.db, taskId)) {
3629
+ stop("it is no longer authorised for this machine");
3630
+ }
3631
+ if (phase === "spawn" && !listReady(ctx.db, ctx.ws).some((t) => t.name === task)) {
3632
+ stop("it is no longer ready");
3633
+ }
3634
+ if (phase === "spawn" && ctx.workers !== void 0 && liveWorkers(ctx.db, ctx.ws) >= ctx.workers) {
3635
+ stop(`the worker ceiling ${ctx.workers} is reached`);
3636
+ }
3637
+ };
3638
+ return { guard, refusedAt: () => refusedAt };
3639
+ }
3640
+ function outcomeMove(kind, task, rec, out) {
3641
+ const reason = out.reason;
3642
+ return {
3643
+ kind,
3644
+ task,
3645
+ attempt: rec.attempt,
3646
+ key: rec.key,
3647
+ agent: out.agent?.name ?? out.receipt.name,
3648
+ cli: rec.cli,
3649
+ state: out.state,
3650
+ ...out.stale !== void 0 ? { stale: out.stale } : {},
3651
+ ...reason !== void 0 ? { reason } : {},
3652
+ ...out.checked !== void 0 ? { checked: out.checked } : {}
3653
+ };
3654
+ }
3655
+ async function drive(ctx, task, kind, rec) {
3656
+ const { db, ws } = ctx;
3657
+ const g = guardOf(ctx, task, rec.key);
3658
+ try {
3659
+ const out = await spawnAutoAttempt(db, {
3660
+ task,
3661
+ text: rec.text,
3662
+ key: rec.key,
3663
+ guard: g.guard,
3664
+ ...rec.inPlace === true ? { keepAgent: true } : {},
3665
+ spawn: {
3666
+ name: rec.name,
3667
+ workstream: ws,
3668
+ cli: rec.cli,
3669
+ ...rec.worktree ? { worktree: true } : {},
3670
+ nextFree: true
3671
+ }
3672
+ });
3673
+ const move = outcomeMove(kind, task, rec, out);
3674
+ if (kind === "replay" && out.state === "unknown") {
3675
+ return {
3676
+ ...park(ctx, task, rec, {
3677
+ state: "needs_human",
3678
+ reason: out.reason ?? "the send could not be confirmed"
3679
+ }),
3680
+ ...out.checked !== void 0 ? { checked: out.checked } : {}
3681
+ };
3682
+ }
3683
+ return move;
3684
+ } catch (err) {
3685
+ if (err instanceof NoMultiplexerError) throw err;
3686
+ const base = { task, attempt: rec.attempt, key: rec.key, cli: rec.cli };
3687
+ if (err instanceof SpawnAttemptBusyError) {
3688
+ return { kind: "skip", ...base, reason: `attempt in flight: ${err.message}` };
3689
+ }
3690
+ if (err instanceof SpawnAttemptGuardError) {
3691
+ const kind2 = g.refusedAt() === "send" ? "rolled_back" : "skip";
3692
+ return { kind: kind2, ...base, reason: err.message };
3693
+ }
3694
+ const reason = err instanceof Error ? err.message : String(err);
3695
+ const taskId = taskIdFor(db, task, ws);
3696
+ const left = taskId === null ? void 0 : getReceipt(db, taskId, rec.key);
3697
+ if (left !== void 0 && IN_FLIGHT_STATES.has(left.state)) {
3698
+ const sent = left.state === "sending" || left.state === "unknown";
3699
+ park(ctx, task, rec, { state: sent ? "needs_human" : "abandoned", reason });
3700
+ }
3701
+ return { kind: "failed", ...base, reason };
3702
+ }
3703
+ }
3704
+ async function resume(ctx, task) {
3705
+ const { db, ws } = ctx;
3706
+ const taskId = taskIdFor(db, task, ws);
3707
+ const { record, receipt } = taskId === null ? {} : readAttempt(db, taskId);
3708
+ if (record === void 0 || receipt === void 0) {
3709
+ return { kind: "skip", task, reason: "no stored attempt" };
3710
+ }
3711
+ const end2 = unreconcilable(db, ws, task, receipt);
3712
+ if (end2 !== void 0) return park(ctx, task, record, end2);
3713
+ return drive(ctx, task, "replay", record);
3714
+ }
3715
+ async function allocate(ctx, task) {
3716
+ const reserved = reserveAttempt(ctx.db, ctx.ws, task);
3717
+ if (!reserved.ok) {
3718
+ return {
3719
+ kind: "skip",
3720
+ task,
3721
+ reason: `attempt ${reserved.code === "in-flight" ? "in flight" : reserved.code}: ${reserved.reason}`
3722
+ };
3723
+ }
3724
+ return drive(ctx, task, "spawn", reserved.plan);
3725
+ }
3726
+ async function recover(ctx, act, observe, signal) {
3727
+ const { db, ws } = ctx;
3728
+ const skip = (reason) => ({ kind: "skip", task: act.task, reason });
3729
+ if (ctx.humanStop()) return skip("this caller has an open human need");
3730
+ const taskId = taskIdFor(db, act.task, ws);
3731
+ if (taskId === null || !autoPermitted(db, taskId)) {
3732
+ return skip("it is no longer authorised for this machine");
3733
+ }
3734
+ if (act.kind === "reap") {
3735
+ const a = act.agent;
3736
+ const now2 = getAgent(db, a.name, a.workstreamName);
3737
+ if (now2?.host != null) return skip("a remote worker is never reaped from here");
3738
+ const deleted = deleteAgent(db, a.name, a.workstreamName, {
3739
+ paneId: a.paneId,
3740
+ incarnation: a.incarnation
3741
+ });
3742
+ return deleted ? { kind: "reap", task: act.task, agent: a.name, reason: act.why } : skip("the worker was replaced before it could be reaped");
3743
+ }
3744
+ const { agent } = act.target;
3745
+ const row = getAgent(db, agent.name, ws);
3746
+ if (row === void 0) return skip("the worker is gone");
3747
+ const now = (await observe([row], { ms: OBSERVE_MS, ...signal !== void 0 ? { signal } : {} })).get(workerKey(row));
3748
+ if (now === void 0 || !idleOnCtl(now) || now.incarnation !== act.target.pi || now.runs !== act.target.errorRuns || now.lastError === void 0) {
3749
+ return skip("the worker is no longer idle at the errored run: left to the wait");
3750
+ }
3751
+ const reserved = reserveRetry(db, ws, act.task, act.target);
3752
+ if (!reserved.ok) {
3753
+ return skip(
3754
+ `retry ${reserved.code === "ceiling" ? "ceiling" : reserved.code}: ${reserved.reason}`
3755
+ );
3756
+ }
3757
+ const { task: _t, taskId: _i, workstream: _w, revision: _r, ...rec } = reserved.plan;
3758
+ const move = await drive(ctx, act.task, "retry", rec);
3759
+ return { ...move, reason: move.reason ?? `API error: ${act.error.slice(0, 200)}` };
3760
+ }
3761
+
3762
+ // src/pi-migrate.ts
3763
+ import { createHash } from "crypto";
3764
+ import { lstatSync as lstatSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync, realpathSync as realpathSync2, renameSync } from "fs";
3765
+ import { homedir } from "os";
3766
+ import { dirname as dirname3, isAbsolute, join as join3, relative, resolve as resolve2, sep as sep2 } from "path";
3767
+ import { fileURLToPath } from "url";
3768
+
3769
+ // src/pi-known-skills.ts
3770
+ var KNOWN_SKILL_DIGESTS = [
3771
+ "bab34e7d50b31dd9960f64a8192f78f023ce3e9a5dd6a080bda3c7d3aaf68ba8",
3772
+ // v0.2.0
3773
+ "a76d272878cb90afc4f5a64ef903a0e80dd3eb7fd92dda23bcbba03854786051",
3774
+ // v0.3.0
3775
+ "cb4116919f86bff5c61268cce5059c0965b6c5673d92fb1466ebe654c560425d",
3776
+ // v0.3.1
3777
+ "1fc6a019ee71c06d311070058ba76572f6b778e160a5ebd93faeef32002a67dd",
3778
+ // v0.3.2
3779
+ "4029bcea85a7470798261041c3626b7fea16f9c84d195a3c4266221e7af4fafb",
3780
+ // v0.4.0
3781
+ "272984f7db1812241f8659a71deeac1e7d81367ecf843d6436385d6cec410000",
3782
+ // v0.4.1
3783
+ "7d58aebec9bbb40cad90b6da9e9a369cee0aebed03d194ea153c8e4229fbfe16",
3784
+ // v0.4.5
3785
+ "e4645a869fbd5153a7390d1cbfe2f3842038f895b4f754a4c44e23512270b5c7",
3786
+ // v1.0.0
3787
+ "41479003fe7f7a7d757413dcf31eb46330b9689c70aa0200347aaf6bafd972f1",
3788
+ // v1.0.1
3789
+ "1ba368054d76b931639e3dfe02bd53ed8277e47ab28d989ce3bfb7df7497dc7e",
3790
+ // v1.1.0
3791
+ "e10e2cf93a31a6b24ce1f19951e1a278299b0c33f8020bd293eff807b24fb20f",
3792
+ // v1.1.1
3793
+ "b38c8da4c685b9478aebb13905607bf295af9d9880c7da2aa1e750f24d508e28",
3794
+ // v1.2.0
3795
+ "82c14d2c9cbe7bf81f89e4031aeccee8284ffd4ddddea298547b31e1f78bef39",
3796
+ // v1.3.0
3797
+ "f787792a820b08aaa7cc338931bf239b5b54e3b536d06127aa8da2d9c938ef5a",
3798
+ // v1.3.2
3799
+ "9c5ac614ff9bf3db7d8c5aca3d2d422f8c175efa35533d0c2e174bd74682e073",
3800
+ // v1.4.0
3801
+ "6aa1d11a96d9ccd7587e979b8ef2d48d55d9b486f2ccfd160e228c57671cf340",
3802
+ // v1.6.0
3803
+ "c33778ae1c9efbb3306395e970c2e9d4a859e19aea11d6ba0429c38ea7f9d05a",
3804
+ // v2.0.0
3805
+ "8aabe979d125399274e6ed42c40353f775be61730e4c07c882d16f68d6fd3da8",
3806
+ // v3.0.0
3807
+ "bce15b51a9d8b1d0d277e7e3a79ff902d7ab306085c51eb5cb7e8e126b92564c",
3808
+ // v3.1.0
3809
+ "7dba4e5b3058e14c4314f886a26a5b5f50b99a3180d5fbe81b4751ac4e187f49",
3810
+ // v3.10.0
3811
+ "177fdc1ab4c273eef1ad004732c35fc4820a72b57991e4754d5f53063d782e49",
3812
+ // v3.10.1
3813
+ "a648aed46f7e1120ef8fbf021748a40c5434621e036fc443a817899a87dbb028",
3814
+ // v3.11.0
3815
+ "ace13205c56b40bb8de0265e4f524eec18a0583b508d7951eb6185c102e4470a",
3816
+ // v3.13.0
3817
+ "ff47c6b99435f0b30ef9fea3f6d8384c5718d7c952bbe3cada79b82ea70680fc",
3818
+ // v3.13.2
3819
+ "76e5b12be5e78f4b53981718938c33377f896be41b70be88c6feedc0e101af2b",
3820
+ // v3.13.3
3821
+ "3bd49e0b22440bd670e915683409e8929579128a9ea3f50d6c7ea3eb6ea124cc",
3822
+ // v3.2.0
3823
+ "13428e619c423d8e3dc39772343ac6be12b08b0e648113d791364c447d4f6dee",
3824
+ // v3.2.1
3825
+ "258fc3f9212387669f4b8a9442cdebfd04736378fedc4055acf2096b7759e962",
3826
+ // v3.3.0
3827
+ "f1a31d16d644bc484b3755ad9019e760098e8f089359cbd12dad3e4570ef14c0",
3828
+ // v3.4.0
3829
+ "75cb30f7291b5613df1ffa11d497ff91972c576c625141c6cb7a5ac34f206d6b",
3830
+ // v3.5.0
3831
+ "4123cd6e5a19b27bae298a1b56896e06237e8187ace51ccd8aef99c6486a6800",
3832
+ // v3.7.0
3833
+ "848eb0fe7fff3108162027f65fe1482675e5ecf074312e9799e6056c6212800f",
3834
+ // v3.8.0
3835
+ "7d9784b1c881f545e376a5ddb37da66b589e12b4dadbb204db69767835887546"
3836
+ // v3.8.1
3837
+ ];
3838
+
3839
+ // src/pi-migrate.ts
3840
+ var MU_SHIM_MARKER = "// mu:shim";
3841
+ function lstatOrUndefined(path) {
3842
+ try {
3843
+ return lstatSync2(path);
3844
+ } catch {
3845
+ return void 0;
3846
+ }
3847
+ }
3848
+ function freeName(dest) {
3849
+ if (!lstatOrUndefined(dest)) return dest;
3850
+ for (let i = 1; ; i++) {
3851
+ const candidate = `${dest}.${i}`;
3852
+ if (!lstatOrUndefined(candidate)) return candidate;
3853
+ }
3854
+ }
3855
+ function park2(from, dest) {
3856
+ const to = freeName(dest);
3857
+ mkdirSync2(join3(to, ".."), { recursive: true });
3858
+ renameSync(from, to);
3859
+ return to;
3860
+ }
3861
+ var SHIM_HEAD = `${MU_SHIM_MARKER}
3862
+ // Generated by \`mu link pi\`. Do not edit.
3863
+ //
3864
+ // A re-export, not a copy: the extension code lives in the mu install, so
3865
+ // upgrading mu upgrades the extension. Re-run \`mu link pi\` only if the
3866
+ // install path itself moves.
3867
+ const { default: extension } = await import(`;
3868
+ var SHIM_TAIL = `);
3869
+ export default extension;
3870
+ `;
3871
+ function isGeneratedImport(arg) {
3872
+ try {
3873
+ const url = JSON.parse(arg);
3874
+ return typeof url === "string" && JSON.stringify(url) === arg && new URL(url).href === url && url.startsWith("file:///") && url.endsWith("/dist/extension/mu-pi.js");
3875
+ } catch {
3876
+ return false;
3877
+ }
3878
+ }
3879
+ function isMuShimText(text) {
3880
+ return text.startsWith(SHIM_HEAD) && text.endsWith(SHIM_TAIL) && isGeneratedImport(text.slice(SHIM_HEAD.length, text.length - SHIM_TAIL.length));
3881
+ }
3882
+ function isMuShim(path) {
3883
+ try {
3884
+ return isMuShimText(readFileSync(path, "utf8"));
3885
+ } catch {
3886
+ return false;
3887
+ }
3888
+ }
3889
+ function defaultPackageRoot() {
3890
+ return resolve2(dirname3(fileURLToPath(import.meta.url)), "..");
3891
+ }
3892
+ function skillDirDigest(dir) {
3893
+ const lines = [];
3894
+ const walk = (d) => {
3895
+ for (const ent of readdirSync(d, { withFileTypes: true })) {
3896
+ const p = join3(d, ent.name);
3897
+ if (ent.isDirectory()) {
3898
+ if (!walk(p)) return false;
3899
+ } else if (ent.isFile()) {
3900
+ const sum = createHash("sha256").update(readFileSync(p)).digest("hex");
3901
+ lines.push(`${relative(dir, p).split(sep2).join("/")}\0${sum}
3902
+ `);
3903
+ } else return false;
3904
+ }
3905
+ return true;
3906
+ };
3907
+ try {
3908
+ if (!walk(dir)) return void 0;
3909
+ } catch {
3910
+ return void 0;
3911
+ }
3912
+ return createHash("sha256").update(lines.sort().join("")).digest("hex");
3913
+ }
3914
+ function isMuSkill(path, st, root, known) {
3915
+ try {
3916
+ if (st.isSymbolicLink()) {
3917
+ const own = realpathSync2(join3(root, "skills", "mu"));
3918
+ const rel = relative(own, realpathSync2(path));
3919
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
3920
+ }
3921
+ if (!st.isDirectory()) return false;
3922
+ const digest = skillDirDigest(path);
3923
+ return digest !== void 0 && known.includes(digest);
3924
+ } catch {
3925
+ return false;
3926
+ }
3927
+ }
3928
+ function migratePi(opts = {}) {
3929
+ const home = opts.home ?? process.env.MU_PI_HOME ?? homedir();
3930
+ const extPath = join3(home, ".pi", "agent", "extensions", "mu.ts");
3931
+ const skillPath = join3(home, ".agents", "skills", "mu");
3932
+ const extSt = lstatOrUndefined(extPath);
3933
+ const extension = {
3934
+ path: extPath,
3935
+ state: !extSt ? "absent" : extSt.isFile() && isMuShim(extPath) ? "shim" : "foreign",
3936
+ moved: false
3937
+ };
3938
+ const skSt = lstatOrUndefined(skillPath);
3939
+ const skill = {
3940
+ path: skillPath,
3941
+ state: !skSt ? "absent" : isMuSkill(
3942
+ skillPath,
3943
+ skSt,
3944
+ opts.packageRoot ?? defaultPackageRoot(),
3945
+ opts.knownSkillDigests ?? KNOWN_SKILL_DIGESTS
3946
+ ) ? "shim" : "foreign",
3947
+ moved: false
3948
+ };
3949
+ if (opts.apply === true) {
3950
+ if (extension.state === "shim") {
3951
+ extension.to = park2(extPath, join3(home, ".pi", "agent", "disabled", "mu.ts.shim"));
3952
+ extension.moved = true;
3953
+ }
3954
+ if (skill.state === "shim") {
3955
+ skill.to = park2(skillPath, join3(home, ".agents", "disabled", "skills", "mu"));
3956
+ skill.moved = true;
3957
+ }
3958
+ }
3959
+ const guidance = [];
3960
+ if (opts.apply !== true && (extension.state === "shim" || skill.state === "shim")) {
3961
+ guidance.push("Nothing moved. Re-run with --apply to move the mu-owned files aside.");
3962
+ }
3963
+ for (const e of [extension, skill]) {
3964
+ if (e.state === "foreign") {
3965
+ guidance.push(
3966
+ `Left ${e.path} in place: not proven mu's. If it duplicates the package, move it aside yourself.`
3967
+ );
3968
+ }
3969
+ }
3970
+ if (extension.moved || skill.moved) {
3971
+ guidance.push(
3972
+ "Running pi agents keep the old extension until they restart: restart pi or run /reload."
3973
+ );
3974
+ }
3975
+ guidance.push("Install the package once: pi install npm:@mu-crew/mu");
3976
+ return { extension, skill, guidance };
3977
+ }
3978
+
3979
+ // src/remote-collect.ts
3980
+ import { createHash as createHash2 } from "crypto";
3981
+ import { rm } from "fs/promises";
3982
+ import { dirname as dirname4, join as join4, resolve as resolve3 } from "path";
3983
+ var REMOTE_COLLECT_TIMEOUT_MS = 3e5;
3984
+ var RemoteCollectUnsupportedError = class extends Error {
3985
+ name = "RemoteCollectUnsupportedError";
3986
+ constructor(agent, why) {
3987
+ super(say("remote.collect_unsupported", agent, why));
3988
+ }
3989
+ };
3990
+ var RemoteCollectError = class extends Error {
3991
+ constructor(host, detail) {
3992
+ super(say("remote.collect_failed", host, detail));
3993
+ this.host = host;
3994
+ }
3995
+ host;
3996
+ name = "RemoteCollectError";
3997
+ };
3998
+ function recoveryTarget(db, rec) {
3999
+ return {
4000
+ host: rec.host,
4001
+ cwd: rec.cwd,
4002
+ base: rec.base,
4003
+ collectedHead: rec.collectedHead,
4004
+ ownerId: rec.agentId,
4005
+ record: (head) => head === rec.collectedHead ? recoveryHolds(db, rec) : setRecoveryCollectedHead(db, rec, head),
4006
+ recheck: () => {
4007
+ assertRecoveryUnchanged(db, rec);
4008
+ assertRecoveryUsable(db, rec);
4009
+ },
4010
+ lost: () => new RemoteRecoveryRefusedError(
4011
+ rec.workstream,
4012
+ rec.name,
4013
+ "changed",
4014
+ `recovery ${rec.agentId} changed while its commits were being collected; the pin was restored`,
4015
+ [rec.agentId]
4016
+ )
4017
+ };
4018
+ }
4019
+ function collectRef(workstream, name) {
4020
+ return `refs/mu/collect/${workstream}/${name}`;
4021
+ }
4022
+ var firstLine = (s) => s.split("\n").find((l) => l.trim() !== "")?.trim() ?? "";
4023
+ function repoLockPath(db, repo) {
4024
+ const base = db.memory || db.name === "" ? locksDir() : join4(dirname4(db.name), "locks");
4025
+ return join4(base, `collect-${createHash2("sha256").update(repo).digest("hex").slice(0, 16)}.lock`);
4026
+ }
4027
+ async function collectRemoteAgent(db, opts) {
4028
+ const { workstream, name, repo } = opts;
4029
+ return withAgentLock(
4030
+ db,
4031
+ workstream,
4032
+ name,
4033
+ () => withFileLock(
4034
+ repoLockPath(db, repo),
4035
+ `collect ${repo}`,
4036
+ async () => {
4037
+ const agent = getAgent(db, name, workstream);
4038
+ const rec = opts.recoveryId !== void 0 || agent === void 0 ? selectRemoteRecovery(db, workstream, name, opts.recoveryId) : void 0;
4039
+ if (rec === void 0 && opts.recoveryId !== void 0) {
4040
+ throw recoveryGone(workstream, name, opts.recoveryId);
4041
+ }
4042
+ let target;
4043
+ if (rec !== void 0) {
4044
+ assertRecoveryUsable(db, rec);
4045
+ target = recoveryTarget(db, rec);
4046
+ } else if (agent !== void 0) {
4047
+ target = agentTarget(db, agent);
4048
+ } else {
4049
+ throw new AgentNotFoundError(name, workstream);
4050
+ }
4051
+ const { host, cwd, base } = target;
4052
+ const r = await withHostChannel(
4053
+ db,
4054
+ host,
4055
+ `collect:${workstream}/${name}`,
4056
+ async (profile, lease) => {
4057
+ if (rec !== void 0) assertRecoveryProfile(rec, profile);
4058
+ const fetchHead = await git(["rev-parse", "--git-path", "FETCH_HEAD"], repo);
4059
+ await rm(resolve3(repo, fetchHead), { force: true });
4060
+ return runLeasedTemplate(
4061
+ db,
4062
+ lease,
4063
+ profile,
4064
+ "fetch",
4065
+ { cwd },
4066
+ REMOTE_COLLECT_TIMEOUT_MS,
4067
+ repo
4068
+ );
4069
+ },
4070
+ { paneAlive: localPaneAlive }
4071
+ );
4072
+ if (r.exitCode === null) {
4073
+ throw new RemoteCollectError(
4074
+ host,
4075
+ `timed out after ${REMOTE_COLLECT_TIMEOUT_MS}ms; nothing recorded`
4076
+ );
4077
+ }
4078
+ if (r.exitCode !== 0) {
4079
+ const why = firstLine(r.stderr) || firstLine(r.stdout);
4080
+ throw new RemoteCollectError(
4081
+ host,
4082
+ `fetch exited ${r.exitCode}${why === "" ? "" : `: ${why}`}; nothing recorded`
4083
+ );
4084
+ }
4085
+ let head;
4086
+ try {
4087
+ head = await git(["rev-parse", "--verify", "FETCH_HEAD^{commit}"], repo);
4088
+ } catch {
4089
+ throw new RemoteCollectError(
4090
+ host,
4091
+ "the fetch template succeeded but left no commit in FETCH_HEAD (does it `git fetch`?); nothing recorded"
4092
+ );
4093
+ }
4094
+ target.recheck();
4095
+ const ref = collectRef(workstream, name);
4096
+ const prior = await git(["rev-parse", "--verify", "-q", ref], repo).catch(() => void 0);
4097
+ if (prior !== void 0 && prior !== head) {
4098
+ const holders = pinnedByOthers(db, { workstream, name, agentId: target.ownerId }, prior);
4099
+ if (holders.length > 0) {
4100
+ throw new RemoteRecoveryRefusedError(
4101
+ workstream,
4102
+ name,
4103
+ "pin",
4104
+ `${ref} pins ${prior.slice(0, 12)}, the collected HEAD of recovery ${holders.join(", ")}; close that recovery first, then collect`,
4105
+ holders
4106
+ );
4107
+ }
4108
+ }
4109
+ await git(["update-ref", ref, head], repo);
4110
+ if (await git(["rev-parse", "--verify", ref], repo) !== head) {
4111
+ throw new RemoteCollectError(host, `could not pin ${ref}; nothing recorded`);
4112
+ }
4113
+ const changed = head !== (target.collectedHead ?? base);
4114
+ await recordCollected(target, head, ref, repo, prior);
4115
+ return {
4116
+ head,
4117
+ ref,
4118
+ merge: `git merge ${head}`,
4119
+ changed,
4120
+ ...rec === void 0 ? {} : { recoveryId: rec.agentId }
4121
+ };
4122
+ },
4123
+ {
4124
+ strict: true,
4125
+ onUnavailable: () => {
4126
+ throw new AgentLockBusyError(name, workstream);
4127
+ }
4128
+ }
4129
+ )
4130
+ );
4131
+ }
4132
+ function agentTarget(db, agent) {
4133
+ const { host, cwd, base } = agent;
4134
+ if (host === null || cwd === null) {
4135
+ throw new RemoteCollectUnsupportedError(agent.name, "it is a local agent");
4136
+ }
4137
+ if (base === null) {
4138
+ throw new RemoteCollectUnsupportedError(
4139
+ agent.name,
4140
+ "it was spawned with --cwd, not --worktree: there is no remote worktree of its own"
4141
+ );
4142
+ }
4143
+ const fence = "id = ? AND pane_id = ? AND host IS ? AND cwd IS ? AND base IS ?";
4144
+ const who = [agent.incarnation, agent.paneId, agent.host, agent.cwd, agent.base];
4145
+ return {
4146
+ host,
4147
+ cwd,
4148
+ base,
4149
+ collectedHead: agent.collectedHead,
4150
+ ownerId: agent.incarnation,
4151
+ // An unchanged head writes nothing, but is still fenced: success names this incarnation.
4152
+ record: (head) => head === agent.collectedHead ? db.prepare(`SELECT 1 FROM agents WHERE ${fence}`).all(...who).length === 1 : db.prepare(`UPDATE agents SET collected_head = ?, updated_at = ? WHERE ${fence}`).run(head, (/* @__PURE__ */ new Date()).toISOString(), ...who).changes === 1,
4153
+ recheck: () => {
4154
+ const now = getAgent(db, agent.name, agent.workstreamName);
4155
+ if (now?.incarnation !== agent.incarnation || now.paneId !== agent.paneId) {
4156
+ throw new AgentReplacedError(agent.name, agent.workstreamName, "collected");
4157
+ }
4158
+ },
4159
+ lost: () => new AgentReplacedError(agent.name, agent.workstreamName, "collected")
4160
+ };
4161
+ }
4162
+ async function recordCollected(target, head, ref, repo, prior) {
4163
+ if (target.record(head)) return;
4164
+ await (prior === void 0 ? git(["update-ref", "-d", ref, head], repo) : git(["update-ref", ref, prior, head], repo)).catch(() => {
4165
+ });
4166
+ throw target.lost();
4167
+ }
4168
+
4169
+ // src/tasks/sort.ts
4170
+ var TASK_SORT_KEYS = ["roi", "recency", "age", "id"];
4171
+ function isTaskSortKey(s) {
4172
+ return TASK_SORT_KEYS.includes(s);
4173
+ }
4174
+ function roiOf(t) {
4175
+ return t.effortDays > 0 ? t.impact / t.effortDays : Number.POSITIVE_INFINITY;
4176
+ }
4177
+ function byRoiDesc(a, b) {
4178
+ if (a.scored !== b.scored) return a.scored ? -1 : 1;
4179
+ if (!a.scored) {
4180
+ const byCreated = compareStrings(a.createdAt, b.createdAt);
4181
+ return byCreated !== 0 ? byCreated : compareStrings(a.name, b.name);
4182
+ }
4183
+ const ra = roiOf(a);
4184
+ const rb = roiOf(b);
4185
+ if (rb !== ra) return rb - ra;
4186
+ if (a.effortDays !== b.effortDays) return a.effortDays - b.effortDays;
4187
+ return compareStrings(a.name, b.name);
4188
+ }
4189
+ function sortTasks(tasks, key) {
4190
+ const out = tasks.slice();
4191
+ switch (key) {
4192
+ case "roi":
4193
+ return out.sort(byRoiDesc);
4194
+ case "recency":
4195
+ return out.sort((a, b) => {
4196
+ const byUpdated = compareStrings(b.updatedAt, a.updatedAt);
4197
+ return byUpdated !== 0 ? byUpdated : compareStrings(a.name, b.name);
4198
+ });
4199
+ case "age":
4200
+ return out.sort((a, b) => {
4201
+ const byCreated = compareStrings(a.createdAt, b.createdAt);
4202
+ return byCreated !== 0 ? byCreated : compareStrings(a.name, b.name);
4203
+ });
4204
+ case "id":
4205
+ return out.sort((a, b) => compareStrings(a.name, b.name));
4206
+ }
4207
+ }
4208
+ function relTimeBasisForSort(key) {
4209
+ if (key === "recency") return "updatedAt";
4210
+ if (key === "age") return "createdAt";
4211
+ return null;
4212
+ }
4213
+
4214
+ // src/state.ts
4215
+ var RECENT_CLOSED = 5;
4216
+ var BIG_WORKSTREAM_TASKS = 300;
4217
+ function taskActivity(db, workstream) {
4218
+ const prefix = `${workstream}/`;
4219
+ const rows = db.prepare(
4220
+ `SELECT entity, key, MAX(seq) AS seq FROM ops
4221
+ WHERE entity IN ('task', 'note', 'edge') AND key >= ? AND key < ?
4222
+ GROUP BY entity, key`
4223
+ ).all(prefix, `${workstream}0`);
4224
+ const out = /* @__PURE__ */ new Map();
4225
+ const bump = (id, seq) => {
4226
+ if (seq > (out.get(id) ?? 0)) out.set(id, seq);
4227
+ };
4228
+ for (const r of rows) {
4229
+ if (r.entity === "task") bump(r.key.slice(prefix.length), r.seq);
4230
+ else if (r.entity === "note") bump(r.key.slice(prefix.length).split("#")[0] ?? "", r.seq);
4231
+ else {
4232
+ const [from, to] = r.key.split("->");
4233
+ if (from !== void 0) bump(from.slice(prefix.length), r.seq);
4234
+ if (to !== void 0) bump(to.slice(prefix.length), r.seq);
4235
+ }
4236
+ }
4237
+ return out;
4238
+ }
4239
+ function stateTask(row, activity) {
4240
+ return {
4241
+ id: row.name,
4242
+ title: row.title,
4243
+ status: row.status,
4244
+ substate: row.substate,
4245
+ impact: row.impact,
4246
+ effort_days: row.effortDays,
4247
+ owner: row.status === "IN_PROGRESS" ? row.ownerName : null,
4248
+ kind: row.kind,
4249
+ wave: row.wave,
4250
+ updated_at: row.updatedAt,
4251
+ activity_seq: activity.get(row.name) ?? 0
4252
+ };
4253
+ }
4254
+ function byActivityDesc(a, b) {
4255
+ if (a.activity_seq !== b.activity_seq) return b.activity_seq - a.activity_seq;
4256
+ if (a.updated_at !== b.updated_at) return a.updated_at < b.updated_at ? 1 : -1;
4257
+ return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
4258
+ }
4259
+ var FINISHED_CALL = ["done", "failed", "cancelled"];
4260
+ function delegatesOf(db, workstream, caller) {
4261
+ if (caller === null) return { delegates: [], waves: [] };
4262
+ const calls = listDelegates(db, caller, workstream);
4263
+ const delegates = calls.filter((c) => !FINISHED_CALL.includes(c.state)).map((c) => ({
4264
+ call_id: c.callId,
4265
+ task: c.task,
4266
+ state: c.state,
4267
+ wave: c.wave,
4268
+ judges: c.record ? `${c.record.workstream}/${c.record.task}` : null,
4269
+ agent: c.agent,
4270
+ verdict: c.verdict,
4271
+ error: c.error,
4272
+ updated_at: c.updatedAt
4273
+ }));
4274
+ const labels = [...new Set(calls.flatMap((c) => c.wave === null ? [] : [c.wave]))].reverse();
4275
+ const waves = labels.map((wave) => waveOf(verdictTally(db, workstream, wave, [caller]), wave));
4276
+ return { delegates, waves };
4277
+ }
4278
+ function waveOf(t, wave) {
4279
+ const sum = (k) => t.findings.reduce((n, f) => n + f[k], 0);
4280
+ const w = {
4281
+ wave,
4282
+ confirmed: sum("confirmed"),
4283
+ refuted: sum("refuted"),
4284
+ unverified: sum("unverified"),
4285
+ pending: sum("pending")
4286
+ };
4287
+ return {
4288
+ ...w,
4289
+ calls: w.confirmed + w.refuted + w.unverified + w.pending,
4290
+ queued: t.queued,
4291
+ running: t.running,
4292
+ refused: t.refused
4293
+ };
4294
+ }
4295
+ function countsOf(db, wsId) {
4296
+ const n = (sql) => db.prepare(sql).get(wsId).n;
4297
+ const by = (cond) => n(`SELECT count(*) AS n FROM tasks WHERE workstream_id = ? AND ${cond}`);
4298
+ return {
4299
+ tasks: by("1 = 1"),
4300
+ open: by("status = 'OPEN'"),
4301
+ parked: by("status = 'OPEN' AND substate = 'parked'"),
4302
+ triage: by("status = 'OPEN' AND substate = 'triage'"),
4303
+ ready: n("SELECT count(*) AS n FROM ready WHERE workstream_id = ?"),
4304
+ blocked: n("SELECT count(*) AS n FROM blocked WHERE workstream_id = ?"),
4305
+ in_progress: by("status = 'IN_PROGRESS'"),
4306
+ closed: by("status = 'CLOSED'")
4307
+ };
4308
+ }
4309
+ async function meOf(db, caller, self) {
4310
+ if (self === null) return { caller: caller ?? "", agent: null, owned_tasks: [] };
4311
+ const ws = self.workstreamName;
4312
+ const wsRow = db.prepare("SELECT id, torn_down_at FROM workstreams WHERE name = ?").get(ws);
4313
+ const [live] = await readLiveAgents(db, ws, self.name);
4314
+ const live_ = live === void 0 ? null : liveAgentToOutput(live, wsRow?.id ?? 0);
4315
+ const alive = wsRow !== void 0 && wsRow.torn_down_at === null;
4316
+ const activity = alive ? taskActivity(db, ws) : /* @__PURE__ */ new Map();
4317
+ return {
4318
+ caller: caller ?? `${ws}/${self.name}`,
4319
+ agent: live_,
4320
+ owned_tasks: alive ? listTasksByOwner(db, ws, self.name).map((r) => stateTask(r, activity)).sort(byActivityDesc) : []
4321
+ };
4322
+ }
4323
+ function callerWorkstreams(db, caller) {
4324
+ const live = new Set(
4325
+ db.prepare("SELECT name FROM workstreams WHERE torn_down_at IS NULL").all().map((r) => r.name)
4326
+ );
4327
+ const names = /* @__PURE__ */ new Set([
4328
+ ...listDelegates(db, caller).map((c) => c.workstream),
4329
+ ...openHumanNeeds(db, caller).map((h) => h.workstream)
4330
+ ]);
4331
+ return [...names].filter((n) => live.has(n)).sort();
4332
+ }
4333
+ async function stateSnapshot(db, opts) {
4334
+ const needs = opts.caller === null ? [] : openHumanNeeds(db, opts.caller);
4335
+ const workstreams = [];
4336
+ let me = null;
4337
+ for (const ws of opts.workstreams) {
4338
+ const wsId = tryResolveLiveWorkstreamId(db, ws);
4339
+ if (wsId === null) throw new WorkstreamNotFoundError(ws);
4340
+ const activity = taskActivity(db, ws);
4341
+ const task = (r) => stateTask(r, activity);
4342
+ const state = {
4343
+ workstream: ws,
4344
+ workstream_id: wsId,
4345
+ counts: countsOf(db, wsId),
4346
+ ready: listReady(db, ws).sort(byRoiDesc).map(task),
4347
+ ...delegatesOf(db, ws, opts.caller),
4348
+ human_needs: needs.filter((h) => h.workstream === ws).map((h) => ({ id: h.id, reason: h.reason }))
4349
+ };
4350
+ if (opts.me === void 0) {
4351
+ state.in_progress = listInProgress(db, ws).map(task).sort(byActivityDesc);
4352
+ state.blocked = listBlocked(db, ws).map(task).sort(byActivityDesc);
4353
+ state.triage = listTasks(db, ws, { status: "OPEN", substate: "triage" }).map(task).sort(byActivityDesc);
4354
+ state.recent_closed = listTasks(db, ws, { status: "CLOSED" }).map(task).sort(byActivityDesc).slice(0, RECENT_CLOSED);
4355
+ state.agents = (await readLiveAgents(db, ws)).map((a) => liveAgentToOutput(a, wsId));
4356
+ const { tasks, open, in_progress } = state.counts;
4357
+ const unfinished = open + in_progress;
4358
+ if (tasks >= BIG_WORKSTREAM_TASKS && unfinished * 10 < tasks) {
4359
+ state.hint = `${tasks} tasks, ${unfinished} open; start the next effort in a new <project>-<purpose> workstream`;
4360
+ }
4361
+ }
4362
+ workstreams.push(state);
4363
+ }
4364
+ if (opts.me !== void 0) me = await meOf(db, opts.caller, opts.me.agent);
4365
+ return { caller: opts.caller, me, workstreams };
4366
+ }
4367
+
4368
+ // src/wait-resume.ts
4369
+ import { randomBytes } from "crypto";
4370
+ var RESUME_KEEP_DAYS = 14;
4371
+ var WaitResumeNotFoundError = class extends Error {
4372
+ constructor(resumeId) {
4373
+ super(say("wait.resume_not_found", resumeId, RESUME_KEEP_DAYS));
4374
+ this.resumeId = resumeId;
4375
+ }
4376
+ resumeId;
4377
+ name = "WaitResumeNotFoundError";
4378
+ };
4379
+ function saveResume(db, callers, spec) {
4380
+ const now = /* @__PURE__ */ new Date();
4381
+ const cutoff = new Date(now.getTime() - RESUME_KEEP_DAYS * 864e5).toISOString();
4382
+ for (let attempt = 0; ; attempt++) {
4383
+ const id = randomBytes(5).toString("hex");
4384
+ try {
4385
+ db.transaction(() => {
4386
+ db.prepare("DELETE FROM wait_resumes WHERE created_at < ?").run(cutoff);
4387
+ db.prepare(
4388
+ "INSERT INTO wait_resumes (id, callers_json, spec_json, created_at) VALUES (?, ?, ?, ?)"
4389
+ ).run(id, JSON.stringify(callers), JSON.stringify(spec), now.toISOString());
4390
+ }).immediate();
4391
+ return id;
4392
+ } catch (e) {
4393
+ if (attempt < 3 && /UNIQUE|PRIMARY KEY/i.test(String(e))) continue;
4394
+ throw e;
4395
+ }
4396
+ }
4397
+ }
4398
+ function loadResume(db, id) {
4399
+ const row = db.prepare("SELECT callers_json AS callers, spec_json AS spec FROM wait_resumes WHERE id = ?").get(id);
4400
+ if (row === void 0) throw new WaitResumeNotFoundError(id);
4401
+ try {
4402
+ const callers = JSON.parse(row.callers);
4403
+ const spec = JSON.parse(row.spec);
4404
+ if (!Array.isArray(callers) || !Array.isArray(spec.refs)) throw new Error("bad shape");
4405
+ return { id, callers, spec };
4406
+ } catch {
4407
+ throw new WaitRefError(`wait resume handle ${id} is unreadable (its stored row is damaged)`);
4408
+ }
4409
+ }
4410
+
4411
+ export {
4412
+ RebuildTargetExistsError,
4413
+ RebuildTargetIsSourceError,
4414
+ rebuildInto,
4415
+ DRIFT_REPORT_CAP,
4416
+ checkCheapDriftInvariant,
4417
+ checkDrift,
4418
+ formatDriftRecord,
4419
+ driftRemediation,
4420
+ DriftDetectedError,
4421
+ HazardFailError,
4422
+ DbInsideSyncDirError,
4423
+ isPathInside,
4424
+ checkDbInsideSyncDir,
4425
+ classifyFsType,
4426
+ probeFilesystem,
4427
+ checkNetworkMount,
4428
+ findCaseCollisions,
4429
+ checkCaseCollisions,
4430
+ checkFleetHazards,
4431
+ checkSegmentVersion,
4432
+ checkOrphanedAgents,
4433
+ checkCloseGuards,
4434
+ checkUnpublishedOps,
4435
+ AGENT_STATE_GLYPH,
4436
+ agentStateGlyph,
4437
+ GLYPH,
4438
+ WAIT_HEARTBEAT_CAP_MS,
4439
+ DEFAULT_STUCK_AFTER_MS,
4440
+ DEFAULT_CTL_STUCK_AFTER_MS,
4441
+ WaitNothingError,
4442
+ WaveNotFoundError,
4443
+ WaitRefError,
4444
+ WAIT_REGEX_MAX_LEN,
4445
+ WAIT_TEXT_MAX,
4446
+ WaitRegexError,
4447
+ checkRegexGrammar,
4448
+ compileTextMatcher,
4449
+ waitForEvents,
4450
+ getActionReceipt,
4451
+ reserveActionReceipt,
4452
+ transitionActionReceipt,
4453
+ releaseStall,
4454
+ releaseStallHandler,
4455
+ steerText,
4456
+ steerStall,
4457
+ steerStallHandler,
4458
+ waitWithActions,
4459
+ ctlAgentHooks,
4460
+ NextArgError,
4461
+ DEFAULT_NEXT_CAP,
4462
+ MAX_NEXT_CAP,
4463
+ executeNext,
4464
+ MU_SHIM_MARKER,
4465
+ skillDirDigest,
4466
+ migratePi,
4467
+ RemoteCollectUnsupportedError,
4468
+ RemoteCollectError,
4469
+ collectRef,
4470
+ collectRemoteAgent,
4471
+ TASK_SORT_KEYS,
4472
+ isTaskSortKey,
4473
+ byRoiDesc,
4474
+ sortTasks,
4475
+ relTimeBasisForSort,
4476
+ callerWorkstreams,
4477
+ stateSnapshot,
4478
+ WaitResumeNotFoundError,
4479
+ saveResume,
4480
+ loadResume
4481
+ };
4482
+ //# sourceMappingURL=chunk-5HECDBK3.js.map