@mu-crew/mu 3.8.1 → 3.10.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 (54) hide show
  1. package/AGENTS.md +2 -1
  2. package/README.md +1 -0
  3. package/dist/chunk-4PACTXAB.js +17 -0
  4. package/dist/chunk-4PACTXAB.js.map +1 -0
  5. package/dist/chunk-77GBZ2KR.js +85 -0
  6. package/dist/chunk-77GBZ2KR.js.map +1 -0
  7. package/dist/chunk-7OQNJWBY.js +391 -0
  8. package/dist/chunk-7OQNJWBY.js.map +1 -0
  9. package/dist/chunk-PZ5AY32C.js +10 -0
  10. package/dist/chunk-PZ5AY32C.js.map +1 -0
  11. package/dist/chunk-WT7IN2OD.js +9887 -0
  12. package/dist/chunk-WT7IN2OD.js.map +1 -0
  13. package/dist/chunk-XXK7FR37.js +640 -0
  14. package/dist/chunk-XXK7FR37.js.map +1 -0
  15. package/dist/chunk-YIF3X32A.js +2792 -0
  16. package/dist/chunk-YIF3X32A.js.map +1 -0
  17. package/dist/cli-UZEMPIPW.js +6266 -0
  18. package/dist/cli-UZEMPIPW.js.map +1 -0
  19. package/dist/cli.js +37 -25280
  20. package/dist/cli.js.map +1 -1
  21. package/dist/extension/mu-pi.js +230 -138
  22. package/dist/extension/mu-pi.js.map +1 -1
  23. package/dist/index.d.ts +543 -142
  24. package/dist/index.js +340 -11169
  25. package/dist/index.js.map +1 -1
  26. package/dist/tui-X2XICJTN.js +6452 -0
  27. package/dist/tui-X2XICJTN.js.map +1 -0
  28. package/dist/tui-sync-worker.js +16 -0
  29. package/dist/tui-sync-worker.js.map +1 -0
  30. package/dist/yank-T3AYQTVR.js +12 -0
  31. package/dist/yank-T3AYQTVR.js.map +1 -0
  32. package/docs/ARCHITECTURE.md +14 -6
  33. package/docs/VISION.md +14 -7
  34. package/docs/VOCABULARY.md +5 -6
  35. package/docs/architecture/control-socket.md +6 -2
  36. package/docs/architecture/mux.md +4 -2
  37. package/docs/architecture/ops-log.md +2 -1
  38. package/docs/architecture/sdk.md +3 -1
  39. package/docs/architecture/sync.md +32 -10
  40. package/docs/architecture/tui.md +18 -8
  41. package/docs/guide/backends.md +5 -3
  42. package/docs/guide/cleanup.md +8 -3
  43. package/docs/guide/delegate.md +3 -1
  44. package/docs/guide/dispatch.md +9 -3
  45. package/docs/guide/recovery.md +19 -8
  46. package/docs/guide/sql.md +11 -4
  47. package/docs/guide/stop-a-worker.md +3 -1
  48. package/docs/guide/tui.md +3 -3
  49. package/docs/guide/upgrade.md +8 -2
  50. package/docs/reference/env.md +11 -5
  51. package/docs/reference/naming.md +3 -4
  52. package/package.json +1 -1
  53. package/skills/mu/SKILL.md +2 -2
  54. package/skills/mu/recipes/drift-audit.md +5 -3
package/dist/index.d.ts CHANGED
@@ -84,6 +84,12 @@ interface HasNextSteps {
84
84
  errorNextSteps(): NextStep[];
85
85
  }
86
86
 
87
+ /**
88
+ * Resolve the canonical mu state directory:
89
+ * MU_STATE_DIR > $XDG_STATE_HOME/mu (absolute only) > ~/.local/state/mu
90
+ */
91
+ declare function defaultStateDir(): string;
92
+
87
93
  type Db = Database;
88
94
  interface OpenDbOptions {
89
95
  /**
@@ -98,11 +104,6 @@ interface OpenDbOptions {
98
104
  */
99
105
  readonly?: boolean;
100
106
  }
101
- /**
102
- * Resolve the canonical mu state directory:
103
- * MU_STATE_DIR > $XDG_STATE_HOME/mu > ~/.local/state/mu
104
- */
105
- declare function defaultStateDir(): string;
106
107
  /**
107
108
  * Resolve the canonical DB path:
108
109
  * MU_DB_PATH > <state-dir>/mu.db
@@ -110,10 +111,17 @@ declare function defaultStateDir(): string;
110
111
  declare function defaultDbPath(): string;
111
112
  /**
112
113
  * Open the mu database. Creates the parent directory and applies the schema
113
- * idempotently on every open. Safe to call from many short-lived processes
114
+ * idempotently when it is missing or differs from this build (an
115
+ * up-to-date DB opens without a write). Safe to call from many short-lived processes
114
116
  * concurrently — WAL mode handles cross-process writes.
115
117
  */
116
118
  declare function openDb(options?: OpenDbOptions): Db;
119
+ declare class WorkstreamNotFoundError extends Error implements HasNextSteps {
120
+ readonly workstream: string;
121
+ readonly name = "WorkstreamNotFoundError";
122
+ constructor(workstream: string);
123
+ errorNextSteps(): NextStep[];
124
+ }
117
125
  /**
118
126
  * Thrown by openDb when the on-disk DB is older than the current
119
127
  * schema. There is no in-place migration ladder; the old DB is left
@@ -126,6 +134,17 @@ declare class SchemaTooOldError extends Error implements HasNextSteps {
126
134
  constructor(detectedVersion: number, requiredVersion: number);
127
135
  errorNextSteps(): NextStep[];
128
136
  }
137
+ /**
138
+ * Thrown by openDb when the on-disk DB was written by a NEWER mu.
139
+ * The DB is left untouched. Maps to exit code 4 (conflict).
140
+ */
141
+ declare class SchemaTooNewError extends Error implements HasNextSteps {
142
+ readonly detectedVersion: number;
143
+ readonly supportedVersion: number;
144
+ readonly name = "SchemaTooNewError";
145
+ constructor(detectedVersion: number, supportedVersion: number);
146
+ errorNextSteps(): NextStep[];
147
+ }
129
148
  /** The schema version a fresh DB starts at. v11 adds tasks.substate
130
149
  * and the task_substates lookup table on top of v10's three-state
131
150
  * lifecycle (OPEN, IN_PROGRESS, CLOSED). See CHANGELOG.md. */
@@ -133,13 +152,14 @@ declare const CURRENT_SCHEMA_VERSION = 11;
133
152
  /** Tables a healthy DB must contain. Single source of truth so
134
153
  * `mu doctor` and any other consumer don't drift. Adding a new table
135
154
  * = one new entry here AND a CREATE TABLE in CURRENT_SCHEMA, plus a
136
- * CURRENT_SCHEMA_VERSION bump. Sorted; exactly 11 entries in v11. */
155
+ * CURRENT_SCHEMA_VERSION bump (sync_fingerprints went in without one:
156
+ * additive, and an older mu ignores it). Sorted; exactly 12 entries. */
137
157
  declare const EXPECTED_TABLES: readonly string[];
138
158
  /** Op entities that cross machines. Everything else is machine-local.
139
159
  * Readonly tuple (not `string[]`) so `SyncedEntity` is a real union
140
160
  * and downstream code gets compile-time checking, not raw strings. */
141
161
  declare const SYNCED_ENTITIES: readonly ["workstream", "task", "edge", "note", "message"];
142
- /** One of the six op entities that sync. Derived from the tuple, so
162
+ /** One of the op entities that sync. Derived from the tuple, so
143
163
  * adding an entity is a one-line change with no type to keep in step. */
144
164
  type SyncedEntity = (typeof SYNCED_ENTITIES)[number];
145
165
  /** Op entities this build KNOWS are machine-local: their payloads name
@@ -177,6 +197,7 @@ type PortableTable = (typeof PORTABLE_TABLES)[number];
177
197
  * machine_identity IS the per-machine identity.
178
198
  * schema_version local bookkeeping.
179
199
  * sync_peers local bookkeeping (per-peer watermarks).
200
+ * sync_fingerprints local cache of what the last clean ingest saw.
180
201
  * task_substates seeded identically on every machine from
181
202
  * TASK_SUBSTATE_ROWS; code, not data.
182
203
  * ops see below — the carrier, not cargo.
@@ -193,7 +214,7 @@ type PortableTable = (typeof PORTABLE_TABLES)[number];
193
214
  * is an FK into `agents`, and `agents` is machine-local. Therefore
194
215
  * OWNERSHIP DOES NOT SYNC. The deleted db-sync.ts reached the same
195
216
  * conclusion via an `includeOwners` flag; here it is structural. */
196
- declare const MACHINE_LOCAL_TABLES: readonly ["agents", "machine_identity", "ops", "schema_version", "sync_peers", "task_substates", "vcs_workspaces"];
217
+ declare const MACHINE_LOCAL_TABLES: readonly ["agents", "machine_identity", "ops", "schema_version", "sync_fingerprints", "sync_peers", "task_substates", "vcs_workspaces"];
197
218
  type MachineLocalTable = (typeof MACHINE_LOCAL_TABLES)[number];
198
219
 
199
220
  /** The multiplexers mu can drive. */
