@volter/supercode-teams 0.3.125 → 0.3.126

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.mjs CHANGED
@@ -55,7 +55,7 @@ import { APP_DOOR_PATH, appDoorFile, appDoorLink, normalOrigin, readAppDoor, rea
55
55
 
56
56
  const MACHINE_VERBS = new Set(['machine', 'slow', 'describe', 'records', 'panes', 'input', 'suspend', 'resume', 'files', 'ports', 'port', 'browser', 'stream', 'jobs', 'workspace', 'title', 'attention', 'watch', 'rpc', 'launches', 'log', 'mail', 'board', 'health', 'app-door']);
57
57
  // Every flag a machine verb reads; any other is refused rather than silently ignored.
58
- const KNOWN_FLAGS = new Set(['home', 'since', 'machine', 'context', 'floor', 'supercode', 'tmux-socket', 'verbose', 'lines', 'input', 'name', 'for', 'urgency', 'reason', 'interrupt', 'query', 'out', 'in', 'interval', 'no-enter', 'when-composer-empty', 'key', 'listen', 'list', 'from', 'to', 'path', 'to-path', 'repo', 'branch', 'solo', 'card', 'create', 'presence-only', 'processes', 'panes', 'clear-draft', 'message-ids', 'harness', 'observe', 'attach-context', 'json', 'all', 'origin', 'port', 'remove-origin', 'open']);
58
+ const KNOWN_FLAGS = new Set(['home', 'since', 'machine', 'context', 'floor', 'supercode', 'tmux-socket', 'verbose', 'lines', 'input', 'name', 'for', 'urgency', 'reason', 'interrupt', 'query', 'out', 'in', 'interval', 'no-enter', 'when-composer-empty', 'key', 'listen', 'list', 'from', 'to', 'path', 'to-path', 'repo', 'branch', 'solo', 'card', 'create', 'presence-only', 'processes', 'panes', 'clear-draft', 'message-ids', 'harness', 'observe', 'attach-context', 'json', 'all', 'origin', 'port', 'remove-origin', 'open', 'foreign']);
59
59
 
60
60
  const argv = process.argv.slice(2);
61
61
  const flags = {};
@@ -247,7 +247,13 @@ async function main() {
247
247
  flags.processes ? 'panes.processes' : flags['presence-only'] ? 'panes.present' : 'panes.list',
248
248
  flags['presence-only'] && typeof flags.panes === 'string' ? { panes: flags.panes.split(',').filter(Boolean) } : {})));
249
249
  if (sub === 'capture') return withNode(async (rpc) => out((await rpc.call('panes.capture', { pane: rest[0], lines: Number(flags.lines ?? 200) })).text));
250
- if (sub === 'close') return withNode(async (rpc) => out(await rpc.call('panes.close', { pane: rest[0] })));
250
+ // One pane, named: `--foreign` closes a pane another session started, and is said for that pane alone.
251
+ if (sub === 'close') {
252
+ // the flag parser takes `--foreign p_…` as the flag's value: that pane is the one named
253
+ const named = typeof flags.foreign === 'string' ? [flags.foreign, ...rest] : rest;
254
+ if (named.length !== 1) die('panes close <pane> [--foreign]: one pane per close');
255
+ return withNode(async (rpc) => out(await rpc.call('panes.close', { pane: named[0], foreign: flags.foreign !== undefined, ...(await acting()) })));
256
+ }
251
257
  // `window <pane> [machine]`: a terminal window on this machine's screen showing the pane, this machine's or, named,
252
258
  // another machine's (attached through Teams, in that machine's Teams context `--attach-context`); `--observe` shows it
253
259
  // read-only.
package/codex-startup.mjs CHANGED
@@ -42,7 +42,16 @@ export function codexStartupArguments(args = []) {
42
42
  }
43
43
  normalized.push(argument);
44
44
  }
45
- return ['-c', 'check_for_update_on_startup=false', ...normalized];
45
+ // Codex 0.160's `resume` does not take this override from before the subcommand: a resumed TUI with it there still
46
+ // stopped on "Update available" once another Codex had cached a newer version, and `codex resume <id> -c …` did not
47
+ // (yuerans, 2026-10-06). It goes after the subcommand and its session id (readers of the process take the word after
48
+ // `resume` as the id: codex_peer.rs `resumed_session_in`), in front when there is no subcommand. `fork` is placed the
49
+ // same way by extension; only `resume` was measured.
50
+ const subcommand = normalized.findIndex((argument, index) => ['resume', 'fork'].includes(argument)
51
+ && !['-c', '--config'].includes(normalized[index - 1]) && !normalized.slice(0, index).includes('--'));
52
+ const id = subcommand >= 0 && normalized[subcommand + 1] !== undefined && normalized[subcommand + 1] !== '--' && !normalized[subcommand + 1].startsWith('-');
53
+ const at = subcommand < 0 ? 0 : subcommand + (id ? 2 : 1);
54
+ return [...normalized.slice(0, at), '-c', 'check_for_update_on_startup=false', ...normalized.slice(at)];
46
55
  }
47
56
 
