@polderlabs/bizar-omp 0.7.7 → 0.7.9

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 (73) hide show
  1. package/README.md +15 -11
  2. package/dist/cli/agents-ink.d.ts +9 -0
  3. package/dist/cli/agents-ink.d.ts.map +1 -1
  4. package/dist/cli/agents-ink.js +57 -15
  5. package/dist/cli/agents-ink.js.map +1 -1
  6. package/dist/cli/agents.d.ts +1 -1
  7. package/dist/cli/agents.d.ts.map +1 -1
  8. package/dist/cli/agents.js +23 -4
  9. package/dist/cli/agents.js.map +1 -1
  10. package/dist/cli/doctor.d.ts +4 -4
  11. package/dist/cli/omb-update.d.ts.map +1 -1
  12. package/dist/cli/omb-update.js +17 -2
  13. package/dist/cli/omb-update.js.map +1 -1
  14. package/dist/core/acceptance.d.ts.map +1 -1
  15. package/dist/core/acceptance.js +22 -2
  16. package/dist/core/acceptance.js.map +1 -1
  17. package/dist/core/transitions.d.ts.map +1 -1
  18. package/dist/core/transitions.js +17 -1
  19. package/dist/core/transitions.js.map +1 -1
  20. package/dist/core/types.d.ts +13 -0
  21. package/dist/core/types.d.ts.map +1 -1
  22. package/dist/core/types.js +15 -1
  23. package/dist/core/types.js.map +1 -1
  24. package/dist/extension.d.ts +2 -0
  25. package/dist/extension.d.ts.map +1 -1
  26. package/dist/extension.js +220 -79
  27. package/dist/extension.js.map +1 -1
  28. package/dist/omp/compatibility.d.ts +5 -5
  29. package/dist/omp/compatibility.d.ts.map +1 -1
  30. package/dist/omp/compatibility.js +2 -2
  31. package/dist/omp/compatibility.js.map +1 -1
  32. package/dist/omp/daemon-service.d.ts +2 -0
  33. package/dist/omp/daemon-service.d.ts.map +1 -1
  34. package/dist/omp/daemon-service.js +38 -7
  35. package/dist/omp/daemon-service.js.map +1 -1
  36. package/dist/omp/lifecycle.d.ts +1 -1
  37. package/dist/omp/lifecycle.d.ts.map +1 -1
  38. package/dist/omp/lifecycle.js +2 -1
  39. package/dist/omp/lifecycle.js.map +1 -1
  40. package/dist/omp/omb-sessions.d.ts +54 -5
  41. package/dist/omp/omb-sessions.d.ts.map +1 -1
  42. package/dist/omp/omb-sessions.js +167 -22
  43. package/dist/omp/omb-sessions.js.map +1 -1
  44. package/dist/omp/omb-settings.d.ts +9 -0
  45. package/dist/omp/omb-settings.d.ts.map +1 -1
  46. package/dist/omp/omb-settings.js +7 -0
  47. package/dist/omp/omb-settings.js.map +1 -1
  48. package/dist/omp/terminal-title.d.ts +70 -0
  49. package/dist/omp/terminal-title.d.ts.map +1 -0
  50. package/dist/omp/terminal-title.js +123 -0
  51. package/dist/omp/terminal-title.js.map +1 -0
  52. package/dist/omp/tmux.d.ts +69 -0
  53. package/dist/omp/tmux.d.ts.map +1 -1
  54. package/dist/omp/tmux.js +193 -14
  55. package/dist/omp/tmux.js.map +1 -1
  56. package/dist/storage/journal.d.ts +1 -1
  57. package/dist/storage/journal.d.ts.map +1 -1
  58. package/dist/storage/journal.js +2 -1
  59. package/dist/storage/journal.js.map +1 -1
  60. package/dist/tools/workflow.d.ts.map +1 -1
  61. package/dist/tools/workflow.js +11 -1
  62. package/dist/tools/workflow.js.map +1 -1
  63. package/docs/compatibility/baseline.json +8 -7
  64. package/docs/compatibility/supported-surfaces.json +2 -1
  65. package/docs/development/ci-release-implementation-guide.md +3 -3
  66. package/docs/releases/0.7.8.md +21 -0
  67. package/docs/releases/0.7.9.md +18 -0
  68. package/docs/releases/npm-publishing.md +12 -12
  69. package/docs/releases/support-matrix.md +1 -1
  70. package/package.json +1 -1
  71. package/skills/omp-native-development/SKILL.md +1 -1
  72. package/skills/omp-native-development/references/accuracy-and-versioning.md +1 -1
  73. package/skills/omp-native-development/references/source-manifest.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/omp/lifecycle.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,WAAW,cAAc;IAAG,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;CAAE;AAEjF,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;KAAE,CAAA;CAAE,GAAG,cAAc,CAEnJ;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,iBAAiB,0CAMnF"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/omp/lifecycle.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE5E,MAAM,WAAW,cAAc;IAAG,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;CAAE;AAEjF,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;KAAE,CAAA;CAAE,GAAG,cAAc,CAEnJ;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,iBAAiB,0CAMnF"}