@@ -466,9 +487,9 @@ interface MuxBackend {
466
487
  enableMuPaneBordersForSession(session: string): Promise<number>;
467
488
  enableMuPaneBordersForPane(paneId: string): Promise<void>;
468
489
  /** Copy-pasteable shell line that lands the user on `target`. */
469
- attachHint(target: AttachTarget): string;
490
+ attachHint(target: AttachTarget): string | Promise<string>;
470
491
  /** The same attach, as argv steps to spawn in order. */
471
- attachCommands(target: AttachTarget): readonly MuxCommand[];
492
+ attachCommands(target: AttachTarget): readonly MuxCommand[] | Promise<readonly MuxCommand[]>;
472
493
  /** Backend-specific `PaneNotFoundError` remediation. */
473
494
  paneNotFoundNextSteps(paneId: string): NextStep[];
474
495
  /** Version + ambient facts for `mu doctor`. Never throws: an
@@ -632,11 +653,12 @@ declare class HerdrWorkspaceGroupCloseError extends Error {
632
653
  constructor(session: string, workspaceId: string, detail: string);
633
654
  errorNextSteps(): NextStep[];
634
655
  }
635
- /** A `MuxBackend` method whose herdr implementation is owned by another
636
- * task. Never thrown on any path mu currently drives on herdr. */
656
+ /** A creation verb was handed a command to run. herdr cannot do that
657
+ * in one step (see `rejectCommand`), so this is a bug in the caller:
658
+ * spawn passes no command on herdr and calls `startAgentInPane`. */
637
659
  declare class HerdrNotImplementedError extends Error {
638
660
  readonly name = "HerdrNotImplementedError";
639
- constructor(method: string, owner: string);
661
+ constructor(method: string);
640
662
  }
641
663
  interface HerdrExecResult {
642
664
  stdout: string;
@@ -762,7 +784,8 @@ declare function newSessionWithPane(name: string, opts: NewSessionWithPaneOption
762
784
  * Four swallowed shapes:
763
785
  * - "can't find session: <name>" — session never existed.
764
786
  * - "session not found" — alternate phrasing on some tmux builds.
765
- * - "no server running on <path>" — the tmux server itself has exited
787
+ * - "no server running on <path>" (or "error connecting to <path> (No
788
+ * such file or directory)", see NO_SERVER_RE) — the tmux server itself has exited
766
789
  * (typical when the test suite runs against a private `tmux -L
767
790
  * <socket>` server and the just-killed session was its last; tmux
768
791
  * quietly shuts the server down). Without this, killSession would
@@ -922,7 +945,10 @@ declare function defaultSendReadinessMs(): number;
922
945
  declare function hasWorkMarker(scrollback: string): boolean;
923
946
  /**
924
947
  * Block until the pane is ready to ACCEPT input, or the budget expires.
925
- * Returns true if the pane quiesced, false on timeout.
948
+ * Returns true if the pane quiesced, false on timeout. Throws the
949
+ * capture's TmuxError at once when the pane or the server is gone:
950
+ * a dead pane never quiesces, so polling it would only burn the whole
951
+ * budget before the send fails anyway.
926
952
  *
927
953
  * WHY THIS EXISTS (dogfood_send_after_new_dropped, reproduced 3/6 at
928
954
  * sleep=0.3s and 1/5 at sleep=2s against a real pi pane):
@@ -999,7 +1025,12 @@ declare function sendToPane(paneId: string, text: string, opts?: SendOptions): P
999
1025
  *
1000
1026
  * - No options: full scrollback (`-S - -E -`)
1001
1027
  * - `lines: 0`: visible pane only
1002
- * - `lines: N`: last N lines (`-S -N`)
1028
+ * - `lines: N`: last N lines, ending at the last non-blank row
1029
+ *
1030
+ * `-S -N` counts from the TOP of the visible screen, so tmux returns the
1031
+ * N rows above the screen plus every visible row, blank rows below the
1032
+ * cursor included. That range always holds the last N non-blank-ended
1033
+ * lines, so drop the trailing blank rows and keep the last N.
1003
1034
  */
1004
1035
  declare function capturePane(paneId: string, opts?: CaptureOptions): Promise<string>;
1005
1036
  declare function attachHint(target: AttachTarget): string;
@@ -1011,12 +1042,18 @@ declare function paneNotFoundNextSteps(paneId: string): NextStep[];
1011
1042
  * reporting different env vars needs no doctor change.
1012
1043
  */
1013
1044
  declare function healthCheck(): Promise<MuxHealth>;
1045
+ /** `MuxBackend` with tmux's synchronous attach methods: SDK callers of
1046
+ * `tmuxBackend.attachHint()` keep getting a string, not a Promise. */
1047
+ interface TmuxBackend extends MuxBackend {
1048
+ attachHint(target: AttachTarget): string;
1049
+ attachCommands(target: AttachTarget): readonly MuxCommand[];
1050
+ }
1014
1051
  /**
1015
1052
  * The tmux implementation of `MuxBackend`. A frozen record of the
1016
1053
  * module's functions — no state of its own, so swapping backends is a
1017
1054
  * pointer assignment (see ./detect.ts).
1018
1055
  */
1019
- declare const tmuxBackend: MuxBackend;
1056
+ declare const tmuxBackend: TmuxBackend;
1020
1057
 
1021
1058
  type mux_AttachTarget = AttachTarget;
1022
1059
  type mux_CaptureOptions = CaptureOptions;
@@ -1075,17 +1112,14 @@ declare namespace mux {
1075
1112
  * "full" Default for `mu state` and `mu agent list`. Prunes
1076
1113
  * ghosts (deleting the registry row, which fires the
1077
1114
  * deleteAgent reaper that flips IN_PROGRESS tasks back
1078
- * to OPEN with [reaper] notes), runs status detection
1079
- * against surviving panes, surfaces orphans.
1115
+ * to OPEN with [reaper] notes), refreshes pane titles
1116
+ * of surviving agents, surfaces orphans.
1080
1117
  *
1081
1118
  * "report-only" Pure observation. Counts would-be-pruned ghosts
1082
- * without deleting; skips status detection entirely
1083
- * (no DB writes, no tmux title writes); surfaces
1084
- * orphans (pure read). Used by `mu undo` (the
1085
- * post-restore pass MUST NOT delete rows the snapshot
1086
- * just restored — see
1087
- * snap_undo_reconcile_destroys_recovered_agents) and
1088
- * `mu doctor` (read-only diagnostic).
1119
+ * without deleting; skips the title refresh (no DB
1120
+ * writes, no tmux title writes); surfaces orphans
1121
+ * (pure read). Used by `mu doctor` (read-only
1122
+ * diagnostic).
1089
1123
  *
1090
1124
  * Mid-spawn placeholders are protected directly in the prune loop, so read
1091
1125
  * paths no longer need a separate mode just to avoid racing spawn's workspace
@@ -1175,6 +1209,14 @@ interface RebaseResult {
1175
1209
  * resolveGitMainRef() symbolic ref; for jj/sl it's the literal
1176
1210
  * `trunk()` revset (or whatever the operator passed via fromRef). */
1177
1211
  fromRef: string;
1212
+ /** Concrete commit id of the workspace's new fork point: fromRef's
1213
+ * commit, or the previous fork point when that already descends from
1214
+ * it (parent_ref never moves backward). The caller writes it to
1215
+ * `vcs_workspaces.parent_ref` so staleness, `mu workspace commits`
1216
+ * and the close-time clean check measure from the new base. Omitted
1217
+ * when the backend cannot resolve a single commit (e.g. a jj revset
1218
+ * naming several); the caller then keeps the old value. */
1219
+ parentRef?: string;
1178
1220
  /** Commit subjects (or descriptions) that got replayed, oldest-first.
1179
1221
  * Empty when the workspace was already at fromRef (no-op). */
1180
1222
  replayed: string[];
@@ -1206,6 +1248,56 @@ interface ShowCommitResult {
1206
1248
  /** Human-readable error message; omitted on success. */
1207
1249
  error?: string;
1208
1250
  }
1251
+ /**
1252
+ * Thrown by `rebaseTo` / `commitsSinceBase` on the `none` backend
1253
+ * (cp -a snapshots have no notion of a rebase target / fork point).
1254
+ * Maps to exit code 4.
1255
+ */
1256
+ declare class WorkspaceVcsRequiredError extends Error implements HasNextSteps {
1257
+ readonly verb: string;
1258
+ readonly workspacePath: string;
1259
+ readonly name = "WorkspaceVcsRequiredError";
1260
+ constructor(verb: string, workspacePath: string);
1261
+ errorNextSteps(): NextStep[];
1262
+ }
1263
+ /**
1264
+ * Thrown by `rebaseTo` when the workspace has uncommitted changes
1265
+ * the rebase would clobber. Carries the dirty file list so the operator
1266
+ * can decide between commit/stash/--force. Maps to exit code 4.
1267
+ */
1268
+ declare class WorkspaceDirtyError extends Error implements HasNextSteps {
1269
+ readonly workspacePath: string;
1270
+ readonly files: readonly string[];
1271
+ readonly name = "WorkspaceDirtyError";
1272
+ /** The verb that refused. Only `rebaseTo` refuses this way today,
1273
+ * but the field keeps the message + nextSteps self-describing. */
1274
+ readonly verb: string;
1275
+ constructor(workspacePath: string, files: readonly string[], verb?: string);
1276
+ errorNextSteps(): NextStep[];
1277
+ }
1278
+ /**
1279
+ * Thrown by `rebaseTo` when the rebase produced conflicts. Carries the
1280
+ * conflicting paths (git/sl) or commits (jj). Maps to exit code 5.
1281
+ *
1282
+ * git and sl abort the rebase before throwing, so the workspace is back
1283
+ * at its pre-rebase state (`aborted` is true). jj cannot abort: the
1284
+ * rebase is done and the conflicts are committed in place (`aborted` is
1285
+ * false). Either way the row's parent_ref is unchanged, so the hint
1286
+ * ends with re-running refresh: once the conflicts are resolved, that
1287
+ * rebase is clean and records the base the disk is on.
1288
+ */
1289
+ declare class WorkspaceConflictError extends Error implements HasNextSteps {
1290
+ readonly workspacePath: string;
1291
+ readonly fromRef: string;
1292
+ readonly conflicts: readonly string[];
1293
+ /** True when the backend aborted the rebase and the workspace is unchanged. */
1294
+ readonly aborted: boolean;
1295
+ readonly name = "WorkspaceConflictError";
1296
+ constructor(workspacePath: string, fromRef: string, conflicts: readonly string[],
1297
+ /** True when the backend aborted the rebase and the workspace is unchanged. */
1298
+ aborted?: boolean);
1299
+ errorNextSteps(): NextStep[];
1300
+ }
1209
1301
  interface CreateWorkspaceOptions$1 {
1210
1302
  /** The repository being branched from. Absolute path. */
1211
1303
  projectRoot: string;
@@ -1237,6 +1329,10 @@ interface FreeWorkspaceResult$1 {
1237
1329
  /** The commit id that captured the pending changes, when `commit` was
1238
1330
  * true and there was something to commit. Otherwise undefined. */
1239
1331
  committedRef?: string;
1332
+ /** Branch created to keep the workspace's commits reachable after the
1333
+ * worktree is removed (git with `commit`, when HEAD was on no branch,
1334
+ * remote or tag). Otherwise undefined. */
1335
+ branch?: string;
1240
1336
  /** True iff the on-disk path was actually removed (vs. already gone). */
1241
1337
  removed: boolean;
1242
1338
  }
@@ -1265,8 +1361,13 @@ interface VcsBackend {
1265
1361
  * missing refs, the `none` backend which has no notion of "main")
1266
1362
  *
1267
1363
  * Callers treat null as "unknown — render — — and don't warn".
1364
+ *
1365
+ * `mainRefCache`, when given, is shared across the rows of one
1366
+ * decorate call so a backend that must resolve "main" per repo (git's
1367
+ * origin/HEAD probe) does it once per repo, not once per workspace.
1368
+ * Backends that resolve main inside the count command ignore it.
1268
1369
  */
1269
- commitsBehind(workspacePath: string, ref: string): Promise<number | null>;
1370
+ commitsBehind(workspacePath: string, ref: string, mainRefCache?: Map<string, Promise<string | undefined>>): Promise<number | null>;
1270
1371
  /**
1271
1372
  * Rebase the workspace onto `fromRef` (or the backend's tracked
1272
1373
  * base when undefined: `origin/HEAD` for git, `trunk()` for jj/sl).
@@ -1275,15 +1376,17 @@ interface VcsBackend {
1275
1376
  * Backend-specific behaviour:
1276
1377
  * - git: refuses on dirty WC (WorkspaceDirtyError); fetches first;
1277
1378
  * `git rebase <ref>`. On conflict, aborts the rebase and throws
1278
- * WorkspaceConflictError so the operator resolves manually.
1379
+ * WorkspaceConflictError (aborted); the workspace is unchanged.
1380
+ * Any other rebase failure is rethrown.
1279
1381
  * - jj: always-snapshotted, so dirty is never an issue. After
1280
1382
  * `jj rebase -d <ref>` the conflict-set is queried via
1281
1383
  * `jj log -r 'conflict()'`. Conflicts surface as
1282
1384
  * WorkspaceConflictError without an abort (jj's conflict markers
1283
1385
  * persist as commits; the operator resolves in-place).
1284
- * - sl: similar to jj. `sl rebase -d <ref>`; conflicts via
1285
- * `sl resolve -l`. On dirty WC sl errors itself; we wrap that
1286
- * into WorkspaceDirtyError.
1386
+ * - sl: like git. Refuses on dirty WC (WorkspaceDirtyError);
1387
+ * `sl rebase -d <ref>`; conflicts via `sl resolve --list`, then
1388
+ * aborts and throws WorkspaceConflictError (aborted). Any other
1389
+ * rebase failure is rethrown.
1287
1390
  * - none: throws WorkspaceVcsRequiredError unconditionally.
1288
1391
  *
1289
1392
  * Surfaced by fb_workspace_recycle_verb: dogfood between waves
@@ -1291,7 +1394,10 @@ interface VcsBackend {
1291
1394
  * main; that killed the worker's LLM context. `refresh` updates
1292
1395
  * the on-disk dir without touching the agent or pane.
1293
1396
  */
1294
- rebaseTo(workspacePath: string, fromRef?: string): Promise<RebaseResult>;
1397
+ rebaseTo(workspacePath: string, fromRef?: string,
1398
+ /** The row's current parent_ref. Kept as the result's parentRef
1399
+ * when it already descends from fromRef and is under the head. */
1400
+ previousParentRef?: string): Promise<RebaseResult>;
1295
1401
  /**
1296
1402
  * Cheap "is the working copy clean?" probe used by close-auto-free
1297
1403
  * (allow_mu_agent_close_without_discard). Definition: ZERO uncommitted
@@ -1434,9 +1540,9 @@ declare function checkCommandResolvable(command: string): Promise<CommandResolut
1434
1540
  interface SpawnAgentOptions {
1435
1541
  name: string;
1436
1542
  workstream: string;
1437
- /** Defaults to "pi". 0.1.0 only really supports "pi" but the column
1438
- * accepts any string for forward-compat with future multi-CLI support
1439
- * (claude/codex). */
1543
+ /** Defaults to "pi". Any CLI is accepted: pi is driven over its control
1544
+ * socket; others run `MU_<CLI>_COMMAND` (or the cli name) and get
1545
+ * text by mux paste. */
1440
1546
  cli?: string;
1441
1547
  /** The actual command to run in the pane. Defaults to the cli value. */
1442
1548
  command?: string;
@@ -1468,6 +1574,9 @@ interface SpawnAgentOptions {
1468
1574
  /** Run the control-socket handshake for pi agents (default true).
1469
1575
  * `false` skips it and reports `ctl: "skipped"`. */
1470
1576
  ctl?: boolean;
1577
+ /** When `name` is taken in the workstream, take the next free one
1578
+ * (see `nextFreeAgentName`) instead of failing with AgentExistsError. */
1579
+ nextFree?: boolean;
1471
1580
  }
1472
1581
  /** Outcome of the spawn-time control-socket handshake. */
1473
1582
  type SpawnCtl = "ok" | "missing" | "refused" | "skipped";
@@ -1504,7 +1613,7 @@ declare function spawnAgent(db: Db, opts: SpawnAgentOptions): Promise<SpawnedAge
1504
1613
  declare function defaultSpawnLivenessMs(): number;
1505
1614
 
1506
1615
  interface AdoptAgentOptions {
1507
- /** tmux pane id (e.g. '%15'). Must already exist on the tmux server. */
1616
+ /** Mux pane id (tmux '%15', herdr 'w1:p2'). Must already exist. */
1508
1617
  paneId: string;
1509
1618
  /** Workstream to adopt the pane into. The pane MUST be in the
1510
1619
  * matching tmux session (`mu-<workstream>`); cross-session adopt is
@@ -1553,18 +1662,18 @@ interface AdoptAgentResult {
1553
1662
  * Validation order (matches the design in note #100):
1554
1663
  * 1. Pane id format -> assertValidPaneId via paneExists / setPaneTitle
1555
1664
  * 2. Pane exists -> PaneNotFoundError
1556
- * 3. Pane is in session -> AgentNotInWorkstreamError (cross-session)
1665
+ * 3. Pane is in session -> PaneNotInSessionError (cross-session)
1557
1666
  * 4. Resolved name OK -> isValidAgentName / Error('agent name invalid')
1558
1667
  * 5. Idempotent check -> if pane already owned by an agent of this
1559
1668
  * name, return alreadyAdopted=true
1560
1669
  * 6. Name not taken -> AgentExistsError (else)
1561
1670
  * 7. Insert + retitle.
1562
1671
  *
1563
- * Status starts at 'free' — reconcile/detect will update it on the next
1564
- * `mu agent list` based on actual pane content (the pi prompt yields
1565
- * 'free'; an agent mid-thought yields 'busy'; etc.). We don't run
1566
- * detection inline here because the caller may not have $TMUX, and
1567
- * adoption shouldn't depend on a captured-pane probe succeeding.
1672
+ * The row's `status` column is the deprecated constant 'spawning' (see
1673
+ * insertAgent); runtime state is read at display time by
1674
+ * src/agent-state.ts (control socket, murmur, or herdr), never from
1675
+ * pane text. Adoption runs no state probe, so it does not depend on
1676
+ * the agent's control socket or a captured pane answering.
1568
1677
  */
1569
1678
  declare function adoptAgent(db: Db, opts: AdoptAgentOptions): Promise<AdoptAgentResult>;
1570
1679
 
@@ -1620,6 +1729,13 @@ declare class AgentExistsError extends Error implements HasNextSteps {
1620
1729
  constructor(agentName: string);
1621
1730
  errorNextSteps(): NextStep[];
1622
1731
  }
1732
+ /** `spawn --next-free` found no free valid name near the asked one. */
1733
+ declare class AgentNameExhaustedError extends Error implements HasNextSteps {
1734
+ readonly agentName: string;
1735
+ readonly name = "AgentNameExhaustedError";
1736
+ constructor(agentName: string, reason: string);
1737
+ errorNextSteps(): NextStep[];
1738
+ }
1623
1739
  declare class AgentNotFoundError extends Error implements HasNextSteps {
1624
1740
  readonly agentName: string;
1625
1741
  /** Optional workstream context. When set, the message is enriched
@@ -1656,6 +1772,20 @@ declare class AgentNotInWorkstreamError extends Error implements HasNextSteps {
1656
1772
  constructor(agentName: string, expectedWorkstream: string, actualWorkstream: string);
1657
1773
  errorNextSteps(): NextStep[];
1658
1774
  }
1775
+ /**
1776
+ * `mu agent adopt` of a pane that exists but is not in the workstream's
1777
+ * mux session. Distinct from AgentNotInWorkstreamError: the subject is a
1778
+ * pane, not an agent, and its owning session is not known without
1779
+ * another mux query. Maps to exit code 4 (conflict).
1780
+ */
1781
+ declare class PaneNotInSessionError extends Error implements HasNextSteps {
1782
+ readonly paneId: string;
1783
+ readonly workstream: string;
1784
+ readonly expectedSession: string;
1785
+ readonly name = "PaneNotInSessionError";
1786
+ constructor(paneId: string, workstream: string, expectedSession: string);
1787
+ errorNextSteps(): NextStep[];
1788
+ }
1659
1789
  /**
1660
1790
  * Thrown when an agent's pane is created and titled successfully but the
1661
1791
  * spawned process exits within the liveness window (default 1500ms;
@@ -1804,6 +1934,27 @@ declare class AgentFreshNeedsCtlError extends Error implements HasNextSteps {
1804
1934
  constructor(agentName: string, workstream: string, cli: string);
1805
1935
  errorNextSteps(): NextStep[];
1806
1936
  }
1937
+ /**
1938
+ * The agent's pi serves the control socket with an extension it loaded
1939
+ * before mu learned `op` (e.g. `fresh`): running pi processes keep the
1940
+ * extension code they started with. pi's `/reload` re-imports the
1941
+ * extension through the `mu link pi` shim and keeps the socket; a
1942
+ * respawn always works. An extension that serves op `command` takes
1943
+ * `/reload` over ctl; an older one needs it typed (`--via mux`).
1944
+ */
1945
+ declare class AgentExtensionOutdatedError extends Error implements HasNextSteps {
1946
+ readonly agentName: string;
1947
+ readonly workstream: string;
1948
+ readonly op: string;
1949
+ readonly extVersion?: string | undefined;
1950
+ /** Ops the extension serves (from its "unknown op" reply); absent from old extensions. */
1951
+ readonly ops?: readonly string[] | undefined;
1952
+ readonly name = "AgentExtensionOutdatedError";
1953
+ constructor(agentName: string, workstream: string, op: string, extVersion?: string | undefined,
1954
+ /** Ops the extension serves (from its "unknown op" reply); absent from old extensions. */
1955
+ ops?: readonly string[] | undefined);
1956
+ errorNextSteps(): NextStep[];
1957
+ }
1807
1958
  /**
1808
1959
  * `send --fresh` or a session command (`/new`, `/reload`, `/compact`)
1809
1960
  * refused: pi is mid-turn, and running it would abandon the turn.
@@ -2047,8 +2198,11 @@ declare function listAgents(db: Db, opts?: {
2047
2198
  declare function composeAgentTitle(db: Db, agent: AgentRow): string;
2048
2199
  /** Push a fresh pane title for `agentName`. Best-effort — a missing
2049
2200
  * agent, a placeholder pane id, or a tmux failure are all swallowed
2050
- * silently (titles are decorative; never block the calling verb). */
2051
- declare function refreshAgentTitle(db: Db, agentName: string, workstream: string): Promise<void>;
2201
+ * silently (titles are decorative; never block the calling verb).
2202
+ * `currentTitle` is the pane's live title when the caller already read
2203
+ * it (reconcile); the write is skipped when it equals the new title,
2204
+ * since each retitle is a mux server write plus a status redraw. */
2205
+ declare function refreshAgentTitle(db: Db, agentName: string, workstream: string, currentTitle?: string): Promise<void>;
2052
2206
  /**
2053
2207
  * Delete an agent row. Returns true if a row was matched. Idempotent;
2054
2208
  * deleting an agent that doesn't exist returns false without throwing.
@@ -2153,10 +2307,7 @@ interface ListLiveAgentsOptions {
2153
2307
  * documented mutating behaviour `mu agent list` has always had,
2154
2308
  * now also used by `mu state`).
2155
2309
  *
2156
- * `mu doctor` and `mu undo` pass `"report-only"`: count drift,
2157
- * mutate nothing. `mu undo` MUST use this so a post-restore
2158
- * reconcile doesn't delete the rows the snapshot just restored
2159
- * (snap_undo_reconcile_destroys_recovered_agents).
2310
+ * `mu doctor` passes `"report-only"`: count drift, mutate nothing.
2160
2311
  *
2161
2312
  * Mid-spawn placeholders (pane id `%pending-<name>`) are protected
2162
2313
  * directly in reconcile's prune loop, independent of mode
@@ -2179,9 +2330,8 @@ interface LiveAgentsView {
2179
2330
  /**
2180
2331
  * Return the live, reality-reconciled view of agents in a workstream.
2181
2332
  * `mu state` and `mu agent list` call this with the default `mode: "full"`
2182
- * (mutating); read-only diagnostic / restore paths
2183
- * (`mu doctor`, `mu undo`) call it with `mode: "report-only"` to mutate
2184
- * nothing at all.
2333
+ * (mutating); the read-only diagnostic (`mu doctor`) calls it with
2334
+ * `mode: "report-only"` to mutate nothing at all.
2185
2335
  */
2186
2336
  declare function listLiveAgents(db: Db, opts: ListLiveAgentsOptions): Promise<LiveAgentsView>;
2187
2337
 
@@ -2463,7 +2613,8 @@ interface CloseTaskOptions extends EvidenceOption {
2463
2613
  * as a bare `addNote` without `--author`). Surfaced in mufeedback
2464
2614
  * task_close_evidence_does_not_append_the. */
2465
2615
  author?: string;
2466
- /** Closing substate; default "done". Any CLOSED/* satisfies edges. */
2616
+ /** Closing substate; default "done" (or, for an already-CLOSED task,
2617
+ * its current substate). Any CLOSED/* satisfies edges. */
2467
2618
  as?: CloseSubstate;
2468
2619
  /** Required (non-empty) unless `as` is "done"; stored as a
2469
2620
  * `<AS>: <why>` note in the same op group. */
@@ -2478,9 +2629,9 @@ interface CloseTaskResult extends SetStatusResult {
2478
2629
  unblocked: string[];
2479
2630
  }
2480
2631
  /** Convenience: setTaskStatus(db, id, "CLOSED"). Accepts evidence.
2481
- * Skipped
2482
- * for the idempotent no-op (already CLOSED) so we don't accumulate
2483
- * empty-delta snapshots on retry loops.
2632
+ * Notes are skipped for the idempotent no-op (already CLOSED with the
2633
+ * same substate; a bare close without `as` keeps the current one) so
2634
+ * retry loops don't accumulate duplicates.
2484
2635
  *
2485
2636
  * With `ifReady: true`, returns a `CloseSkippedResult` (no mutation,
2486
2637
  * no snapshot) when any blocker is still OPEN / IN_PROGRESS. Used by
@@ -2593,7 +2744,7 @@ interface ClaimTaskOptions extends AttributedEvidence {
2593
2744
  */
2594
2745
  agentWorkstream?: string;
2595
2746
  /**
2596
- * Anonymous claim: write `owner = NULL` instead of resolving an agent
2747
+ * Anonymous claim: write `owner_id = NULL` instead of resolving an agent
2597
2748
  * name and checking the FK. Use when the actor is the orchestrator
2598
2749
  * (or a script, or a human) doing direct work in a workstream they
2599
2750
  * aren't a registered worker in.
@@ -2604,9 +2755,10 @@ interface ClaimTaskOptions extends AttributedEvidence {
2604
2755
  *
2605
2756
  * Resolution order for the actor name (used as the log source):
2606
2757
  * 1. `actor` if explicitly passed.
2607
- * 2. Current pane title (when `$TMUX_PANE` is set).
2608
- * 3. `$USER`.
2609
- * 4. The literal string 'unknown'.
2758
+ * 2. `$MU_AGENT_NAME`.
2759
+ * 3. The mux's current agent name (pane title on tmux).
2760
+ * 4. `$USER`.
2761
+ * 5. The literal string 'orchestrator'.
2610
2762
  *
2611
2763
  * Mutually exclusive with `agentName` (the two are alternative
2612
2764
  * answers to "who's the actor for this claim?"). Passing both is a
@@ -2644,14 +2796,15 @@ interface ClaimResult {
2644
2796
  * Claim a task. Two modes:
2645
2797
  *
2646
2798
  * Worker claim (default):
2647
- * Resolve an agent name from `opts.agentName` or from $TMUX_PANE's
2648
- * pane title. The name MUST exist in the agents table (FK on
2649
- * tasks.owner). Sets `owner = <name>`. This is what mu-spawned
2799
+ * Resolve an agent name from `opts.agentName`, else $MU_AGENT_NAME,
2800
+ * else the mux's current agent name (pane title on tmux). The name
2801
+ * MUST exist in the agents table (`tasks.owner_id` is an FK to
2802
+ * `agents.id`). Sets `owner_id` to that agent. This is what mu-spawned
2650
2803
  * workers do, and what `mu task claim --for <worker>` does for
2651
2804
  * orchestrator dispatch.
2652
2805
  *
2653
2806
  * Anonymous claim (--self):
2654
- * Skip the name -> agents FK lookup entirely. Sets `owner = NULL`.
2807
+ * Skip the name -> agents FK lookup entirely. Sets `owner_id = NULL`.
2655
2808
  * Records the actor in `agent_logs.source` instead. This is the
2656
2809
  * orchestrator-doing-direct-work path — the actor is logged but
2657
2810
  * not registered as a worker pane.
@@ -2867,12 +3020,15 @@ interface DeleteTaskResult {
2867
3020
  * found an existing task with zero edges and zero notes
2868
3021
  * (`present: true, deletedEdges: 0, deletedNotes: 0`). */
2869
3022
  present: boolean;
3023
+ /** The op group the delete wrote, for `mu undo <group> --yes`. Null
3024
+ * when nothing was deleted (dry-run or missing row). */
3025
+ group: string | null;
2870
3026
  }
2871
3027
  interface DeleteTaskOptions {
2872
3028
  /** When true, return the cascade preview (would-be edge / note
2873
- * counts) without mutating and without snapshotting. The CLI uses
2874
- * this to power the bare `mu task delete <id>` two-phase pattern
2875
- * (mirrors `mu workstream teardown` / `mu snapshot prune`). Surfaced
3029
+ * counts) without mutating. The CLI uses this to power the bare
3030
+ * `mu task delete <id>` two-phase pattern (mirrors
3031
+ * `mu workstream teardown` / `mu undo`). Surfaced
2876
3032
  * by feedback ws task
2877
3033
  * fb_task_delete_no_yes (impact=30): a dogfood report typed
2878
3034
  * `mu task delete X --yes` (mirroring workstream teardown) and got
@@ -2889,10 +3045,9 @@ interface DeleteTaskOptions {
2889
3045
  * `changes()` only reports rows directly affected by the DELETE.
2890
3046
  *
2891
3047
  * With `opts.dryRun: true`, returns the would-be counts without
2892
- * touching the DB and without taking a snapshot (no mutation = no
2893
- * snapshot — same reasoning that gates the closeTask snap on the
2894
- * idempotent no-op path). The CLI bare `mu task delete <id>` form
2895
- * uses this; `--yes` calls through with `dryRun: false`.
3048
+ * touching the DB (no mutation, so no op is written). The CLI bare
3049
+ * `mu task delete <id>` form uses this; `--yes` calls through with
3050
+ * `dryRun: false`.
2896
3051
  */
2897
3052
  declare function deleteTask(db: Db, localId: string, workstream: string, opts?: DeleteTaskOptions): DeleteTaskResult;
2898
3053
  interface UpdateTaskOptions {
@@ -3042,6 +3197,9 @@ interface FreeWorkspaceResult {
3042
3197
  /** The committed ref, when `commit` was true and there was something
3043
3198
  * to commit. */
3044
3199
  committedRef?: string;
3200
+ /** Branch mu created to keep the workspace's commits reachable after
3201
+ * removal (git `--commit` on a detached HEAD). */
3202
+ branch?: string;
3045
3203
  /** True iff the on-disk path was actually removed. */
3046
3204
  removed: boolean;
3047
3205
  /** True iff the DB row was actually deleted. */
@@ -3121,6 +3279,19 @@ declare class TaskNotFoundError extends Error implements HasNextSteps {
3121
3279
  constructor(taskId: string);
3122
3280
  errorNextSteps(): NextStep[];
3123
3281
  }
3282
+ /**
3283
+ * Thrown by `addTask` when `localId` violates the schema regex
3284
+ * `/^[a-z][a-z0-9_-]{0,63}$/`. Replaces a bare `TypeError` so the
3285
+ * CLI's `handle()` wrapper can map it to exit code 2 (usage, with the
3286
+ * verb's --help, like WorkstreamNameInvalidError) and surface a `--json` `nextSteps` block pointing at
3287
+ * the auto-derived-id workflow and a sanitised candidate.
3288
+ */
3289
+ declare class TaskIdInvalidError extends Error implements HasNextSteps {
3290
+ readonly attempted: string;
3291
+ readonly name = "TaskIdInvalidError";
3292
+ constructor(attempted: string);
3293
+ errorNextSteps(): NextStep[];
3294
+ }
3124
3295
  declare class TaskExistsError extends Error implements HasNextSteps {
3125
3296
  readonly taskId: string;
3126
3297
  readonly name = "TaskExistsError";
@@ -3153,7 +3324,7 @@ declare class TaskAlreadyOwnedError extends Error implements HasNextSteps {
3153
3324
  * Thrown when `mu task claim` resolves a claimer agent name (from the
3154
3325
  * pane title or --for) that has no matching row in the agents table.
3155
3326
  *
3156
- * The FK on `tasks.owner` references `agents.name`; without this guard
3327
+ * The FK on `tasks.owner_id` references `agents.id`; without this guard
3157
3328
  * the claim attempt would fail with the unhelpful 'FOREIGN KEY constraint
3158
3329
  * failed' from SQLite. This typed error gives the user actionable next
3159
3330
  * steps (run `mu agent adopt <pane-id>` to register, or use --for to pick a
@@ -3174,6 +3345,12 @@ declare class ClaimerNotRegisteredError extends Error implements HasNextSteps {
3174
3345
  */
3175
3346
  errorNextSteps(): NextStep[];
3176
3347
  }
3348
+ declare class TaskClaimStaleWorkspaceError extends Error implements HasNextSteps {
3349
+ readonly staleness: WorkspaceStaleness;
3350
+ readonly name = "TaskClaimStaleWorkspaceError";
3351
+ constructor(staleness: WorkspaceStaleness);
3352
+ errorNextSteps(): NextStep[];
3353
+ }
3177
3354
  declare class CycleError extends Error implements HasNextSteps {
3178
3355
  readonly from: string;
3179
3356
  readonly to: string;
@@ -3181,6 +3358,64 @@ declare class CycleError extends Error implements HasNextSteps {
3181
3358
  constructor(from: string, to: string);
3182
3359
  errorNextSteps(): NextStep[];
3183
3360
  }
3361
+ /**
3362
+ * Thrown by the `mu task wait` CLI wrapper when the per-poll
3363
+ * reconciler detects that a watched task transitioned
3364
+ * `IN_PROGRESS → OPEN` between polls (the reaper saw the owner's
3365
+ * pane was gone and flipped the task back). With `--status CLOSED`
3366
+ * (the default) the wait can never satisfy by progress — the worker
3367
+ * is dead — so we abort fast instead of running out the operator's
3368
+ * `--timeout`.
3369
+ *
3370
+ * Maps to exit code 6 (REAPER_DETECTED) via the cli.ts handler. The
3371
+ * suppression rule (only fire when target=CLOSED) lives in the
3372
+ * caller; this error type is a pure data carrier.
3373
+ *
3374
+ * Surfaced live by task_wait_reconcile_dead_panes (twice in one
3375
+ * v0.3 dispatch wave: tmux restart killed worker panes; `mu task
3376
+ * wait --timeout 1800` blocked silently for 25 min instead of
3377
+ * failing in seconds).
3378
+ */
3379
+ declare class ReaperDetectedDuringWaitError extends Error implements HasNextSteps {
3380
+ readonly taskId: string;
3381
+ readonly previousOwner: string | null;
3382
+ readonly workstream: string;
3383
+ readonly name = "ReaperDetectedDuringWaitError";
3384
+ constructor(taskId: string, previousOwner: string | null, workstream: string);
3385
+ errorNextSteps(): NextStep[];
3386
+ }
3387
+ declare class StallDetectedDuringWaitError extends Error implements HasNextSteps {
3388
+ readonly taskName: string;
3389
+ readonly owner: string | null;
3390
+ readonly workstream: string;
3391
+ readonly ageSecs: number;
3392
+ /** The owner runs pi (control socket): `mu agent abort` stops its turn. */
3393
+ readonly ownerExpectsCtl: boolean;
3394
+ /** The owner's ctl run count at the stall, when its socket answered. */
3395
+ readonly ownerRuns?: number | undefined;
3396
+ /** The owner's workstream when it differs from the task's
3397
+ * (`task claim --for <ws>/<agent>`). Agent commands use it. */
3398
+ readonly ownerWorkstream: string;
3399
+ readonly name = "StallDetectedDuringWaitError";
3400
+ constructor(taskName: string, owner: string | null, workstream: string, ageSecs: number,
3401
+ /** The owner runs pi (control socket): `mu agent abort` stops its turn. */
3402
+ ownerExpectsCtl?: boolean,
3403
+ /** The owner's ctl run count at the stall, when its socket answered. */
3404
+ ownerRuns?: number | undefined,
3405
+ /** The owner's workstream when it differs from the task's
3406
+ * (`task claim --for <ws>/<agent>`). Agent commands use it. */
3407
+ ownerWorkstream?: string);
3408
+ /**
3409
+ * Diagnose before acting. `mu agent read` comes FIRST because the
3410
+ * predicate that threw cannot tell the three causes of needs_input
3411
+ * apart, and the evidence is in the pane rather than the task row.
3412
+ * The earlier order led with "poke the worker", which is the right
3413
+ * move for only one of the three and actively wrong for a worker
3414
+ * waiting on an answer — a poke with no answer in it just restates
3415
+ * the question.
3416
+ */
3417
+ errorNextSteps(): NextStep[];
3418
+ }
3184
3419
  declare class CrossWorkstreamEdgeError extends Error implements HasNextSteps {
3185
3420
  readonly blocker: string;
3186
3421
  readonly blockerWorkstream: string;
@@ -3253,6 +3488,16 @@ declare class InvalidSubstateError extends Error implements HasNextSteps {
3253
3488
  errorNextSteps(): NextStep[];
3254
3489
  }
3255
3490
 
3491
+ /** Thrown when a title has no ASCII letter or digit, so no id can be
3492
+ * derived from it (any non-ASCII script, or pure punctuation). Exit 2
3493
+ * (usage) with the verb's --help; the fix is the `<id>` positional.
3494
+ * Lives here, not in errors.ts, because errors.ts imports this file. */
3495
+ declare class TaskTitleSlugEmptyError extends Error implements HasNextSteps {
3496
+ readonly title: string;
3497
+ readonly name = "TaskTitleSlugEmptyError";
3498
+ constructor(title: string);
3499
+ errorNextSteps(): NextStep[];
3500
+ }
3256
3501
  declare function isValidTaskId(id: string): boolean;
3257
3502
  /**
3258
3503
  * Lowercase title; collapse non-alnum runs into single `_`; trim
@@ -3262,7 +3507,7 @@ declare function isValidTaskId(id: string): boolean;
3262
3507
  * when one exists, else hard-truncate). Mirrors `tg`'s `id_from_title`
3263
3508
  * but adds the soft cap.
3264
3509
  *
3265
- * Throws if `title` yields an empty slug after stripping.
3510
+ * Throws `TaskTitleSlugEmptyError` if `title` yields an empty slug.
3266
3511
  */
3267
3512
  declare function slugifyTitle(title: string): string;
3268
3513
  /**
@@ -3379,8 +3624,11 @@ declare function listGoals(db: Db, workstream: string): TaskRow[];
3379
3624
  * named SDK helper so CLI renderers don't re-derive the row-shape
3380
3625
  * conversion (review_code_raw_task_state_duplicate). */
3381
3626
  declare function listInProgress(db: Db, workstream: string): TaskRow[];
3382
- /** Most-recently-closed tasks in a workstream, newest first, capped at
3383
- * `limit` (default 5). Used by `mu state` for its 'recent closed'
3627
+ /** CLOSED tasks in a workstream, most recently updated first, capped at
3628
+ * `limit` (default 5). `updated_at` is a proxy for close time: there is
3629
+ * no `closed_at` column, and a later note or edge change on a CLOSED
3630
+ * task bumps `updated_at` (touchTask), moving it up. Used by `mu state`
3631
+ * for its 'recent closed'
3384
3632
  * slice; exposed as a named SDK helper so the CLI no longer needs the
3385
3633
  * raw-row type that was duplicating RawTaskRow
3386
3634
  * (review_code_raw_task_state_duplicate). */
@@ -3406,8 +3654,9 @@ interface ListNotesOptions {
3406
3654
  * an error here — CLI-side validation rejects `--tail 0`. */
3407
3655
  tail?: number;
3408
3656
  /** ISO-8601 cutoff: only notes with `created_at > since` survive.
3409
- * Comparison is lexicographic on the ISO string (matches the way
3410
- * the rest of the codebase compares ISO timestamps). */
3657
+ * Any `Date.parse`-able value; it is normalised to `toISOString()`
3658
+ * form (the stored shape) before the text comparison, so offsets
3659
+ * and second-precision cutoffs compare as instants. */
3411
3660
  since?: string;
3412
3661
  /** When true and `since` is unset, look up the `created_at` of the
3413
3662
  * most recent `task claim` event for this task and use it as the
@@ -3631,9 +3880,12 @@ declare class LinkConflictError extends Error implements HasNextSteps {
3631
3880
  }
3632
3881
  /** Install the extension shim (or, with `copy`, an inlined copy pinned
3633
3882
  * to this version). `replacedCopy` is true when an existing file without
3634
- * the shim marker was overwritten. */
3883
+ * the shim marker was overwritten. A symlink at the destination (e.g. to
3884
+ * a dev checkout) is never written through: a live one needs `force`,
3885
+ * which replaces the link itself, never its target. */
3635
3886
  declare function linkPi(opts?: LinkOptions & {
3636
3887
  copy?: boolean;
3888
+ force?: boolean;
3637
3889
  }): {
3638
3890
  path: string;
3639
3891
  replacedCopy: boolean;
@@ -3664,7 +3916,7 @@ interface LogRow {
3664
3916
  * render as "system" rather than the string "null". */
3665
3917
  source: string;
3666
3918
  /** Structured intent ('task.close', 'agent.spawn', ...). Null only for
3667
- * operator-authored prose lines (`mu log write` / a `--kind` ledger),
3919
+ * operator-authored prose lines (`mu log "<text>"` / a `--kind` ledger),
3668
3920
  * which name no state change. The formatter in src/log-render.ts
3669
3921
  * renders from this — never from the payload text. */
3670
3922
  intent: string | null;
@@ -3683,6 +3935,15 @@ interface LogRow {
3683
3935
  /** ISO 8601 timestamp set at insert time. */
3684
3936
  createdAt: string;
3685
3937
  }
3938
+ /** Raised when a group-id prefix matches more than one group.
3939
+ * Astronomically unlikely with uuids, but silently picking one of two
3940
+ * candidate groups to UNDO is not an acceptable failure mode. */
3941
+ declare class GroupIdAmbiguousError extends Error implements HasNextSteps {
3942
+ readonly prefix: string;
3943
+ readonly candidates: readonly string[];
3944
+ constructor(prefix: string, candidates: readonly string[]);
3945
+ errorNextSteps(): NextStep[];
3946
+ }
3686
3947
  interface AppendLogOptions {
3687
3948
  /** Workstream this entry belongs to. `null` for machine-wide. */
3688
3949
  workstream: string | null;
@@ -3698,6 +3959,14 @@ interface AppendLogOptions {
3698
3959
  * to name. */
3699
3960
  intent?: string;
3700
3961
  }
3962
+ /** Raised by `appendLog` for a kind in RESERVED_LOG_KINDS. Usage lane
3963
+ * (exit 2): the operator picked a tag that names a projectable entity. */
3964
+ declare class LogKindReservedError extends Error implements HasNextSteps {
3965
+ readonly kind: string;
3966
+ readonly name = "LogKindReservedError";
3967
+ constructor(kind: string);
3968
+ errorNextSteps(): NextStep[];
3969
+ }
3701
3970
  /**
3702
3971
  * Append a log entry. Returns the inserted row (with assigned `seq`).
3703
3972
  * Constant-time. Single INSERT; safe to call from any state-changing
@@ -3865,6 +4134,11 @@ interface WorkstreamSnapshotSlowFields {
3865
4134
  commitsBackend?: VcsBackendName | null;
3866
4135
  doctor: DoctorSummary | null;
3867
4136
  }
4137
+ /**
4138
+ * Fast TUI/state snapshot tier: pure SQLite reads only. Subprocess-backed
4139
+ * fields are intentionally empty placeholders so callers can merge the last
4140
+ * slow-tier values without blocking a 1s render tick on tmux or VCS probes.
4141
+ */
3868
4142
  declare function loadWorkstreamSnapshotFast(db: Db, workstream: string, opts?: LoadWorkstreamSnapshotOptions): Promise<WorkstreamSnapshot>;
3869
4143
  /**
3870
4144
  * Slow snapshot tier: fields backed by tmux / VCS subprocess probes (plus
@@ -3927,6 +4201,10 @@ interface DoctorSummary {
3927
4201
  /** Convenience: how many rows are warn or fail. Card subtitle
3928
4202
  * reads this directly. Pure derivation from `checks`. */
3929
4203
  problemCount: number;
4204
+ /** Where the murmur row read agent state from. The slot-9 popup
4205
+ * re-derives the full check list and passes this back, so its
4206
+ * murmur row matches the card's (herdr on herdr). */
4207
+ agentStateSource?: "murmur" | "herdr";
3930
4208
  }
3931
4209
  /**
3932
4210
  * Compute the doctor summary for a workstream. Pure-ish: runs cheap
@@ -3951,7 +4229,7 @@ declare function countProblems(checks: readonly DoctorCheck[]): number;
3951
4229
  * just `.checks`. Pure-ish (same cheap synchronous DB reads as
3952
4230
  * `loadDoctorSummary`).
3953
4231
  */
3954
- declare function loadDoctorChecks(db: Db, snapshot: WorkstreamSnapshot | null): readonly DoctorCheck[];
4232
+ declare function loadDoctorChecks(db: Db, snapshot: WorkstreamSnapshot | null, agentStateSource?: "murmur" | "herdr"): readonly DoctorCheck[];
3955
4233
  /**
3956
4234
  * Map a check row to the most useful informational command the
3957
4235
  * operator might paste. Read-only by construction: `mu agent list`,
@@ -4023,8 +4301,9 @@ interface CheapDriftReport {
4023
4301
  /**
4024
4302
  * Every live row must have at least one op naming its natural key.
4025
4303
  *
4026
- * ~1ms on a 200-task DB: four indexed NOT EXISTS scans, no rebuild, no
4027
- * temp file. Cheap enough to run on every `mu doctor`.
4304
+ * Four indexed NOT EXISTS scans, no rebuild, no temp file: ~10 ms on a
4305
+ * 2.3k-task, 10.7k-note, 67k-op DB. Cheap enough to run on every `mu
4306
+ * doctor` and the TUI slow tick.
4028
4307
  *
4029
4308
  * WHAT IT CATCHES: a row that exists with no history — an uncaptured
4030
4309
  * INSERT, or a mutation path that bypassed the triggers entirely.
@@ -4082,11 +4361,15 @@ declare class DriftDetectedError extends Error implements HasNextSteps {
4082
4361
 
4083
4362
  /** Sub-directory of the state dir holding lock directories. */
4084
4363
  declare function locksDir(): string;
4085
- interface FileLockOptions {
4364
+ interface FileLockOptions<T = unknown> {
4086
4365
  acquireTimeoutMs?: number;
4087
4366
  staleLockMs?: number;
4088
4367
  /** Env var consulted for the acquire timeout, if any. */
4089
4368
  timeoutEnvVar?: string;
4369
+ /** Run instead of `fn` when the lock cannot be taken (contended past
4370
+ * the deadline, or the lock dir cannot be created). Absent, `fn` runs
4371
+ * unlocked. */
4372
+ onUnavailable?: () => T;
4090
4373
  }
4091
4374
  /**
4092
4375
  * Run `fn` while holding the lock directory at `lockPath`.
@@ -4095,7 +4378,7 @@ interface FileLockOptions {
4095
4378
  * Records pid + acquisition time in `meta.json` for stale-lock
4096
4379
  * diagnostics.
4097
4380
  */
4098
- declare function withFileLock<T>(lockPath: string, label: string, fn: () => Promise<T>, opts?: FileLockOptions): Promise<T>;
4381
+ declare function withFileLock<T>(lockPath: string, label: string, fn: () => Promise<T>, opts?: FileLockOptions<T>): Promise<T>;
4099
4382
  /** Read a held lock's metadata, or null. Exposed for tests. */
4100
4383
  declare function readFileLockMeta(lockPath: string): Promise<{
4101
4384
  pid: number;
@@ -4113,6 +4396,17 @@ interface FleetHazard {
4113
4396
  /** Multi-line explanation + remediation, shown when non-ok. */
4114
4397
  remediation?: readonly string[];
4115
4398
  }
4399
+ /**
4400
+ * `mu doctor` found at least one `fail`-severity row. Thrown after the
4401
+ * whole report is printed (or the --json payload emitted) so scripts and
4402
+ * CI see a non-zero exit; maps to exit 5 like drift, because a `fail`
4403
+ * row means the substrate is unsafe, not that a hint is pending.
4404
+ */
4405
+ declare class HazardFailError extends Error {
4406
+ readonly failedRows: readonly string[];
4407
+ readonly name = "HazardFailError";
4408
+ constructor(failedRows: readonly string[]);
4409
+ }
4116
4410
  /**
4117
4411
  * True iff `child` is inside `parent` (or is `parent`).
4118
4412
  *
@@ -4330,7 +4624,7 @@ declare function nextHlc(db: Db, now?: number): string;
4330
4624
  *
4331
4625
  * wall = max(local_wall, remote_wall, now)
4332
4626
  *
4333
- * The counter has three explicit cases, by which of the three won:
4627
+ * The counter has four explicit cases, by which of the three won:
4334
4628
  * - `now` strictly won -> counter = 0 (fresh millisecond)
4335
4629
  * - local and remote tie at max -> counter = max(local_c, remote_c) + 1
4336
4630
  * - only local is at max -> counter = local_c + 1
@@ -4350,7 +4644,7 @@ declare function receiveHlc(db: Db, remoteHlc: string, now?: number): string;
4350
4644
  /** Every intent mu writes: the local ones (`emitEvent`, for changes no
4351
4645
  * trigger can see) plus the capture-trigger ones (set via
4352
4646
  * `withOpContext` around a portable-table mutation). */
4353
- type CaptureIntent = "task.add" | "task.update" | "task.note" | "task.delete" | "task.close" | "task.open" | "task.park" | "task.unpark" | "task.reject" | "task.defer" | "task.claim" | "task.release" | "task.reap" | "task.block" | "task.unblock" | "task.reparent" | "workstream.init" | "workstream.teardown" | "workstream.destroy";
4647
+ type CaptureIntent = "task.add" | "task.update" | "task.note" | "task.delete" | "task.close" | "task.open" | "task.park" | "task.unpark" | "task.accept" | "task.reject" | "task.defer" | "task.claim" | "task.release" | "task.reap" | "task.block" | "task.unblock" | "task.reparent" | "workstream.init" | "workstream.teardown" | "sql.write" | "workstream.destroy";
4354
4648
  type KnownIntent = CaptureIntent | LocalIntent;
4355
4649
  /** A log row, reduced to what rendering needs. Structural so both
4356
4650
  * `LogRow` (the SDK shape) and raw op rows satisfy it. */
@@ -4402,7 +4696,7 @@ declare const KNOWN_VERBS: readonly string[];
4402
4696
  * Render one op as prose.
4403
4697
  *
4404
4698
  * Returns null when the row is not a rendered op at all — operator prose
4405
- * from `mu log write` / a `--kind` ledger, which has no intent and
4699
+ * from `mu log "<text>"` / a `--kind` ledger, which has no intent and
4406
4700
  * should be shown verbatim. Callers print `payload` in that case.
4407
4701
  */
4408
4702
  declare function renderOp(row: RenderableOp): RenderedOp | null;
@@ -4521,8 +4815,9 @@ interface RebuildReport {
4521
4815
  /** Ops copied into the target's log. Every op, not just synced ones. */
4522
4816
  opsCopied: number;
4523
4817
  /** Ops that projected into a portable table. Always <= opsCopied:
4524
- * log-only entities (message / event / broadcast / marker) are
4525
- * copied but have no table to land in. */
4818
+ * log-only entities (message / event / broadcast), machine-local
4819
+ * agent / workspace ops and legacy log-only intents are copied but
4820
+ * have no table to land in. */
4526
4821
  opsProjected: number;
4527
4822
  /** Ops that changed a row when applied. Lower than opsProjected
4528
4823
  * whenever later ops superseded earlier ones — which is normal and
@@ -4559,6 +4854,39 @@ interface RebuildOptions {
4559
4854
  */
4560
4855
  declare function rebuildInto(source: Db, opts: RebuildOptions): RebuildReport;
4561
4856
 
4857
+ /** Serialisable running state: eight hash words (hex), bytes hashed so
4858
+ * far, and the partial trailing block not yet compressed (base64). */
4859
+ interface Sha256State {
4860
+ h: string;
4861
+ n: number;
4862
+ tail: string;
4863
+ }
4864
+
4865
+ /** Whole-file verification sidecar. */
4866
+ interface SegmentManifest {
4867
+ v: number;
4868
+ machine: string;
4869
+ count: number;
4870
+ lastHlc: string | null;
4871
+ sha256: string;
4872
+ updatedAt: string;
4873
+ /** Bytes of the segment `sha256` covers. Written by the owner so its
4874
+ * next flush can trust this manifest instead of rescanning; absent in
4875
+ * manifests from older builds. */
4876
+ size?: number;
4877
+ /** The segment's mtime when this manifest was written (owner-local). */
4878
+ mtimeMs?: number;
4879
+ /** Running hash state after `size` bytes, so an append rehashes only
4880
+ * what it added. */
4881
+ shaState?: Sha256State;
4882
+ /** sha256 over the fields above (`manifestSeal`). The owner trusts
4883
+ * count/size/shaState only when it matches, so a torn or hand-edited
4884
+ * manifest falls back to the full scan instead of being persisted. */
4885
+ seal?: string;
4886
+ }
4887
+ /** Read a segment's manifest, or null when absent/unparsable. */
4888
+ declare function readManifest(segment: string): SegmentManifest | null;
4889
+
4562
4890
  /** Current segment line format. Bumped only on a breaking shape change;
4563
4891
  * a reader that sees a version it does not know REFUSES the line rather
4564
4892
  * than guessing at its meaning. */
@@ -4577,15 +4905,6 @@ interface SegmentLine {
4577
4905
  payload: unknown;
4578
4906
  crc: string;
4579
4907
  }
4580
- /** Whole-file verification sidecar. */
4581
- interface SegmentManifest {
4582
- v: number;
4583
- machine: string;
4584
- count: number;
4585
- lastHlc: string | null;
4586
- sha256: string;
4587
- updatedAt: string;
4588
- }
4589
4908
  /** Why a segment line was rejected. Reported, never silently swallowed. */
4590
4909
  type SegmentDefectKind = "torn-write" | "manifest-mismatch" | "crc-mismatch" | "non-monotonic-hlc" | "unknown-version" | "malformed-shape" | "entity-not-synced" | "duplicate-op";
4591
4910
  interface SegmentDefect {
@@ -4614,7 +4933,10 @@ interface FlushResult {
4614
4933
  appended: number;
4615
4934
  /** Total lines in the segment afterwards. */
4616
4935
  total: number;
4617
- /** Ops skipped because their entity is machine-local. */
4936
+ /** Ops skipped because their entity is machine-local. A full scan
4937
+ * (`verify`, or an untrusted manifest) counts every unflushed one; the
4938
+ * fast path counts only those up to the last op it appended, so a
4939
+ * no-op ambient flush reports 0 (see `pendingLines`). */
4618
4940
  skippedLocal: number;
4619
4941
  /**
4620
4942
  * Non-null when THIS MACHINE'S OWN segment was found defective past
@@ -4630,34 +4952,22 @@ interface FlushResult {
4630
4952
  * to be (`mu sync`, `ambientFlush`).
4631
4953
  */
4632
4954
  selfRepaired: SegmentDefect | null;
4955
+ /**
4956
+ * True when ops were pending but another process held the segment
4957
+ * lock past the wait, so this call wrote nothing. The ops stay
4958
+ * unflushed in `ops` (the segment's last hlc is the watermark), and
4959
+ * the next flush appends them. Never set on a successful flush.
4960
+ */
4961
+ lockBusy?: boolean;
4633
4962
  }
4634
- /**
4635
- * Append this machine's not-yet-flushed ops to its own segment.
4636
- *
4637
- * FILTERING IS LOAD-BEARING. Only ops whose entity is in
4638
- * `SYNCED_ENTITIES` are written. Machine-local ops (agent.*, workspace.*)
4639
- * are captured and DO appear in `mu log`, but they must never reach a
4640
- * segment: they carry pane ids and absolute paths that are meaningless,
4641
- * and frequently wrong, on another machine. "Not synced" is not "not
4642
- * logged".
4643
- *
4644
- * Also filters `machine_id = <me>`: a segment holds ONE machine's ops.
4645
- * Ops ingested from a peer live in our `ops` table too, and re-flushing
4646
- * them into our own segment would duplicate a peer's history under our
4647
- * name — and would grow without bound as two machines echoed each other.
4648
- *
4649
- * The high-water mark is the last hlc already in the file (read from the
4650
- * manifest when present, else derived by scanning), so flush is
4651
- * incremental and idempotent: calling it twice appends nothing the second
4652
- * time.
4653
- */
4654
- declare function flushSegment(db: Db, dir?: string | null): Promise<FlushResult>;
4963
+ declare function flushSegment(db: Db, dir?: string | null, opts?: {
4964
+ verify?: boolean;
4965
+ lockWaitMs?: number;
4966
+ }): Promise<FlushResult>;
4655
4967
  /** Number of GOOD lines in a segment (stopping at the first defect, as
4656
4968
  * ingest does). The denominator of "how far behind am I" — exported for
4657
4969
  * `mu sync`'s peer table. */
4658
4970
  declare function segmentLineCount(path: string): number;
4659
- /** Read a segment's manifest, or null when absent/unparsable. */
4660
- declare function readManifest(segment: string): SegmentManifest | null;
4661
4971
  /**
4662
4972
  * Verify a segment against its manifest (layer 4).
4663
4973
  *
@@ -4674,6 +4984,13 @@ declare function verifyAgainstManifest(segment: string): {
4674
4984
  interface PeerSegment {
4675
4985
  /** Machine id the segment belongs to. */
4676
4986
  machineId: string;
4987
+ /** Key of this FILE's watermark in `sync_peers`: the machine id for
4988
+ * the peer's own segment, the full file stem for a conflict copy.
4989
+ * A copy diverges from its original, so line N of one says nothing
4990
+ * about line N of the other; sharing one watermark skipped every op
4991
+ * that existed only in the copy. Optional for SDK callers written
4992
+ * before it existed: absent means the machine id (`peerWatermarkKey`). */
4993
+ watermarkKey?: string;
4677
4994
  /** Path on disk. */
4678
4995
  path: string;
4679
4996
  /** True for a Syncthing-style conflict copy. */
@@ -4695,7 +5012,9 @@ interface PeerSegment {
4695
5012
  */
4696
5013
  declare function discoverPeers(dir: string, selfMachineId: string): PeerSegment[];
4697
5014
  /**
4698
- * How far into a peer's segment we have applied.
5015
+ * How far into one segment file we have applied, keyed by
5016
+ * `PeerSegment.watermarkKey` (the machine id, or a conflict copy's own
5017
+ * stem).
4699
5018
  *
4700
5019
  * ONE INTEGER SUFFICES because segments are append-only and ordered — a
4701
5020
  * set or a vector clock would be strictly more state for no more
@@ -4706,15 +5025,24 @@ declare function discoverPeers(dir: string, selfMachineId: string): PeerSegment[
4706
5025
  * `ops.seq` (which is a local-only cursor on their machine and means
4707
5026
  * nothing here).
4708
5027
  */
4709
- declare function getWatermark(db: Db, machineId: string): number;
4710
- declare function setWatermark(db: Db, machineId: string, value: number): void;
5028
+ declare function getWatermark(db: Db, watermarkKey: string): number;
5029
+ /**
5030
+ * Store a watermark and stamp `last_seen_at` with the ISO time. The
5031
+ * segment fingerprint goes to `sync_fingerprints` when the caller just
5032
+ * read the file cleanly to its end (`segmentCaughtUp`). Every other
5033
+ * write deletes it, so a reset or partial ingest never vouches for the
5034
+ * file.
5035
+ */
5036
+ declare function setWatermark(db: Db, watermarkKey: string, value: number, fingerprint?: string | null): void;
4711
5037
  /** Reset a peer's watermark so the next ingest re-reads from zero. The
4712
5038
  * universal repair, safe because ingest is idempotent. */
4713
- declare function resetWatermark(db: Db, machineId: string): void;
5039
+ declare function resetWatermark(db: Db, watermarkKey: string): void;
4714
5040
  interface IngestResult {
4715
5041
  machineId: string;
4716
5042
  path: string;
4717
- /** Lines read past the watermark. */
5043
+ /** Lines consumed past the starting watermark: applied plus skipped
5044
+ * (refused, re-delivered, historical log-only) lines. A line that
5045
+ * halted ingest is not counted. */
4718
5046
  read: number;
4719
5047
  /** Ops applied (some are no-ops: already present, or lost an LWW). */
4720
5048
  applied: number;
@@ -4750,11 +5078,40 @@ interface IngestResult {
4750
5078
  * lines, forever — so it is reported and SKIPPED. A non-monotonic line
4751
5079
  * we have NOT seen before is still real damage and still halts.
4752
5080
  *
5081
+ * A HISTORICAL log-only line (`isLegacyLogOnlyIntent`: a prose payload
5082
+ * under an otherwise projectable entity, written by a mu older than the
5083
+ * flush filter) is skipped silently, exactly as flush and `--from` skip
5084
+ * it. Applying it threw a JSON SyntaxError that rolled back the whole
5085
+ * segment on every invocation.
5086
+ *
5087
+ * A manifest mismatch (layer 4) is reported but does not halt: every
5088
+ * line still present is individually valid, so applying them is safe.
5089
+ *
4753
5090
  * Calls `receiveHlc` per op so the local clock advances past the peer's,
4754
5091
  * which is what makes "laptop edits after seeing the devserver's op" order
4755
5092
  * correctly rather than losing to it.
4756
5093
  */
4757
- declare function ingestSegment(db: Db, peer: PeerSegment): IngestResult;
5094
+ declare function ingestSegment(db: Db, peer: PeerSegment, opts?: {
5095
+ verify?: boolean;
5096
+ }): IngestResult;
5097
+ /**
5098
+ * Apply ONE incoming op and record it in the local `ops` table.
5099
+ *
5100
+ * The shared tail of every ingest path — segment ingest above, and the
5101
+ * `mu sync --from <peer.db>` reader in `src/sync.ts`, which is a
5102
+ * different READER over the same apply semantics. Extracted so the two
5103
+ * cannot drift: a second copy of "advance the clock, apply, record" is
5104
+ * how one of them silently stops advancing the clock.
5105
+ *
5106
+ * Three steps, in this order:
5107
+ * 1. `receiveHlc` BEFORE applying, so anything we mint afterwards
5108
+ * sorts above the peer's op.
5109
+ * 2. `applyOp`, which is capture-suppressed (no echo op is minted).
5110
+ * 3. Record the op locally so it survives, participates in
5111
+ * provenance, and can be re-flushed by rebuild. INSERT OR IGNORE
5112
+ * makes this idempotent via UNIQUE (machine_id, hlc) — the
5113
+ * property that lets "re-read from zero" be the universal repair.
5114
+ */
4758
5115
  declare function applyIncomingOp(db: Db, op: Op): {
4759
5116
  changed: boolean;
4760
5117
  };
@@ -4767,7 +5124,7 @@ interface SyncPassResult {
4767
5124
  /**
4768
5125
  * One flush + one ingest of every discovered peer.
4769
5126
  *
4770
- * This is the SDK seam `mu sync` (v2-sync) will call; it deliberately
5127
+ * The SDK seam `mu sync` calls (src/cli/sync.ts); it deliberately
4771
5128
  * prints nothing and starts nothing. No daemon, no watcher, no polling
4772
5129
  * loop that outlives the command — the anti-feature pledges are firm, and
4773
5130
  * mu never moves files itself: the operator owns transport.
@@ -4818,7 +5175,12 @@ interface PeerStatus {
4818
5175
  path: string;
4819
5176
  /** True for a Syncthing-style `*.sync-conflict-*.jsonl` copy. */
4820
5177
  conflictCopy: boolean;
4821
- /** Lines of this peer's segment already applied. */
5178
+ /** `sync_peers` key of this file's watermark (see `PeerSegment`).
5179
+ * `peerStatuses` always sets it. Optional so SDK callers that build
5180
+ * the pre-watermarkKey shape still compile; absent means the machine
5181
+ * id (`peerWatermarkKey`). */
5182
+ watermarkKey?: string;
5183
+ /** Lines of this segment file already applied. */
4822
5184
  watermark: number;
4823
5185
  /** GOOD lines currently in the segment (a defect stops the count). */
4824
5186
  total: number;
@@ -4838,10 +5200,16 @@ declare function peerStatuses(db: Db, dir: string): PeerStatus[];
4838
5200
  /** Resolve an operator-typed peer reference (full machine id, or any
4839
5201
  * unique prefix) against the discovered peers. Ambiguity is a
4840
5202
  * UsageError, never a guess — repairing the wrong peer would re-read a
4841
- * whole segment for nothing and confuse the report. */
5203
+ * whole segment for nothing and confuse the report.
5204
+ *
5205
+ * A ref names a MACHINE, so a segment and its conflict copies (same
5206
+ * machine id) count as one match; the peer's own segment is returned
5207
+ * when present. Counting them separately made the short id `mu sync`
5208
+ * prints unresolvable. */
4842
5209
  declare function resolvePeerRef(peers: readonly PeerStatus[], ref: string): PeerStatus;
4843
- /** Reset a peer's watermark so the next ingest re-reads its segment from
4844
- * zero. Safe by construction: apply is idempotent and `ops` dedupes on
5210
+ /** Reset the watermark of every segment file a peer has (its own and
5211
+ * any conflict copies) so the next ingest re-reads them from zero. Safe
5212
+ * by construction: apply is idempotent and `ops` dedupes on
4845
5213
  * `UNIQUE (machine_id, hlc)`. */
4846
5214
  declare function repairPeer(db: Db, ref: string, dir: string): PeerStatus;
4847
5215
  /** Is sync configured at all? THE single `if` the no-sync case pays.
@@ -4880,8 +5248,12 @@ declare function ambientIngest(db: Db, opts?: AmbientOptions): Promise<AmbientRe
4880
5248
  * laptop followed by `mu sync` on the devserver would show nothing —
4881
5249
  * exactly the no-hands claim, broken.
4882
5250
  *
4883
- * Runs under the cross-process file lock inside `flushSegment`, so two
4884
- * concurrent mu processes cannot interleave partial lines.
5251
+ * Appends under the cross-process file lock inside `flushSegment`, so
5252
+ * two concurrent mu processes cannot interleave partial lines. A flush
5253
+ * with nothing to append skips the lock, so read-only verbs never queue.
5254
+ * A flush that cannot take the lock within AMBIENT_FLUSH_LOCK_WAIT_MS
5255
+ * writes nothing (`lockBusy`) and the verb carries on silently: its ops
5256
+ * stay pending in `ops` and the next invocation's flush appends them.
4885
5257
  */
4886
5258
  declare function ambientFlush(db: Db, opts?: AmbientOptions): Promise<FlushResult | null>;
4887
5259
  /**
@@ -4960,6 +5332,7 @@ declare const tmux_PaneNotFoundError: typeof PaneNotFoundError;
4960
5332
  type tmux_SendOptions = SendOptions;
4961
5333
  type tmux_SendWarning = SendWarning;
4962
5334
  type tmux_SplitWindowOptions = SplitWindowOptions;
5335
+ type tmux_TmuxBackend = TmuxBackend;
4963
5336
  type tmux_TmuxError = TmuxError;
4964
5337
  declare const tmux_TmuxError: typeof TmuxError;
4965
5338
  type tmux_TmuxExecResult = TmuxExecResult;
@@ -5010,7 +5383,7 @@ declare const tmux_sleep: typeof sleep;
5010
5383
  declare const tmux_splitWindow: typeof splitWindow;
5011
5384
  declare const tmux_tmuxBackend: typeof tmuxBackend;
5012
5385
  declare namespace tmux {
5013
- export { type tmux_AttachTarget as AttachTarget, type tmux_CaptureOptions as CaptureOptions, type tmux_MuxCommand as MuxCommand, tmux_MuxError as MuxError, type tmux_MuxHealth as MuxHealth, type tmux_NewSessionOptions as NewSessionOptions, type tmux_NewSessionWithPaneOptions as NewSessionWithPaneOptions, type tmux_NewWindowOptions as NewWindowOptions, tmux_PANE_ID_RE as PANE_ID_RE, tmux_PaneNotFoundError as PaneNotFoundError, type tmux_SendOptions as SendOptions, type tmux_SendWarning as SendWarning, type tmux_SplitWindowOptions as SplitWindowOptions, tmux_TmuxError as TmuxError, type tmux_TmuxExecResult as TmuxExecResult, type tmux_TmuxExecutor as TmuxExecutor, type tmux_TmuxPane as TmuxPane, type tmux_TmuxSession as TmuxSession, type tmux_TmuxWindow as TmuxWindow, tmux_assertValidPaneId as assertValidPaneId, tmux_attachCommands as attachCommands, tmux_attachHint as attachHint, tmux_awaitPaneQuiescence as awaitPaneQuiescence, tmux_capturePane as capturePane, tmux_currentAgentName as currentAgentName, tmux_currentPaneTitle as currentPaneTitle, tmux_currentSessionName as currentSessionName, tmux_defaultSendDelayMs as defaultSendDelayMs, tmux_defaultSendReadinessMs as defaultSendReadinessMs, tmux_enableMuPaneBorders as enableMuPaneBorders, tmux_enableMuPaneBordersForPane as enableMuPaneBordersForPane, tmux_enableMuPaneBordersForSession as enableMuPaneBordersForSession, tmux_getPaneTitle as getPaneTitle, tmux_getWindowIdForPane as getWindowIdForPane, tmux_hasWorkMarker as hasWorkMarker, tmux_healthCheck as healthCheck, tmux_isValidPaneId as isValidPaneId, tmux_killPane as killPane, tmux_killSession as killSession, tmux_listPanes as listPanes, tmux_listPanesInSession as listPanesInSession, tmux_listSessions as listSessions, tmux_listWindows as listWindows, tmux_newSession as newSession, tmux_newSessionWithPane as newSessionWithPane, tmux_newWindow as newWindow, tmux_paneExists as paneExists, tmux_paneNotFoundNextSteps as paneNotFoundNextSteps, tmux_paneTTY as paneTTY, tmux_parseAgentNameFromTitle as parseAgentNameFromTitle, tmux_resetSleep as resetSleep, tmux_resetTmuxExecutor as resetTmuxExecutor, tmux_selectLayout as selectLayout, tmux_sendToPane as sendToPane, tmux_sessionExists as sessionExists, tmux_setPaneTitle as setPaneTitle, tmux_setSleepForTests as setSleepForTests, tmux_setTmuxExecutor as setTmuxExecutor, tmux_sleep as sleep, tmux_splitWindow as splitWindow, tmux$1 as tmux, tmux_tmuxBackend as tmuxBackend };
5386
+ export { type tmux_AttachTarget as AttachTarget, type tmux_CaptureOptions as CaptureOptions, type tmux_MuxCommand as MuxCommand, tmux_MuxError as MuxError, type tmux_MuxHealth as MuxHealth, type tmux_NewSessionOptions as NewSessionOptions, type tmux_NewSessionWithPaneOptions as NewSessionWithPaneOptions, type tmux_NewWindowOptions as NewWindowOptions, tmux_PANE_ID_RE as PANE_ID_RE, tmux_PaneNotFoundError as PaneNotFoundError, type tmux_SendOptions as SendOptions, type tmux_SendWarning as SendWarning, type tmux_SplitWindowOptions as SplitWindowOptions, type tmux_TmuxBackend as TmuxBackend, tmux_TmuxError as TmuxError, type tmux_TmuxExecResult as TmuxExecResult, type tmux_TmuxExecutor as TmuxExecutor, type tmux_TmuxPane as TmuxPane, type tmux_TmuxSession as TmuxSession, type tmux_TmuxWindow as TmuxWindow, tmux_assertValidPaneId as assertValidPaneId, tmux_attachCommands as attachCommands, tmux_attachHint as attachHint, tmux_awaitPaneQuiescence as awaitPaneQuiescence, tmux_capturePane as capturePane, tmux_currentAgentName as currentAgentName, tmux_currentPaneTitle as currentPaneTitle, tmux_currentSessionName as currentSessionName, tmux_defaultSendDelayMs as defaultSendDelayMs, tmux_defaultSendReadinessMs as defaultSendReadinessMs, tmux_enableMuPaneBorders as enableMuPaneBorders, tmux_enableMuPaneBordersForPane as enableMuPaneBordersForPane, tmux_enableMuPaneBordersForSession as enableMuPaneBordersForSession, tmux_getPaneTitle as getPaneTitle, tmux_getWindowIdForPane as getWindowIdForPane, tmux_hasWorkMarker as hasWorkMarker, tmux_healthCheck as healthCheck, tmux_isValidPaneId as isValidPaneId, tmux_killPane as killPane, tmux_killSession as killSession, tmux_listPanes as listPanes, tmux_listPanesInSession as listPanesInSession, tmux_listSessions as listSessions, tmux_listWindows as listWindows, tmux_newSession as newSession, tmux_newSessionWithPane as newSessionWithPane, tmux_newWindow as newWindow, tmux_paneExists as paneExists, tmux_paneNotFoundNextSteps as paneNotFoundNextSteps, tmux_paneTTY as paneTTY, tmux_parseAgentNameFromTitle as parseAgentNameFromTitle, tmux_resetSleep as resetSleep, tmux_resetTmuxExecutor as resetTmuxExecutor, tmux_selectLayout as selectLayout, tmux_sendToPane as sendToPane, tmux_sessionExists as sessionExists, tmux_setPaneTitle as setPaneTitle, tmux_setSleepForTests as setSleepForTests, tmux_setTmuxExecutor as setTmuxExecutor, tmux_sleep as sleep, tmux_splitWindow as splitWindow, tmux$1 as tmux, tmux_tmuxBackend as tmuxBackend };
5014
5387
  }
5015
5388
 
5016
5389
  /** What undoing one op will do. */
@@ -5052,8 +5425,9 @@ interface UndoPlan {
5052
5425
  interface UndoResult {
5053
5426
  plan: UndoPlan;
5054
5427
  /** Group id of the ops the UNDO itself wrote — pass this to
5055
- * `mu undo` to redo. */
5056
- undoGroupId: string;
5428
+ * `mu undo` to redo. Null when no inverse changed a row: the undo
5429
+ * wrote no ops, so there is no group to redo. */
5430
+ undoGroupId: string | null;
5057
5431
  /** Inverse ops that actually changed a row. */
5058
5432
  applied: number;
5059
5433
  }
@@ -5098,8 +5472,12 @@ declare class UndoSupersededError extends Error implements HasNextSteps {
5098
5472
  * Returns `{ found: false }` when no earlier op named the field, which
5099
5473
  * means the field had no value before this op — so there is nothing to
5100
5474
  * restore and the op must have been part of the row's creation.
5475
+ *
5476
+ * `machineId` limits the lookup to one machine's ops. Undo passes it for
5477
+ * `owner_id`, whose live value only this machine's ops ever wrote (apply
5478
+ * strips it from peers' ops).
5101
5479
  */
5102
- declare function priorFieldValue(db: Db, entity: string, key: string, hlc: string, field: string): {
5480
+ declare function priorFieldValue(db: Db, entity: string, key: string, hlc: string, field: string, machineId?: string): {
5103
5481
  found: true;
5104
5482
  value: string | number | null;
5105
5483
  } | {
@@ -5172,8 +5550,10 @@ interface UndoOptions {
5172
5550
  declare function undoGroup(db: Db, groupId: string, opts?: UndoOptions): UndoResult;
5173
5551
 
5174
5552
  declare function isValidWorkstreamName(name: string): boolean;
5175
- /** Thrown by `ensureWorkstream` and `mu workstream init` when the name
5176
- * doesn't match the rules. */
5553
+ /** A workstream name collision. Exported SDK error class mapped to exit
5554
+ * 4 (conflict) by `classifyError`; no mu verb throws it today
5555
+ * (`ensureWorkstream` is idempotent). Invalid names throw
5556
+ * `WorkstreamNameInvalidError`. */
5177
5557
  declare class WorkstreamExistsError extends Error implements HasNextSteps {
5178
5558
  readonly workstream: string;
5179
5559
  readonly name: string;
@@ -5186,6 +5566,15 @@ declare class WorkstreamNameInvalidError extends Error implements HasNextSteps {
5186
5566
  constructor(attempted: string);
5187
5567
  errorNextSteps(): NextStep[];
5188
5568
  }
5569
+ /** Thrown by `mu workstream init` when the operator tries to create a
5570
+ * reserved workstream (e.g. `scratch`) explicitly. The name is not
5571
+ * invalid — it auto-creates on spawn — it just can't be `init`ed. */
5572
+ declare class WorkstreamNameReservedError extends Error implements HasNextSteps {
5573
+ readonly attempted: string;
5574
+ readonly name = "WorkstreamNameReservedError";
5575
+ constructor(attempted: string);
5576
+ errorNextSteps(): NextStep[];
5577
+ }
5189
5578
  /**
5190
5579
  * Ensure a row exists in the `workstreams` table for `name`. Idempotent;
5191
5580
  * INSERT OR IGNORE so concurrent callers race safely. Called by
@@ -5276,6 +5665,18 @@ interface WorkstreamOptions {
5276
5665
  }
5277
5666
  interface TeardownWorkstreamOptions extends WorkstreamOptions {
5278
5667
  }
5668
+ /**
5669
+ * Discover every workstream visible on this machine. The union of:
5670
+ * - rows in the `workstreams` table (canonical DB source; populated by
5671
+ * `mu init` and auto-created by insertAgent / addTask)
5672
+ * - mux sessions named `mu-*` (with the prefix stripped) — catches
5673
+ * externally-created sessions (e.g. `tmux new-session -s mu-foo`)
5674
+ * that mu hasn't observed yet
5675
+ *
5676
+ * Returns one `WorkstreamSummary` per workstream, sorted by name.
5677
+ * Useful as a pre-flight before `mu init` ("is this name taken?") and
5678
+ * for `mu doctor`-style diagnostics.
5679
+ */
5279
5680
  declare function listWorkstreams(db: Db): Promise<WorkstreamSummary[]>;
5280
5681
  declare function summarizeWorkstream(db: Db, opts: WorkstreamOptions): Promise<WorkstreamSummary>;
5281
5682
  /**
@@ -5289,4 +5690,4 @@ declare function summarizeWorkstream(db: Db, opts: WorkstreamOptions): Promise<W
5289
5690
  */
5290
5691
  declare function teardownWorkstream(db: Db, opts: TeardownWorkstreamOptions): Promise<TeardownResult>;
5291
5692
 
5292
- export { AGENT_STATE_GLYPH, type AbortAgentOptions, type AbortResult, type AddNoteOptions, type AddTaskOptions, type AdoptAgentOptions, type AdoptAgentResult, AgentAbortNeedsCtlError, AgentAbortTimeoutError, AgentBusyError, AgentCtlUnreachableError, AgentDiedOnSpawnError, AgentExistsError, AgentFreshNeedsCtlError, AgentNotFoundError, AgentNotInWorkstreamError, type AgentRow, AgentSlashCommandUnsupportedError, AgentSpawnCliNotFoundError, AgentSpawnStartupError, type AmbientOptions, type AmbientResult, type AppendLogOptions, type ApplyResult, type BlockEdgeResult, CAPTURE_TRIGGER_DDL, CURRENT_SCHEMA_VERSION, type CaptureDb, type CaptureIntent, type CaptureOptions, type CaseCollision, type CheapDriftReport, type ClaimResult, type ClaimTaskOptions, ClaimerNotRegisteredError, type CloseAgentOptions, type CloseAgentResult, type CloseSubstate, type CloseTaskResult, type CommandResolutionResult, type CommandResolver, CrossWorkstreamEdgeError, type CtlLink, CycleError, DEFAULT_SUBSTATE, DRIFT_REPORT_CAP, type Db, type DeleteTaskResult, type DoctorCheck, type DoctorStatus, type DoctorSummary, DriftDetectedError, type DriftRecord, type DriftReport, EXPECTED_TABLES, type EvidenceOption, type FileLockOptions, type FleetHazard, type FlushResult, type FsProbe, type FullDag, GLYPH, type GroupSummary, type HazardSeverity, type Hlc, HlcOverflowError, HlcParseError, HomeDirAsProjectRootError, type IdFromTitleResult, type IngestFromDbResult, type IngestResult, type InsertAgentInput, InvalidSubstateError, type InverseOp, KNOWN_INTENTS, KNOWN_VERBS, type KickAgentOptions, type KickAgentResult, type KickProcessExecutor, type KickSignal, type KnownIntent, LinkConflictError, type LinkState, type LinkStatus, type ListLiveAgentsOptions, type ListLogsOptions, type ListNotesOptions, type ListReadyOptions, type ListTasksOptions, type LiveAgent, type LiveAgentsView, type LoadFullDagOptions, type LoadWorkstreamSnapshotOptions, type LocalIntent, type LogKind, type LogRow, MACHINE_LOCAL_ENTITIES, MACHINE_LOCAL_TABLES, MU_SHIM_MARKER, MachineIdentityMissingError, type MachineLocalEntity, type MachineLocalLoss, type MachineLocalTable, type MuxBackend, type MuxBackendName, MuxError, type MuxPane, type MuxPaneStatus, type MuxSession, type MuxWindow, type NewSessionOptions, type NewWindowOptions, NoForegroundProcessError, NoMultiplexerError, NothingToUndoError, OP_CTX_DDL, type Op, type OpContext, OpEntityNotSyncedError, OpKeyMalformedError, type OpenDbOptions, type OwnedTasksSummary, PANE_ID_RE, PEER_SHORT_LEN, PEER_STALE_MS, PORTABLE_TABLES, PaneNotFoundError, type ParkTaskOptions, type PeerSegment, type PeerStatus, type PortableTable, type RebuildOptions, type RebuildReport, RebuildTargetExistsError, RebuildTargetIsSourceError, type ReconcileMode, type ReconcileOptions, type ReconcileReport, type ReleaseResult, type ReleaseTaskOptions, type RemoveBlockEdgeResult, type RenderableOp, type RenderedOp, type ReparentTaskResult, type RoiBucket, type RuntimeState, SEGMENT_FORMAT_VERSION, SYNCED_ENTITIES, SchemaTooOldError, type SegmentDefect, type SegmentDefectKind, type SegmentLine, type SegmentManifest, type SendOptions, type SendResult, type SendWarning, type SetStatusOptions, type SetStatusResult, type SlugifyResult, type SpawnAgentOptions, type SplitWindowOptions, type StateReading, type StateSource, type StrandedWorkspaceOrphan, SubstateReasonRequiredError, type SyncPassResult, SyncPeerNotFoundError, SyncPeerRefAmbiguousError, SyncSourceNotFoundError, type SyncedEntity, TASK_STATUSES, TASK_STATUS_LIST, TASK_SUBSTATES, TASK_SUBSTATE_ROWS, TaskAlreadyOwnedError, type TaskEdgeWithStatus, type TaskEdges, type TaskEdgesWithStatus, TaskExistsError, TaskInTriageError, TaskNotFoundError, TaskNotInWorkstreamError, type TaskNoteRow, type TaskPair, TaskParkStateError, TaskParkedError, type TaskRow, type TaskStatus, type TaskSubstate, type TaskWaitOptions, type TaskWaitRef, type TaskWaitResult, type TaskWaitTaskState, type TeardownResult, TmuxError, type TmuxExecResult, type TmuxExecutor, type TmuxPane, type TmuxSession, type TmuxWindow, type Track, type Transport, UNKNOWN_REASON, UndoGroupNotFoundError, type UndoOptions, type UndoPlan, type UndoResult, UndoSupersededError, type UpdateTaskOptions, type UpdateTaskResult, type VcsBackend, type VcsBackendName, WORKSPACE_STALE_THRESHOLD, WorkspaceExistsError, type WorkspaceFailure, WorkspaceNotFoundError, type WorkspaceOrphan, WorkspacePathNotEmptyError, WorkspacePreservedError, type WorkspaceRow, type WorkspaceStaleness, WorkstreamExistsError, WorkstreamNameInvalidError, type WorkstreamOptions, type WorkstreamSnapshot, type WorkstreamSnapshotSlowFields, type WorkstreamSummary, abortAgent, acceptTask, activeMux, addBlockEdge, addNote, addTask, adoptAgent, agentKey, agentStateGlyph, agentStateHistogram, ambientFlush, ambientIngest, ambientSyncPass, appendLog, applyIncomingOp, applyOp, applyOps, assertValidPaneId, awaitPaneQuiescence, backendByName, capturePane, checkCaseCollisions, checkCheapDriftInvariant, checkCommandResolvable, checkDbInsideSyncDir, checkDrift, checkFleetHazards, checkNetworkMount, claimTask, classifyFsType, closeAgent, closeTask, compareHlc, composeAgentTitle, countProblems, createWorkspace, ctlRuntimeState, currentAgentName, currentOpContext, currentPaneTitle, decorateWithDirty, decorateWithStaleness, defaultDbPath, defaultSendDelayMs, defaultSendReadinessMs, defaultSpawnLivenessMs, defaultStateDir, deleteAgent, deleteTask, detectBackend, detectMux, discoverPeers, driftRemediation, emitEvent, ensureWorkstream, envVarNameForCli, expectsCtl, findCaseCollisions, flushSegment, foregroundPgid, formatDriftRecord, formatHlc, formatPair, freeWorkspace, getAgent, getAgentByPane, getParallelTracks, getPrerequisites, getTask, getTaskEdges, getTaskEdgesWithStatus, getWaitPollCount, getWatermark, getWorkspaceForAgent, getWorkspaceStaleness, gitBackend, hasWorkMarker, idFromTitle, idFromTitleVerbose, ingestFromDb, ingestSegment, insertAgent, inspectLinks, installCapture, interruptAgent, isKickSignal, isPathInside, isTaskStatus, isValidAgentName, isValidPair, isValidPaneId, isValidTaskId, isValidWorkstreamName, isWorkspaceStale, jjBackend, kickAgent, killPane, killSession, latestSeq, linkPi, linkSkill, listAgents, listAllOrphanWorkspaces, listBlocked, listGoals, listInProgress, listLiveAgents, listLogs, listNotes, listPanes, listPanesInSession, listReady, listRecentClosed, listRecentGroups, listSessions, listTasks, listTasksByOwner, listWindows, listWorkspaceOrphans, listWorkspaces, listWorkstreams, loadDoctorChecks, loadDoctorSummary, loadFullDag, loadWorkstreamSnapshot, loadWorkstreamSnapshotFast, loadWorkstreamSnapshotSlow, localMachineId, locksDir, mapLegacyStatus, mergeSnapshotFastSlow, mostRecentGroup, murmurAvailable, mux, muxByName, newSession, newSessionWithPane, newWindow, nextHlc, noneBackend, opSubject, openDb, openTask, paneExists, paneTTY, parkTask, parseAgentNameFromTitle, parseHlc, parseOpKey, parsePsTtyOutput, peerStatuses, planUndo, priorFieldValue, probeFilesystem, readAgent, readAgentStates, readFileLockMeta, readManifest, rebuildInto, receiveHlc, reconcile, refreshAgentTitle, releaseTask, remediationParagraph, removeBlockEdge, renderForest, renderOp, renderOpLine, renderTaskTree, repairPeer, reparentTask, resetCommandResolverForTests, resetKickProcessExecutor, resetMux, resetSleep, resetTmuxExecutor, resetWaitPollCount, resetWatermark, resolveActorIdentity, resolveCliCommand, resolveCliCommandWithSource, resolveGroupId, resolvePair, resolvePeerRef, roiBucket, segmentLineCount, segmentPath, selectLayout, sendToAgent, sendToPane, sendViaTransport, sessionExists, setCommandResolverForTests, setKickProcessExecutor, setMuxForTests, setPaneTitle, setSleepForTests, setTaskStatus, setTmuxExecutor, setWaitSleepForTests, setWaitStuckWarnForTests, setWatermark, slBackend, sleep, slugifyTitle, slugifyTitleVerbose, spawnAgent, splitWindow, summarizeOwnedTasks, summarizeWorkstream, syncDir, syncEnabled, syncPass, teardownWorkstream, tmux, tmuxBackend, transportNextSteps, undoGroup, unparkTask, updateTask, verifyAgainstManifest, waitForTasks, withCaptureSuppressed, withFileLock, withOpContext, workspacePath, workspacesRoot, yankCommandForCheck };
5693
+ export { AGENT_STATE_GLYPH, type AbortAgentOptions, type AbortResult, type AddNoteOptions, type AddTaskOptions, type AdoptAgentOptions, type AdoptAgentResult, AgentAbortNeedsCtlError, AgentAbortTimeoutError, AgentBusyError, AgentCtlUnreachableError, AgentDiedOnSpawnError, AgentExistsError, AgentExtensionOutdatedError, AgentFreshNeedsCtlError, AgentNameExhaustedError, AgentNotFoundError, AgentNotInWorkstreamError, type AgentRow, AgentSlashCommandUnsupportedError, AgentSpawnCliNotFoundError, AgentSpawnStartupError, type AmbientOptions, type AmbientResult, type AppendLogOptions, type ApplyResult, type BlockEdgeResult, CAPTURE_TRIGGER_DDL, CURRENT_SCHEMA_VERSION, type CaptureDb, type CaptureIntent, type CaptureOptions, type CaseCollision, type CheapDriftReport, type ClaimResult, type ClaimTaskOptions, ClaimerNotRegisteredError, type CloseAgentOptions, type CloseAgentResult, type CloseSubstate, type CloseTaskResult, type CommandResolutionResult, type CommandResolver, CrossWorkstreamEdgeError, type CtlLink, CycleError, DEFAULT_SUBSTATE, DRIFT_REPORT_CAP, type Db, type DeleteTaskResult, type DoctorCheck, type DoctorStatus, type DoctorSummary, DriftDetectedError, type DriftRecord, type DriftReport, EXPECTED_TABLES, type EvidenceOption, type FileLockOptions, type FleetHazard, type FlushResult, type FsProbe, type FullDag, GLYPH, GroupIdAmbiguousError, type GroupSummary, HazardFailError, type HazardSeverity, type Hlc, HlcOverflowError, HlcParseError, HomeDirAsProjectRootError, type IdFromTitleResult, type IngestFromDbResult, type IngestResult, type InsertAgentInput, InvalidSubstateError, type InverseOp, KNOWN_INTENTS, KNOWN_VERBS, type KickAgentOptions, type KickAgentResult, type KickProcessExecutor, type KickSignal, type KnownIntent, LinkConflictError, type LinkState, type LinkStatus, type ListLiveAgentsOptions, type ListLogsOptions, type ListNotesOptions, type ListReadyOptions, type ListTasksOptions, type LiveAgent, type LiveAgentsView, type LoadFullDagOptions, type LoadWorkstreamSnapshotOptions, type LocalIntent, type LogKind, LogKindReservedError, type LogRow, MACHINE_LOCAL_ENTITIES, MACHINE_LOCAL_TABLES, MU_SHIM_MARKER, MachineIdentityMissingError, type MachineLocalEntity, type MachineLocalLoss, type MachineLocalTable, type MuxBackend, type MuxBackendName, MuxError, type MuxPane, type MuxPaneStatus, type MuxSession, type MuxWindow, type NewSessionOptions, type NewWindowOptions, NoForegroundProcessError, NoMultiplexerError, NothingToUndoError, OP_CTX_DDL, type Op, type OpContext, OpEntityNotSyncedError, OpKeyMalformedError, type OpenDbOptions, type OwnedTasksSummary, PANE_ID_RE, PEER_SHORT_LEN, PEER_STALE_MS, PORTABLE_TABLES, PaneNotFoundError, PaneNotInSessionError, type ParkTaskOptions, type PeerSegment, type PeerStatus, type PortableTable, ReaperDetectedDuringWaitError, type RebuildOptions, type RebuildReport, RebuildTargetExistsError, RebuildTargetIsSourceError, type ReconcileMode, type ReconcileOptions, type ReconcileReport, type ReleaseResult, type ReleaseTaskOptions, type RemoveBlockEdgeResult, type RenderableOp, type RenderedOp, type ReparentTaskResult, type RoiBucket, type RuntimeState, SEGMENT_FORMAT_VERSION, SYNCED_ENTITIES, SchemaTooNewError, SchemaTooOldError, type SegmentDefect, type SegmentDefectKind, type SegmentLine, type SegmentManifest, type SendOptions, type SendResult, type SendWarning, type SetStatusOptions, type SetStatusResult, type SlugifyResult, type SpawnAgentOptions, type SplitWindowOptions, StallDetectedDuringWaitError, type StateReading, type StateSource, type StrandedWorkspaceOrphan, SubstateReasonRequiredError, type SyncPassResult, SyncPeerNotFoundError, SyncPeerRefAmbiguousError, SyncSourceNotFoundError, type SyncedEntity, TASK_STATUSES, TASK_STATUS_LIST, TASK_SUBSTATES, TASK_SUBSTATE_ROWS, TaskAlreadyOwnedError, TaskClaimStaleWorkspaceError, type TaskEdgeWithStatus, type TaskEdges, type TaskEdgesWithStatus, TaskExistsError, TaskIdInvalidError, TaskInTriageError, TaskNotFoundError, TaskNotInWorkstreamError, type TaskNoteRow, type TaskPair, TaskParkStateError, TaskParkedError, type TaskRow, type TaskStatus, type TaskSubstate, TaskTitleSlugEmptyError, type TaskWaitOptions, type TaskWaitRef, type TaskWaitResult, type TaskWaitTaskState, type TeardownResult, TmuxError, type TmuxExecResult, type TmuxExecutor, type TmuxPane, type TmuxSession, type TmuxWindow, type Track, type Transport, UNKNOWN_REASON, UndoGroupNotFoundError, type UndoOptions, type UndoPlan, type UndoResult, UndoSupersededError, type UpdateTaskOptions, type UpdateTaskResult, type VcsBackend, type VcsBackendName, WORKSPACE_STALE_THRESHOLD, WorkspaceConflictError, WorkspaceDirtyError, WorkspaceExistsError, type WorkspaceFailure, WorkspaceNotFoundError, type WorkspaceOrphan, WorkspacePathNotEmptyError, WorkspacePreservedError, type WorkspaceRow, type WorkspaceStaleness, WorkspaceVcsRequiredError, WorkstreamExistsError, WorkstreamNameInvalidError, WorkstreamNameReservedError, WorkstreamNotFoundError, type WorkstreamOptions, type WorkstreamSnapshot, type WorkstreamSnapshotSlowFields, type WorkstreamSummary, abortAgent, acceptTask, activeMux, addBlockEdge, addNote, addTask, adoptAgent, agentKey, agentStateGlyph, agentStateHistogram, ambientFlush, ambientIngest, ambientSyncPass, appendLog, applyIncomingOp, applyOp, applyOps, assertValidPaneId, awaitPaneQuiescence, backendByName, capturePane, checkCaseCollisions, checkCheapDriftInvariant, checkCommandResolvable, checkDbInsideSyncDir, checkDrift, checkFleetHazards, checkNetworkMount, claimTask, classifyFsType, closeAgent, closeTask, compareHlc, composeAgentTitle, countProblems, createWorkspace, ctlRuntimeState, currentAgentName, currentOpContext, currentPaneTitle, decorateWithDirty, decorateWithStaleness, defaultDbPath, defaultSendDelayMs, defaultSendReadinessMs, defaultSpawnLivenessMs, defaultStateDir, deleteAgent, deleteTask, detectBackend, detectMux, discoverPeers, driftRemediation, emitEvent, ensureWorkstream, envVarNameForCli, expectsCtl, findCaseCollisions, flushSegment, foregroundPgid, formatDriftRecord, formatHlc, formatPair, freeWorkspace, getAgent, getAgentByPane, getParallelTracks, getPrerequisites, getTask, getTaskEdges, getTaskEdgesWithStatus, getWaitPollCount, getWatermark, getWorkspaceForAgent, getWorkspaceStaleness, gitBackend, hasWorkMarker, idFromTitle, idFromTitleVerbose, ingestFromDb, ingestSegment, insertAgent, inspectLinks, installCapture, interruptAgent, isKickSignal, isPathInside, isTaskStatus, isValidAgentName, isValidPair, isValidPaneId, isValidTaskId, isValidWorkstreamName, isWorkspaceStale, jjBackend, kickAgent, killPane, killSession, latestSeq, linkPi, linkSkill, listAgents, listAllOrphanWorkspaces, listBlocked, listGoals, listInProgress, listLiveAgents, listLogs, listNotes, listPanes, listPanesInSession, listReady, listRecentClosed, listRecentGroups, listSessions, listTasks, listTasksByOwner, listWindows, listWorkspaceOrphans, listWorkspaces, listWorkstreams, loadDoctorChecks, loadDoctorSummary, loadFullDag, loadWorkstreamSnapshot, loadWorkstreamSnapshotFast, loadWorkstreamSnapshotSlow, localMachineId, locksDir, mapLegacyStatus, mergeSnapshotFastSlow, mostRecentGroup, murmurAvailable, mux, muxByName, newSession, newSessionWithPane, newWindow, nextHlc, noneBackend, opSubject, openDb, openTask, paneExists, paneTTY, parkTask, parseAgentNameFromTitle, parseHlc, parseOpKey, parsePsTtyOutput, peerStatuses, planUndo, priorFieldValue, probeFilesystem, readAgent, readAgentStates, readFileLockMeta, readManifest, rebuildInto, receiveHlc, reconcile, refreshAgentTitle, releaseTask, remediationParagraph, removeBlockEdge, renderForest, renderOp, renderOpLine, renderTaskTree, repairPeer, reparentTask, resetCommandResolverForTests, resetKickProcessExecutor, resetMux, resetSleep, resetTmuxExecutor, resetWaitPollCount, resetWatermark, resolveActorIdentity, resolveCliCommand, resolveCliCommandWithSource, resolveGroupId, resolvePair, resolvePeerRef, roiBucket, segmentLineCount, segmentPath, selectLayout, sendToAgent, sendToPane, sendViaTransport, sessionExists, setCommandResolverForTests, setKickProcessExecutor, setMuxForTests, setPaneTitle, setSleepForTests, setTaskStatus, setTmuxExecutor, setWaitSleepForTests, setWaitStuckWarnForTests, setWatermark, slBackend, sleep, slugifyTitle, slugifyTitleVerbose, spawnAgent, splitWindow, summarizeOwnedTasks, summarizeWorkstream, syncDir, syncEnabled, syncPass, teardownWorkstream, tmux, tmuxBackend, transportNextSteps, undoGroup, unparkTask, updateTask, verifyAgainstManifest, waitForTasks, withCaptureSuppressed, withFileLock, withOpContext, workspacePath, workspacesRoot, yankCommandForCheck };