48
57
  export function codexUpdatePrompt(screen) {
package/machine.mjs CHANGED
@@ -2079,16 +2079,22 @@ export class TeamsMachine extends MachineCore {
2079
2079
  }
2080
2080
 
2081
2081
  #refuseForeign(pane, caller) {
2082
- if (typeof caller !== 'string' || !caller || /^sc:[^:]+:board:/.test(caller)) return;
2082
+ const starter = this.#foreignStarter(pane, caller);
2083
+ if (starter) throw Object.assign(new Error(`pane ${pane} ${starter}, not by ${caller}: a session acts only on the panes it started. Nothing was done; reach that session through its own doors (supercode message send)`), { code: 403 });
2084
+ }
2085
+
2086
+ /** Who started `pane`, said, when `caller` is a session that did not start it; null when the caller may act on it (a
2087
+ * person, a board, its starter). */
2088
+ #foreignStarter(pane, caller) {
2089
+ if (typeof caller !== 'string' || !caller || /^sc:[^:]+:board:/.test(caller)) return null;
2083
2090
  const launch = this.launches.all().find((entry) => entry.pane === pane);
2084
2091
  const by = launch?.by ?? null;
2085
- if (by === caller) return;
2092
+ if (by === caller) return null;
2086
2093
  // A pane that resumes a session is that session's as well: its first starter acts on it as before the resume
2087
2094
  // (t_09a795fd item 18). The starter is the oldest harness launch whose own transcript confirmed it started that
2088
2095
  // session, never a launch row's claim (any caller can launch a program with --session-id S).
2089
- if (launch?.resumes && this.#sessionOrigin(launch.resumes)?.by === caller) return;
2090
- const starter = by ? `was started by ${by}` : 'was not started by a session (a person or an earlier daemon started it)';
2091
- throw Object.assign(new Error(`pane ${pane} ${starter}, not by ${caller}: a session acts only on the panes it started. Nothing was done; reach that session through its own doors (supercode message send)`), { code: 403 });
2096
+ if (launch?.resumes && this.#sessionOrigin(launch.resumes)?.by === caller) return null;
2097
+ return by ? `was started by ${by}` : 'was not started by a session (a person or an earlier daemon started it)';
2092
2098
  }
2093
2099
 
2094
2100
  /** The launch that started native Claude session `session`: the oldest journal row of a Claude launch whose own
@@ -3307,7 +3313,7 @@ export class TeamsMachine extends MachineCore {
3307
3313
  }
3308
3314
  const contextKey = params.pane ?? `p_${randomBytes(9).toString('base64url')}`;
3309
3315
  // Who started the pane: the launching session's (or board's) address, null for a person. A session's later
3310
- // input on the pane is refused unless it is this one (#refuseForeign); close is not creator-restricted.
3316
+ // input on the pane is refused unless it is this one (#refuseForeign); its close needs `foreign` (panes.close).
3311
3317
  // Who to tell when the pane ends (`open --new … --notify-when-ended`): a session's mail address, and this
3312
3318
  // machine's name as the address a notice from its pane comes from.
3313
3319
  if (params.notify !== undefined && !(params.notify && typeof params.notify.to === 'string' && /^sc:[^:\s]+:[^:\s]+:\S+$/.test(params.notify.to) && typeof params.notify.machine === 'string' && /^[a-z0-9][a-z0-9-]*$/.test(params.notify.machine))) throw Object.assign(new Error('launch notify is { to: <sc: address>, machine: <this machine\'s mail name> }'), { code: 400 });
@@ -3459,14 +3465,28 @@ export class TeamsMachine extends MachineCore {
3459
3465
  case 'harness.v1.panes.close':
3460
3466
  case 'panes.close': {
3461
3467
  need('control', `pane:${params.pane}`);
3462
- for (const pending of this.pendingStartups.values()) if (pending.pane === params.pane) pending.abort.abort();
3468
+ // A session closes the panes it started and the one it runs in. Another's pane is closed only when the caller
3469
+ // says so (`foreign`), one pane at a time, by name: a listing piped into close is refused pane by pane rather
3470
+ // than ending every session on the machine (docs/adr/0004-pane-close-authority.md).
3471
+ const starting = [...this.pendingStartups.values()].filter((pending) => pending.pane === params.pane);
3472
+ // only a pane here (open, or still starting) is anyone's: one this machine lacks is answered as absent, before
3473
+ // the caller is asked for
3474
+ const here = starting.length > 0 || Boolean(await this.#paneByContext(params.pane));
3475
+ const closeBy = here ? await this.#callerOf(entry, params) : null;
3476
+ const starter = params.foreign === true || !here ? null : this.#foreignStarter(params.pane, closeBy);
3477
+ // the pane the caller runs in: a Claude row names it as its tmux locator (`p_…:@window.%pane`), the others as the pane
3478
+ if (starter && !this.live.sessions().some((live) => live.address === closeBy && String(live.pane ?? '').split(':')[0] === params.pane)) {
3479
+ act('close', `pane:${params.pane}`, 'refused', `foreign: ${starter}`);
3480
+ throw Object.assign(new Error(`pane ${params.pane} ${starter}, not by ${closeBy}. Nothing was closed. A session closes the panes it started; closing another's is deliberate: read what the pane holds, settle it with whoever started it, then close that one pane with --foreign`), { code: 403 });
3481
+ }
3482
+ for (const pending of starting) pending.abort.abort();
3463
3483
  const session = await this.#paneByContext(params.pane);
3464
3484
  if (!session) {
3465
3485
  act('close', `pane:${params.pane}`, 'ok', 'already absent');
3466
3486
  return { closed: params.pane, alreadyAbsent: true };
3467
3487
  }
3468
3488
  await this.host.closeSession(session.id);
3469
- act('close', `pane:${params.pane}`);
3489
+ act('close', `pane:${params.pane}`, 'ok', params.foreign === true ? `foreign, by ${closeBy ?? 'a person'}` : null);
3470
3490
  this.#panesChanged();
3471
3491
  return { closed: params.pane };
3472
3492
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-teams",
3
- "version": "0.3.125",
3
+ "version": "0.3.126",
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",