@volter/supercode-teams 0.3.200 → 0.3.202
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/bin/teams-cli.mjs +12 -3
- package/live-sessions.mjs +3 -0
- package/machine.mjs +6 -3
- package/mail/send.mjs +5 -2
- package/package.json +3 -3
package/bin/teams-cli.mjs
CHANGED
|
@@ -108,8 +108,11 @@ const home = flags.home ? String(flags.home) : migrationRead
|
|
|
108
108
|
? process.env.SUPERCODE_TEAMS_HOME ?? join(supercodeHome(), 'teams') : teamsHome();
|
|
109
109
|
const p = paths(home);
|
|
110
110
|
const out = (value) => process.stdout.write((typeof value === 'string' ? value : JSON.stringify(value, null, 2)) + '\n');
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
// A refusal ends the command through main()'s promise, never by process.exit() at the point of refusal: by then a
|
|
112
|
+
// machine verb has usually opened a machine (a fetch, a socket), and on Windows exiting while that handle is still
|
|
113
|
+
// closing aborts Node inside libuv (`!(handle->flags & UV_HANDLE_CLOSING)`, src\win\async.c) and loses the exit code.
|
|
114
|
+
const die = (message, code = 2) => { throw Object.assign(new Error(message), { exitCode: code }); };
|
|
115
|
+
if (!workspaceVerb) for (const flag of Object.keys(flags)) if (!KNOWN_FLAGS.has(flag)) { process.stderr.write(`supercode teams: unknown flag: --${flag}\n`); process.exit(2); }
|
|
113
116
|
|
|
114
117
|
/** Named keys (tmux's names) as the bytes a terminal sends for them: `panes.input` delivers
|
|
115
118
|
* them (`keys`) as a keystream, which every terminal host turns back into keys. */
|
|
@@ -611,4 +614,10 @@ async function main() {
|
|
|
611
614
|
}
|
|
612
615
|
}
|
|
613
616
|
|
|
614
|
-
if (!workspaceVerb) main().catch((error) =>
|
|
617
|
+
if (!workspaceVerb) main().catch(async (error) => {
|
|
618
|
+
process.stderr.write(`supercode teams: ${error.message ?? String(error)}\n`);
|
|
619
|
+
process.exitCode = error.exitCode ?? (error.code === 'machine_offline' ? 5 : 1);
|
|
620
|
+
// Windows: let the loop drain so closing handles finish, then force the exit if something still holds it open.
|
|
621
|
+
if (process.platform === 'win32') { setTimeout(() => process.exit(process.exitCode), 2_000).unref(); return; }
|
|
622
|
+
await new Promise((resolve) => process.stdout.write('', resolve)); process.exit();
|
|
623
|
+
});
|
package/live-sessions.mjs
CHANGED
|
@@ -769,6 +769,9 @@ export class LiveSessions extends EventEmitter {
|
|
|
769
769
|
}
|
|
770
770
|
const left = () => Math.max(250, deadline - Date.now());
|
|
771
771
|
for (const { pid: at, command } of await ancestryOf(Number(pid), left())) {
|
|
772
|
+
// a process with no entry has its registry file read now: the folder's watch can miss a new file's event, and a
|
|
773
|
+
// session that started after this daemon was then no one for its first sends (R71)
|
|
774
|
+
if (!this.claude.has(at)) for (const dir of this.claudeDirs.keys()) { this.#readClaude(dir, `${at}.json`); if (this.claude.has(at)) break; }
|
|
772
775
|
const claude = this.claude.get(at);
|
|
773
776
|
if (claude && alive(claude.pid)) return this.#address('claude-code', claude.sessionId);
|
|
774
777
|
const hosted = await this.#runtimeOfPid(at, deadline);
|
package/machine.mjs
CHANGED
|
@@ -74,6 +74,9 @@ function inProcessPair() {
|
|
|
74
74
|
import { boardLabel, boardName } from '@volter/teams/board-name';
|
|
75
75
|
import { fileURLToPath } from 'node:url';
|
|
76
76
|
import { boardGuardArguments } from '@volter/teams/board-process-guard';
|
|
77
|
+
// The harnesses whose board launch carries the native process policy here: Claude's exec-form hook runs on every
|
|
78
|
+
// platform; Codex's shell-form hook is POSIX-quoted, so Windows does not offer it.
|
|
79
|
+
const BOARD_GUARDED_HARNESSES = process.platform === 'win32' ? ['claude-code'] : ['claude-code', 'codex'];
|
|
77
80
|
import { homedir, hostname, userInfo, tmpdir } from 'node:os';
|
|
78
81
|
import { mkdtempSync } from 'node:fs';
|
|
79
82
|
import { serveHttpListener } from './http-listener.mjs';
|
|
@@ -1069,7 +1072,7 @@ export class TeamsMachine extends MachineCore {
|
|
|
1069
1072
|
return { panes, activities, sessions_unread };
|
|
1070
1073
|
},
|
|
1071
1074
|
// what a board launch needs of this machine: its board process guard and its keyed launch door (this daemon has it)
|
|
1072
|
-
capabilities: async () => ({ guard:
|
|
1075
|
+
capabilities: async () => ({ guard: BOARD_GUARDED_HARNESSES, launches: true }),
|
|
1073
1076
|
// the mail headers, from the header index (no process)
|
|
1074
1077
|
mail: async (mailboxes) => {
|
|
1075
1078
|
const answer = this.mail.headers({ recent: RECENT_PER_MAILBOX, mailboxes });
|
|
@@ -2399,7 +2402,7 @@ export class TeamsMachine extends MachineCore {
|
|
|
2399
2402
|
// Native session hooks carry the launcher's identity across Codex's shared
|
|
2400
2403
|
// app-server process boundary. Process ancestry alone cannot recover it.
|
|
2401
2404
|
#nativeQuestionCommand(params, pane, by, nativeInput) {
|
|
2402
|
-
if (params.env?.SUPERCODE_BOARD_PROCESS_GUARD === '1' &&
|
|
2405
|
+
if (params.env?.SUPERCODE_BOARD_PROCESS_GUARD === '1' && !BOARD_GUARDED_HARNESSES.includes(params.kind)) throw new Error(`board process guard is unavailable for ${params.kind} on this machine; no unguarded session was launched`);
|
|
2403
2406
|
let args = boardGuardArguments(params.kind,params.arguments ?? [],{cwd:params.cwd,enabled:params.env?.SUPERCODE_BOARD_PROCESS_GUARD === '1'});
|
|
2404
2407
|
const name = String(params.program ?? '').split(/[\\/]/).pop();
|
|
2405
2408
|
// Windows has no Unix endpoint for a pane's own native server, so a fresh Codex there starts with no session to
|
|
@@ -3369,7 +3372,7 @@ export class TeamsMachine extends MachineCore {
|
|
|
3369
3372
|
node: { pid: process.pid, version: MACHINE_VERSION, protocol: NODE_PROTOCOL, floor: this.floor },
|
|
3370
3373
|
egress: this.env.SUPERCODE_TEAMS_EGRESS ?? 'unrestricted',
|
|
3371
3374
|
execution: executionCapabilities(this.env),
|
|
3372
|
-
boardProcessPolicy:
|
|
3375
|
+
boardProcessPolicy: { version: 1, harnesses: BOARD_GUARDED_HARNESSES, boundary: 'native-PreToolUse' },
|
|
3373
3376
|
runSpec: this.env.SUPERCODE_TEAMS_RUN_SPEC_SHA256 ?? null,
|
|
3374
3377
|
restart: Boolean(this.restartHandler),
|
|
3375
3378
|
at: Date.now(),
|
package/mail/send.mjs
CHANGED
|
@@ -690,7 +690,10 @@ export class MailSend {
|
|
|
690
690
|
else text = `sent to ${reached.length ? reached.join(', ') : 'no one woken yet'}.${copied.length ? ` CC (filed, not woken): ${copied.join(', ')}.` : ''}${heldText} Message id ${shortId(sent.id)}.${thread} Don't poll; carry on.`;
|
|
691
691
|
if (failed.length) text += ` Not delivered to: ${failed.join('; ')}.`;
|
|
692
692
|
const code = reached.length || copied.length || held.length || waiting ? 0 : EXIT_FAILED;
|
|
693
|
-
|
|
693
|
+
// Filing, CC and a held wake are durable mail, but only native reaches confirm handoff. Keep this fact beside
|
|
694
|
+
// the wording so callers never have to infer it from the translated display text or a changed sentence.
|
|
695
|
+
const handoffConfirmed = reached.length > 0 && failed.length === 0 && held.length === 0;
|
|
696
|
+
return { ...outcome(code, text, code === 0 ? { delivered: true, handoff_confirmed: handoffConfirmed, message_ids: [sent.id], receipt: 'agent-mailbox', ...(waiting ? { waiting } : {}), ...(held.length && !reached.length && !copied.length ? { unanswered: true } : {}) } : null), waiting };
|
|
694
697
|
}
|
|
695
698
|
|
|
696
699
|
/** `sent` filed unread in `address`'s mailbox on this machine's mail, without waking it (mail_agent.rs `file_unread`):
|
|
@@ -829,7 +832,7 @@ export class MailSend {
|
|
|
829
832
|
try { plan = await this.agents.plan(sent, receiver, channel); } catch (error) { return refused('delivery_failed', error.message); }
|
|
830
833
|
if (plan) {
|
|
831
834
|
const done = await this.#deliverPlanned({ address: sender, name: sent.from_name }, sent, plan, { queue: false, notify_when_idle: params.notify_when_idle === true });
|
|
832
|
-
return { delivered_to_bus: done.code === 0, message_id: sent.id, reply_to: sender, thread: plan.thread?.id ?? null, target: { harness, session_id: sessionId }, delivery: { door: 'agent', how: done.text } };
|
|
835
|
+
return { delivered_to_bus: done.code === 0, message_id: sent.id, reply_to: sender, thread: plan.thread?.id ?? null, target: { harness, session_id: sessionId }, delivery: { door: 'agent', how: done.text, handoff_confirmed: done.receipt?.handoff_confirmed === true } };
|
|
833
836
|
}
|
|
834
837
|
if (!door || door.kind === 'other_machine') return refused('not_live', `no running \`${harness}\` session \`${sessionId}\` is reachable; its transcript is persisted only`);
|
|
835
838
|
let how;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-teams",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.202",
|
|
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",
|
|
@@ -84,10 +84,10 @@
|
|
|
84
84
|
"@volter/supercode-frontend": "^0.2.2",
|
|
85
85
|
"@volter/supercode-harness-sdk": "^0.3.58",
|
|
86
86
|
"@volter/supercode-health": "^0.1.0",
|
|
87
|
-
"@volter/supercode-interchange": "0.1.
|
|
87
|
+
"@volter/supercode-interchange": "0.1.11",
|
|
88
88
|
"@volter/supercode-terminal": "^0.2.54",
|
|
89
89
|
"@volter/supercode-trace": "^0.1.0",
|
|
90
|
-
"@volter/teams": "0.2.
|
|
90
|
+
"@volter/teams": "0.2.47",
|
|
91
91
|
"@xterm/addon-serialize": "^0.14.0",
|
|
92
92
|
"@xterm/headless": "^6.0.0",
|
|
93
93
|
"protobufjs": "8.8.0",
|