@volter/supercode-teams 0.3.181 → 0.3.182
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/live-sessions.mjs +50 -7
- package/package.json +1 -1
package/live-sessions.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// - Messaging tools: the marker a `message mcp` server writes for its session process, with the server alive.
|
|
15
15
|
import { EventEmitter } from 'node:events';
|
|
16
16
|
import { execFile } from 'node:child_process';
|
|
17
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, unlinkSync, watch, writeFileSync } from 'node:fs';
|
|
17
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, unlinkSync, watch, writeFileSync } from 'node:fs';
|
|
18
18
|
import { homedir } from 'node:os';
|
|
19
19
|
import { dirname, join } from 'node:path';
|
|
20
20
|
import { FrontendClient } from '@volter/supercode-frontend';
|
|
@@ -773,7 +773,11 @@ export class LiveSessions extends EventEmitter {
|
|
|
773
773
|
if (claude && alive(claude.pid)) return this.#address('claude-code', claude.sessionId);
|
|
774
774
|
const hosted = await this.#runtimeOfPid(at, deadline);
|
|
775
775
|
if (hosted) return hosted;
|
|
776
|
-
if (/(^|\/)codex$/.test(command) && typeof session === 'string' && /^[0-9a-f-]{8,64}$/.test(session)
|
|
776
|
+
if (/(^|\/)codex$/.test(command) && typeof session === 'string' && /^[0-9a-f-]{8,64}$/.test(session)) {
|
|
777
|
+
// the session that process is: the thread the command names, or the one whose subagent it is
|
|
778
|
+
const held = await rolloutSession(at, session, left());
|
|
779
|
+
if (held) return this.#address('codex', held);
|
|
780
|
+
}
|
|
777
781
|
}
|
|
778
782
|
return null;
|
|
779
783
|
}
|
|
@@ -835,12 +839,51 @@ async function ancestryOf(pid, timeout = 5000) {
|
|
|
835
839
|
return chain;
|
|
836
840
|
}
|
|
837
841
|
|
|
838
|
-
/**
|
|
839
|
-
*
|
|
840
|
-
|
|
842
|
+
/**
|
|
843
|
+
* The session Codex process `pid` is, for a command that says it runs in thread `thread`: the one thread whose rollout
|
|
844
|
+
* the process holds open (its file is named by the thread's id), or, when it holds several, the thread they all
|
|
845
|
+
* descend from. A Codex session that spawns subagents keeps each subagent's rollout open beside its own, and every one
|
|
846
|
+
* of them says in its first line which thread spawned it: such a family is one session, its root, and a command of the
|
|
847
|
+
* root or of any subagent acts as it. A process serving threads that are not one family (an app-server) names none of
|
|
848
|
+
* them, so a child cannot name a sibling. Null when `thread`'s rollout is not among them.
|
|
849
|
+
* (2026-10-07: "exactly one rollout" left a session that had spawned subagents unable to read or send its own mail.)
|
|
850
|
+
*/
|
|
851
|
+
async function rolloutSession(pid, thread, timeout = 5000) {
|
|
841
852
|
const out = await new Promise((resolve) => execFile(tool('lsof'), ['-a', '-p', String(pid), '-Fn'], { timeout, maxBuffer: 16 * 1024 * 1024 }, (_error, stdout) => resolve(String(stdout ?? ''))));
|
|
842
|
-
const rollouts = new Set(out.split('\n').filter((line) => line.startsWith('n') && /rollout-[^/]*\.jsonl$/.test(line)).map((line) => line.slice(1)));
|
|
843
|
-
|
|
853
|
+
const rollouts = [...new Set(out.split('\n').filter((line) => line.startsWith('n') && /rollout-[^/]*\.jsonl$/.test(line)).map((line) => line.slice(1)))];
|
|
854
|
+
if (!rollouts.some((path) => path.includes(thread))) return null;
|
|
855
|
+
if (rollouts.length === 1) return thread;
|
|
856
|
+
// each thread held, with the thread that spawned it (none for one a person started)
|
|
857
|
+
const parentOf = new Map();
|
|
858
|
+
for (const path of rollouts) {
|
|
859
|
+
const id = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/.exec(path)?.[1];
|
|
860
|
+
const first = rolloutHead(path);
|
|
861
|
+
if (!id || first === null) return null;
|
|
862
|
+
parentOf.set(id, /"parent_thread_id"\s*:\s*"([0-9a-f-]{8,64})"/.exec(first)?.[1] ?? null);
|
|
863
|
+
}
|
|
864
|
+
// one family: exactly one thread nobody held here spawned, and every other one reaches it through threads held here
|
|
865
|
+
const roots = [...parentOf].filter(([, parent]) => parent === null).map(([id]) => id);
|
|
866
|
+
if (roots.length !== 1) return null;
|
|
867
|
+
for (const id of parentOf.keys()) {
|
|
868
|
+
let at = id;
|
|
869
|
+
for (let depth = 0; at !== roots[0]; depth++) {
|
|
870
|
+
if (depth > 16 || !parentOf.has(at)) return null;
|
|
871
|
+
at = parentOf.get(at);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return roots[0];
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/** A rollout's first line (its session's own record), read from the file's head; null when it cannot be read. */
|
|
878
|
+
function rolloutHead(path) {
|
|
879
|
+
let file = null;
|
|
880
|
+
try {
|
|
881
|
+
file = openSync(path, 'r');
|
|
882
|
+
const head = Buffer.alloc(256 * 1024);
|
|
883
|
+
const text = head.toString('utf8', 0, readSync(file, head, 0, head.length, 0));
|
|
884
|
+
const end = text.indexOf('\n');
|
|
885
|
+
return end < 0 ? text : text.slice(0, end);
|
|
886
|
+
} catch { return null; } finally { if (file !== null) try { closeSync(file); } catch { /* closed */ } }
|
|
844
887
|
}
|
|
845
888
|
|
|
846
889
|
/** A live-runtime receipt file, when it is one a delivery may use: its schema, and a loopback HTTP address. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-teams",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.182",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A machine daemon serving this machine's doors, and a local or remote Teams server for shared session discovery, OpenTelemetry ingestion and access to enrolled machines.",
|
|
6
6
|
"license": "MIT",
|