@@ -1,11 +1,12 @@
1
1
  import { createRun } from "../core/transitions.js";
2
2
  import { nativeSessionReference } from "./context.js";
3
+ import { isRunPhaseActive } from "../core/types.js";
3
4
  export function bindSession(journal, ctx) {
4
5
  return { journal, owner: nativeSessionReference(ctx) ?? `session:${ctx.cwd ?? process.cwd()}`, cwd: ctx.cwd ?? process.cwd() };
5
6
  }
6
7
  export function startObjective(binding, objective) {
7
8
  const existing = binding.journal.snapshot().run;
8
- if (existing && !["complete", "failed", "cancelled"].includes(existing.phase))
9
+ if (isRunPhaseActive(existing?.phase))
9
10
  throw new Error("A non-terminal workflow run already exists");
10
11
  const run = createRun(binding.cwd, objective, undefined, binding.owner);
11
12
  binding.journal.create(run);
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/omp/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAKtD,MAAM,UAAU,WAAW,CAAC,OAAgB,EAAE,GAAqF;IACjI,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,sBAAsB,CAAC,GAAG,CAAC,IAAI,WAAW,GAAG,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;AACjI,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAuB,EAAE,SAA4B;IAClF,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC;IAChD,IAAI,QAAQ,IAAI,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAC7I,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACxE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/omp/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAA0B,MAAM,kBAAkB,CAAC;AAI5E,MAAM,UAAU,WAAW,CAAC,OAAgB,EAAE,GAAqF;IACjI,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,sBAAsB,CAAC,GAAG,CAAC,IAAI,WAAW,GAAG,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;AACjI,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAuB,EAAE,SAA4B;IAClF,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC;IAChD,IAAI,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IACrG,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACxE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { type Lease } from "../storage/lease.js";
2
- import type { OmbAgentsScope } from "./omb-settings.js";
2
+ import type { OmbAgentsScope, OmbSessionFilter } from "./omb-settings.js";
3
3
  import { type OmbSessionActivity } from "./session-activity.js";
4
4
  export declare const BIZAR_SESSION_PREFIX = "bizar-";
5
5
  /**
@@ -90,6 +90,17 @@ interface OmpLinkManifest {
90
90
  source: string;
91
91
  id: string;
92
92
  }
93
+ /**
94
+ * Drop the journal link manifest for one session.
95
+ *
96
+ * The manifest and the tmux generation binding are the two independent
97
+ * authorities a dead-pane respawn is checked against. Removing the manifest is
98
+ * how a host that switched conversations is made unrecoverable on purpose: the
99
+ * respawn check can no longer resolve a generation, so it refuses instead of
100
+ * resuming a journal this host has stopped writing. Unlike a tombstone this
101
+ * hides nothing — the host is still running and must stay visible.
102
+ */
103
+ export declare function clearOmbSessionReference(session: string, env?: NodeJS.ProcessEnv): boolean;
93
104
  /** Keep an intentionally removed session out of journal-based discovery. */
94
105
  export declare function markOmbSessionDeleted(session: string, env?: NodeJS.ProcessEnv): boolean;
95
106
  /** Reusing a session name makes its native journal discoverable again. */
@@ -129,14 +140,31 @@ export declare function indexOmbSessionFile(session: string, journal: string, cw
129
140
  }>;
130
141
  /** Compare session paths by file identity, including OMP picker hard links. */
131
142
  export declare function sameOmpSessionFile(left: string, right: string): boolean;
143
+ /**
144
+ * Native session generation recorded in one journal's header.
145
+ *
146
+ * A caller re-pointing a host at a different conversation must be told exactly
147
+ * which generation it is switching to; resolving "the newest journal" would
148
+ * name a different session in a directory shared by every session for that
149
+ * project. Only the id is exposed — the rest of the header is internal.
150
+ */
151
+ export declare function readOmbJournalNativeId(journal: string): Promise<string | undefined>;
132
152
  /** Native OMP title first, then a deterministic prompt/project fallback. */
133
153
  export declare function ombSessionDisplayName(session: Pick<OmbSessionInfo, "project" | "id" | "nativeName" | "firstMessage" | "nativeSessionAvailable">): string;
134
154
  /** Return whether a session belongs to the current-directory scope filter. */
135
155
  export declare function sessionMatchesCwdScope(session: Pick<OmbSessionInfo, "cwd" | "project">, scopeCwd: string): boolean;
136
- /** Apply the agents hub's persisted project/stopped visibility policy. */
156
+ /**
157
+ * Apply the agents hub's persisted scope and stopped/active visibility policy.
158
+ *
159
+ * `stopped` isolates the conversations whose host exited, which is how a
160
+ * recoverable session is found to continue it; `active` is the opposite. A
161
+ * boolean could not express "only stopped", and defaulting it to false hid
162
+ * every stopped session, so the default filter is `all`.
163
+ */
137
164
  export declare function filterOmbSessions(sessions: readonly OmbSessionInfo[], cwd: string, options: {
138
165
  scope: OmbAgentsScope;
139
166
  includeStopped: boolean;
167
+ sessionFilter?: OmbSessionFilter;
140
168
  }): OmbSessionInfo[];
141
169
  /**
142
170
  * Resolve the tmux-compatible multiplexer binary. `BIZAR_TMUX_BIN` always
@@ -152,9 +180,14 @@ export declare function resolveMultiplexerBin(env?: NodeJS.ProcessEnv): string;
152
180
  *
153
181
  * Work wins over attachment: a detached client must never hide an agent that
154
182
  * is still processing, which is the failure this hub exists to prevent.
155
- * `waiting` refines an attached session that finished its turn, `connected`
156
- * covers an attached session with no structured activity yet, and
157
- * `disconnected` covers a live session with no client and no work in flight.
183
+ * `waiting` means the agent finished its turn and wants input — that is a
184
+ * property of the journal, not of the client, so it is reported whether or
185
+ * not a terminal is attached. Reading attachment first would label every
186
+ * detached-but-idle session "disconnected" and bury the sessions that are
187
+ * actually ready for the operator.
188
+ * `connected` covers an attached session with no structured activity yet, and
189
+ * `disconnected` is reserved for a live session with no client and no evidence
190
+ * either way, so it never overwrites a known phase.
158
191
  */
159
192
  export declare function ombSessionState(input: {
160
193
  dead: boolean;
@@ -206,6 +239,22 @@ export type OmbHostSession = {
206
239
  export declare function currentOmbHostSession(cwd?: string, env?: NodeJS.ProcessEnv): Promise<OmbHostSession>;
207
240
  /** Discover Bizar-owned tmux sessions without touching unrelated tmux sessions. */
208
241
  export declare function listOmbSessions(cwd?: string, env?: NodeJS.ProcessEnv, options?: ListOmbSessionsOptions): Promise<OmbSessionInfo[]>;
242
+ /**
243
+ * Whether the multiplexer answered a session scan, and if not, why.
244
+ *
245
+ * `listOmbSessions` cannot express this on its own: a failed scan yields an
246
+ * empty list, which is indistinguishable from "there are genuinely no
247
+ * sessions". A caller rendering that as an empty hub would blank every row on a
248
+ * transient tmux restart, so the distinction has to survive the call.
249
+ */
250
+ export interface OmbScanAuthority {
251
+ /** True only when the multiplexer answered the scan itself. */
252
+ known: boolean;
253
+ /** Why the scan is not authoritative, for display. */
254
+ reason?: string;
255
+ }
256
+ /** Probe the same multiplexer command discovery uses, without scanning. */
257
+ export declare function ombScanAuthority(cwd?: string, env?: NodeJS.ProcessEnv): Promise<OmbScanAuthority>;
209
258
  /** Re-resolve a selected row immediately before a mutating action. */
210
259
  export declare function assertOmbSessionGeneration(expected: Pick<OmbSessionInfo, "name" | "generation">, cwd?: string, env?: NodeJS.ProcessEnv): Promise<OmbSessionInfo>;
211
260
  /** Return the most recently attached live session for one exact project path. */
@@ -1 +1 @@
1
- {"version":3,"file":"omb-sessions.d.ts","sourceRoot":"","sources":["../../src/omp/omb-sessions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"omb-sessions.d.ts","sourceRoot":"","sources":["../../src/omp/omb-sessions.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { createHash } from "node:crypto";
3
3
  import { parseTmuxSessionRecords } from "./tmux.js";
4
4
  import { access, readdir, stat } from "node:fs/promises";
5
5
  import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
6
- import { basename, join, relative, resolve, sep } from "node:path";
6
+ import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
7
7
  import { homedir, tmpdir } from "node:os";
8
8
  import { ContractError } from "../core/errors.js";
9
9
  import { acquireLease } from "../storage/lease.js";
@@ -28,6 +28,12 @@ export function isLegacyOmbSessionDir(directory, session, env = process.env) {
28
28
  const rel = relative(root, candidate);
29
29
  return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && basename(candidate) === session && rel.split(sep).includes("native-sessions");
30
30
  }
31
+ /** Whether a directory is Bizar's own native session root for this profile. */
32
+ function isOmbSessionNativeDir(directory, env) {
33
+ const root = resolve(nativeSessionRoot(env));
34
+ const rel = relative(root, resolve(directory));
35
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
36
+ }
31
37
  /**
32
38
  * Whether a name is one of the sessions Bizar owns, and may therefore kill,
33
39
  * rename, tombstone, or adopt as its own native journal.
@@ -56,6 +62,29 @@ function readOmpLinkManifest(session, env) {
56
62
  return undefined;
57
63
  }
58
64
  }
65
+ /**
66
+ * Drop the journal link manifest for one session.
67
+ *
68
+ * The manifest and the tmux generation binding are the two independent
69
+ * authorities a dead-pane respawn is checked against. Removing the manifest is
70
+ * how a host that switched conversations is made unrecoverable on purpose: the
71
+ * respawn check can no longer resolve a generation, so it refuses instead of
72
+ * resuming a journal this host has stopped writing. Unlike a tombstone this
73
+ * hides nothing — the host is still running and must stay visible.
74
+ */
75
+ export function clearOmbSessionReference(session, env = process.env) {
76
+ if (!isOmbSessionName(session))
77
+ return false;
78
+ try {
79
+ rmSync(join(ombSessionDir(session, env), OMP_LINK_MANIFEST), { force: true });
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ // `rmSync` not throwing is not proof the link is gone, and this boolean is
85
+ // what the caller uses to decide the host failed closed. Re-read instead.
86
+ return readOmpLinkManifest(session, env) === undefined;
87
+ }
59
88
  function removeOmpSessionFile(session, env) {
60
89
  const manifest = readOmpLinkManifest(session, env);
61
90
  if (!manifest)
@@ -289,6 +318,17 @@ function cleanText(value) {
289
318
  function asRecord(value) {
290
319
  return typeof value === "object" && value !== null ? value : undefined;
291
320
  }
321
+ /**
322
+ * Native session generation recorded in one journal's header.
323
+ *
324
+ * A caller re-pointing a host at a different conversation must be told exactly
325
+ * which generation it is switching to; resolving "the newest journal" would
326
+ * name a different session in a directory shared by every session for that
327
+ * project. Only the id is exposed — the rest of the header is internal.
328
+ */
329
+ export async function readOmbJournalNativeId(journal) {
330
+ return (await readNativeSessionFile(journal))?.id;
331
+ }
292
332
  /**
293
333
  * Parse one journal's session header, title, and first user prompt.
294
334
  *
@@ -366,8 +406,15 @@ async function readNativeSessionFile(journal) {
366
406
  createdAt: Number.isFinite(timestamp) ? Math.floor(timestamp / 1000) : undefined,
367
407
  };
368
408
  }
369
- /** Metadata plus live activity for a Bizar-owned native session directory. */
370
- async function readNativeSessionSnapshot(sessionDir, now, env = process.env) {
409
+ /**
410
+ * Metadata plus live activity for one native session directory.
411
+ *
412
+ * `nativeId` narrows the read to a single journal. OMP's project directory is
413
+ * shared by every session for that project, so without this the newest journal
414
+ * in the directory would be reported as one host's activity even when another
415
+ * session is the one working.
416
+ */
417
+ async function readNativeSessionSnapshot(sessionDir, now, env = process.env, nativeId) {
371
418
  let journals = await listSessionJournals(sessionDir);
372
419
  const sessionName = basename(sessionDir);
373
420
  const reference = isOmbSessionName(sessionName) ? readOmpLinkManifest(sessionName, env) : undefined;
@@ -378,6 +425,21 @@ async function readNativeSessionSnapshot(sessionDir, now, env = process.env) {
378
425
  }
379
426
  catch { /* keep a legacy private journal available if the pointer went stale */ }
380
427
  }
428
+ if (nativeId) {
429
+ const matches = [];
430
+ for (const journal of journals) {
431
+ const metadata = await readNativeSessionFile(journal);
432
+ if (metadata?.id === nativeId)
433
+ matches.push(journal);
434
+ }
435
+ // A directory that no longer holds this generation proves nothing about it;
436
+ // reporting another session's journal here would misattribute its work. The
437
+ // mismatch is reported so the caller can surface it rather than quietly
438
+ // falling back to a private copy.
439
+ if (matches.length === 0)
440
+ return { generationMismatch: true };
441
+ journals = matches;
442
+ }
381
443
  const newest = journals.at(-1);
382
444
  if (!newest)
383
445
  return {};
@@ -464,10 +526,23 @@ export function sessionMatchesCwdScope(session, scopeCwd) {
464
526
  catch { /* Path may not exist. */ }
465
527
  return sessionCwd === currentCwd;
466
528
  }
467
- /** Apply the agents hub's persisted project/stopped visibility policy. */
529
+ /**
530
+ * Apply the agents hub's persisted scope and stopped/active visibility policy.
531
+ *
532
+ * `stopped` isolates the conversations whose host exited, which is how a
533
+ * recoverable session is found to continue it; `active` is the opposite. A
534
+ * boolean could not express "only stopped", and defaulting it to false hid
535
+ * every stopped session, so the default filter is `all`.
536
+ */
468
537
  export function filterOmbSessions(sessions, cwd, options) {
469
- return sessions.filter(session => (options.includeStopped || session.state !== "stopped")
470
- && (options.scope === "all" || sessionMatchesCwdScope(session, cwd)));
538
+ const mode = options.sessionFilter ?? (options.includeStopped ? "all" : "active");
539
+ return sessions.filter(session => {
540
+ if (mode === "active" && session.state === "stopped")
541
+ return false;
542
+ if (mode === "stopped" && session.state !== "stopped")
543
+ return false;
544
+ return options.scope === "all" || sessionMatchesCwdScope(session, cwd);
545
+ });
471
546
  }
472
547
  /**
473
548
  * Resolve the tmux-compatible multiplexer binary. `BIZAR_TMUX_BIN` always
@@ -487,9 +562,14 @@ export function resolveMultiplexerBin(env = process.env) {
487
562
  *
488
563
  * Work wins over attachment: a detached client must never hide an agent that
489
564
  * is still processing, which is the failure this hub exists to prevent.
490
- * `waiting` refines an attached session that finished its turn, `connected`
491
- * covers an attached session with no structured activity yet, and
492
- * `disconnected` covers a live session with no client and no work in flight.
565
+ * `waiting` means the agent finished its turn and wants input — that is a
566
+ * property of the journal, not of the client, so it is reported whether or
567
+ * not a terminal is attached. Reading attachment first would label every
568
+ * detached-but-idle session "disconnected" and bury the sessions that are
569
+ * actually ready for the operator.
570
+ * `connected` covers an attached session with no structured activity yet, and
571
+ * `disconnected` is reserved for a live session with no client and no evidence
572
+ * either way, so it never overwrites a known phase.
493
573
  */
494
574
  export function ombSessionState(input) {
495
575
  if (input.dead)
@@ -497,9 +577,13 @@ export function ombSessionState(input) {
497
577
  const activity = input.activity;
498
578
  if (activity && activity.phase !== "unknown" && !activity.awaitingInput)
499
579
  return "working";
580
+ // A finished turn is waiting for the operator, attached or not. The stale
581
+ // marker on the activity still tells the reader how old that evidence is.
582
+ if (activity?.awaitingInput)
583
+ return "waiting";
500
584
  if (!input.attached)
501
585
  return "disconnected";
502
- return activity?.awaitingInput ? "waiting" : "connected";
586
+ return "connected";
503
587
  }
504
588
  const STATE_RANK = {
505
589
  working: 0,
@@ -554,19 +638,42 @@ function sessionProject(name) {
554
638
  return trimmed.replace(/-[a-f0-9]{10}(?:-.*)?$/, "") || trimmed || "session";
555
639
  }
556
640
  /**
557
- * One pane's fields, including the command tmux started it with. Bizar launches
558
- * every host as `omp --session-dir <native root>/<session>`, so that command is
559
- * the only exact record of which journal a live row owns.
641
+ * One pane's fields, including the command tmux started it with and the native
642
+ * session generation the host was bound to. Bizar launches every host as
643
+ * `omp --session-dir <dir>`, and `@bizar-native-id` names the exact journal
644
+ * that host writes, so both are needed to attribute activity to one session
645
+ * inside a directory shared by every session for that project.
560
646
  */
561
- const PANE_FORMAT = "#{pane_dead}\t#{pane_current_path}\t#{pane_current_command}\t#{pane_pid}\t#{pane_start_command}\t#{@bizar-session-owner}";
647
+ const PANE_FORMAT = "#{pane_dead}\t#{pane_current_path}\t#{pane_current_command}\t#{pane_pid}\t#{pane_start_command}\t#{@bizar-session-owner}\t#{@bizar-native-id}";
562
648
  /**
563
- * Session whose native directory a pane command declares.
649
+ * Native journal directory one pane writes to.
564
650
  *
565
651
  * A renamed tmux session keeps the directory its host was started with, so
566
652
  * discovery reads the binding from the pane instead of assuming the directory
567
- * matches the session name. Paths outside the Bizar native root, or names
568
- * Bizar does not own, are ignored so unrelated OMP hosts are never adopted.
653
+ * matches the session name. Bizar launches hosts with OMP's own project
654
+ * directory (`--session-dir <omp sessions>/<encoded cwd>`), so that path is
655
+ * the live journal and must be honoured.
656
+ *
657
+ * Only two directories are ever accepted: Bizar's own native root, or the
658
+ * exact OMP project directory for the pane's working directory. The owner
659
+ * option is required for the second, and even then the path must match what
660
+ * `defaultOmpSessionDir` computes — a pane naming any other directory is not
661
+ * trusted, so the hub never reads journals it does not own.
569
662
  */
663
+ function paneSessionDir(startCommand, env, owner, paneCwd) {
664
+ const declared = startCommand?.match(/--session-dir(?:=|\s+)(?:"([^"]*)"|'([^']*)'|(\S+))/);
665
+ const value = declared?.[1] ?? declared?.[2] ?? declared?.[3];
666
+ if (!value)
667
+ return undefined;
668
+ const resolved = resolve(value);
669
+ if (isOmbSessionNativeDir(resolved, env))
670
+ return resolved;
671
+ if (!owner || !isOmbSessionName(owner))
672
+ return undefined;
673
+ if (paneCwd && resolved === resolve(defaultOmpSessionDir(paneCwd, env)))
674
+ return resolved;
675
+ return undefined;
676
+ }
570
677
  function paneBoundSession(startCommand, env, owner) {
571
678
  if (owner && isOmbSessionName(owner))
572
679
  return owner;
@@ -581,13 +688,20 @@ function paneBoundSession(startCommand, env, owner) {
581
688
  }
582
689
  function parsePane(output, env, ownerOverride) {
583
690
  const [line = ""] = output.trim().split("\n");
584
- const [dead, cwd, command, pid, startCommand, owner] = line.split("\t");
691
+ const [dead, cwd, command, pid, startCommand, owner, nativeId] = line.split("\t");
692
+ const resolvedOwner = ownerOverride || owner;
585
693
  return {
586
694
  dead: dead === "1",
587
695
  cwd: cwd || "?",
588
696
  command: command || "?",
589
697
  pid: pid || "?",
590
- bound: paneBoundSession(startCommand, env, ownerOverride || owner),
698
+ bound: paneBoundSession(startCommand, env, resolvedOwner),
699
+ paneDir: paneSessionDir(startCommand, env, resolvedOwner, cwd || ""),
700
+ // The generation this host was bound to. A shared project directory holds
701
+ // every session for that project, so activity may only be read from the
702
+ // journal carrying this exact id — otherwise one host would report another
703
+ // session's work as its own.
704
+ nativeId: nativeId?.trim() || undefined,
591
705
  };
592
706
  }
593
707
  async function sessionOwner(tmux, name, cwd, childEnv) {
@@ -638,11 +752,24 @@ async function describeLiveSession(tmux, session, cwd, childEnv, env, now) {
638
752
  const authorityKnown = pane.code === 0 && /^\d+$/.test(details.pid) && details.command !== "?";
639
753
  // Prefer the journal the pane command names: after a rename the tmux session
640
754
  // and its native directory differ, and the host keeps writing the directory
641
- // it was started with.
755
+ // it was started with. The pane's `--session-dir` is the exact record of
756
+ // which journal this host writes — normally OMP's own project directory, not
757
+ // a Bizar private one. Reading a stale private copy instead reports a live,
758
+ // working session as idle.
642
759
  const declared = details.bound && details.bound !== session.name
643
760
  ? await readNativeSessionSnapshot(ombSessionDir(details.bound, env), now, env)
644
761
  : undefined;
645
- const snapshot = declared?.metadata ? declared : await readNativeSessionSnapshot(ombSessionDir(session.name, env), now, env);
762
+ const owned = declared?.metadata ? declared : await readNativeSessionSnapshot(ombSessionDir(session.name, env), now, env);
763
+ const paneSnapshot = details.paneDir && details.paneDir !== ombSessionDir(session.name, env)
764
+ ? await readNativeSessionSnapshot(details.paneDir, now, env, details.nativeId)
765
+ : undefined;
766
+ // The pane's own journal wins, and only when it is this host's generation: a
767
+ // shared project directory otherwise attributes another session's work to
768
+ // this row. A generation mismatch is not "no activity" — falling back to the
769
+ // private copy there would present a stale journal as current — so it is
770
+ // reported on the row instead.
771
+ const generationMismatch = paneSnapshot?.generationMismatch === true;
772
+ const snapshot = generationMismatch || !paneSnapshot?.activity ? owned : paneSnapshot;
646
773
  const hasReference = Boolean(readOmpLinkManifest(details.bound ?? session.name, env));
647
774
  const native = snapshot.metadata ?? (hasReference ? undefined : await readLegacyNativeSessionMetadata(details.cwd, session.createdAt, env));
648
775
  return {
@@ -651,7 +778,12 @@ async function describeLiveSession(tmux, session, cwd, childEnv, env, now) {
651
778
  binding: declared?.metadata ? details.bound : undefined,
652
779
  session: sessionRecord(session.name, {
653
780
  cwd: details.cwd,
654
- state: authorityKnown ? ombSessionState({ dead: details.dead, attached: session.attached, activity: snapshot.activity }) : "unknown",
781
+ // A pane bound to a generation its directory no longer holds cannot be
782
+ // described, so the row is unknown rather than guessing at another
783
+ // journal's activity.
784
+ state: authorityKnown
785
+ ? (generationMismatch ? "unknown" : ombSessionState({ dead: details.dead, attached: session.attached, activity: snapshot.activity }))
786
+ : "unknown",
655
787
  attached: authorityKnown && session.attached,
656
788
  activity: snapshot.activity,
657
789
  hosted: authorityKnown,
@@ -898,6 +1030,19 @@ export async function listOmbSessions(cwd = process.cwd(), env = process.env, op
898
1030
  a.project.localeCompare(b.project) ||
899
1031
  a.name.localeCompare(b.name));
900
1032
  }
1033
+ /** Probe the same multiplexer command discovery uses, without scanning. */
1034
+ export async function ombScanAuthority(cwd = process.cwd(), env = process.env) {
1035
+ const tmux = resolveMultiplexerBin(env);
1036
+ const result = await readTmux(tmux, ["list-sessions", "-F", "#{session_name}"], cwd, { ...env, BIZAR_OMP_NO_TMUX: "1" });
1037
+ if (result.code === 0)
1038
+ return { known: true };
1039
+ return {
1040
+ known: false,
1041
+ reason: result.code === 127
1042
+ ? "the multiplexer is not installed on this host"
1043
+ : `the multiplexer did not answer a session scan (exit ${result.code})`,
1044
+ };
1045
+ }
901
1046
  /** Re-resolve a selected row immediately before a mutating action. */
902
1047
  export async function assertOmbSessionGeneration(expected, cwd = process.cwd(), env = process.env) {
903
1048
  const current = (await listOmbSessions(cwd, env, { includeStopped: true, requireMultiplexer: true }))