@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.
- package/README.md +15 -11
- package/dist/cli/agents-ink.d.ts +9 -0
- package/dist/cli/agents-ink.d.ts.map +1 -1
- package/dist/cli/agents-ink.js +57 -15
- package/dist/cli/agents-ink.js.map +1 -1
- package/dist/cli/agents.d.ts +1 -1
- package/dist/cli/agents.d.ts.map +1 -1
- package/dist/cli/agents.js +23 -4
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/doctor.d.ts +4 -4
- package/dist/cli/omb-update.d.ts.map +1 -1
- package/dist/cli/omb-update.js +17 -2
- package/dist/cli/omb-update.js.map +1 -1
- package/dist/core/acceptance.d.ts.map +1 -1
- package/dist/core/acceptance.js +22 -2
- package/dist/core/acceptance.js.map +1 -1
- package/dist/core/transitions.d.ts.map +1 -1
- package/dist/core/transitions.js +17 -1
- package/dist/core/transitions.js.map +1 -1
- package/dist/core/types.d.ts +13 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +15 -1
- package/dist/core/types.js.map +1 -1
- package/dist/extension.d.ts +2 -0
- package/dist/extension.d.ts.map +1 -1
- package/dist/extension.js +220 -79
- package/dist/extension.js.map +1 -1
- package/dist/omp/compatibility.d.ts +5 -5
- package/dist/omp/compatibility.d.ts.map +1 -1
- package/dist/omp/compatibility.js +2 -2
- package/dist/omp/compatibility.js.map +1 -1
- package/dist/omp/daemon-service.d.ts +2 -0
- package/dist/omp/daemon-service.d.ts.map +1 -1
- package/dist/omp/daemon-service.js +38 -7
- package/dist/omp/daemon-service.js.map +1 -1
- package/dist/omp/lifecycle.d.ts +1 -1
- package/dist/omp/lifecycle.d.ts.map +1 -1
- package/dist/omp/lifecycle.js +2 -1
- package/dist/omp/lifecycle.js.map +1 -1
- package/dist/omp/omb-sessions.d.ts +54 -5
- package/dist/omp/omb-sessions.d.ts.map +1 -1
- package/dist/omp/omb-sessions.js +167 -22
- package/dist/omp/omb-sessions.js.map +1 -1
- package/dist/omp/omb-settings.d.ts +9 -0
- package/dist/omp/omb-settings.d.ts.map +1 -1
- package/dist/omp/omb-settings.js +7 -0
- package/dist/omp/omb-settings.js.map +1 -1
- package/dist/omp/terminal-title.d.ts +70 -0
- package/dist/omp/terminal-title.d.ts.map +1 -0
- package/dist/omp/terminal-title.js +123 -0
- package/dist/omp/terminal-title.js.map +1 -0
- package/dist/omp/tmux.d.ts +69 -0
- package/dist/omp/tmux.d.ts.map +1 -1
- package/dist/omp/tmux.js +193 -14
- package/dist/omp/tmux.js.map +1 -1
- package/dist/storage/journal.d.ts +1 -1
- package/dist/storage/journal.d.ts.map +1 -1
- package/dist/storage/journal.js +2 -1
- package/dist/storage/journal.js.map +1 -1
- package/dist/tools/workflow.d.ts.map +1 -1
- package/dist/tools/workflow.js +11 -1
- package/dist/tools/workflow.js.map +1 -1
- package/docs/compatibility/baseline.json +8 -7
- package/docs/compatibility/supported-surfaces.json +2 -1
- package/docs/development/ci-release-implementation-guide.md +3 -3
- package/docs/releases/0.7.8.md +21 -0
- package/docs/releases/0.7.9.md +18 -0
- package/docs/releases/npm-publishing.md +12 -12
- package/docs/releases/support-matrix.md +1 -1
- package/package.json +1 -1
- package/skills/omp-native-development/SKILL.md +1 -1
- package/skills/omp-native-development/references/accuracy-and-versioning.md +1 -1
- 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,
|
|
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"}
|
package/dist/omp/lifecycle.js
CHANGED
|
@@ -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 (
|
|
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;
|
|
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
|
-
/**
|
|
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`
|
|
156
|
-
*
|
|
157
|
-
*
|
|
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":"AAQA,OAAO,EAAgB,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"omb-sessions.d.ts","sourceRoot":"","sources":["../../src/omp/omb-sessions.ts"],"names":[],"mappings":"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"}
|
package/dist/omp/omb-sessions.js
CHANGED
|
@@ -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
|
-
/**
|
|
370
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
470
|
-
|
|
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`
|
|
491
|
-
*
|
|
492
|
-
*
|
|
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
|
|
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
|
|
558
|
-
*
|
|
559
|
-
*
|
|
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
|
-
*
|
|
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.
|
|
568
|
-
*
|
|
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,
|
|
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
|
|
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
|
-
|
|
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 }))
|