@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 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
- const die = (message, code = 2) => { process.stderr.write(`supercode teams: ${message}\n`); process.exit(code); };
112
- if (!workspaceVerb) for (const flag of Object.keys(flags)) if (!KNOWN_FLAGS.has(flag)) die(`unknown flag: --${flag}`);
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) => die(error.message ?? String(error), error.code === 'machine_offline' ? 5 : 1));
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: process.platform === 'win32' ? [] : ['claude-code', 'codex'], launches: true }),
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' && process.platform === 'win32') throw new Error('board process guard is unavailable on Windows; no unguarded session was launched');
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: process.platform === 'win32' ? null : { version: 1, harnesses: ['claude-code','codex'], boundary: 'native-PreToolUse' },
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
- return { ...outcome(code, text, code === 0 ? { delivered: true, message_ids: [sent.id], receipt: 'agent-mailbox', ...(waiting ? { waiting } : {}), ...(held.length && !reached.length && !copied.length ? { unanswered: true } : {}) } : null), waiting };
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.200",
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.10",
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.46",
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",