@ctliz/agent-intercom-codex 0.11.0-connect.2 → 0.12.0-connect.2

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/dist/coi.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- process.stderr.write("[agent-intercom-build] package=@ctliz/agent-intercom-codex version=0.11.0-connect.2 target=coi sourceSha256=3f81ffd19c83ad2f4db13939ce8dba9eae17d8c4b0c419e405941e0352f9c102\n");
2
+ process.stderr.write("[agent-intercom-build] package=@ctliz/agent-intercom-codex version=0.12.0-connect.2 target=coi sourceSha256=618efd98d8b0f52e066dbc535b3d8f61e7221d9f6c40cb2a3a1da690cbcae849\n");
3
3
 
4
4
  // codex/coi.ts
5
5
  import { once as once2 } from "node:events";
@@ -104,7 +104,8 @@ var CodexAppServerClient = class extends EventEmitter {
104
104
  const started = spawnSync(this.options.startDaemonCommand, this.options.startDaemonArgs, {
105
105
  encoding: "utf8",
106
106
  env: this.options.env,
107
- stdio: ["ignore", "pipe", "pipe"]
107
+ stdio: ["ignore", "pipe", "pipe"],
108
+ shell: false
108
109
  });
109
110
  if (started.status !== 0) {
110
111
  throw new Error(`Failed to start Codex app-server daemon: ${started.stderr || started.stdout || `exit ${started.status}`}`);
@@ -117,7 +118,8 @@ var CodexAppServerClient = class extends EventEmitter {
117
118
  }
118
119
  const proc = spawn(this.options.command, this.options.args, {
119
120
  stdio: ["pipe", "pipe", "pipe"],
120
- env: this.options.env
121
+ env: this.options.env,
122
+ shell: false
121
123
  });
122
124
  this.proc = proc;
123
125
  this.rl = readline.createInterface({ input: proc.stdout, crlfDelay: Infinity });
@@ -2444,7 +2446,8 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, env = process.env)
2444
2446
  stdio: "ignore",
2445
2447
  cwd: extensionDir,
2446
2448
  env: { ...env, PI_CODING_AGENT_DIR: getAgentDirPath(env), NODE_NO_WARNINGS: "1" },
2447
- windowsHide: true
2449
+ windowsHide: true,
2450
+ shell: false
2448
2451
  };
2449
2452
  }
2450
2453
  function toError2(error) {
@@ -2823,6 +2826,7 @@ import {
2823
2826
  parseWorkerIdentityV2 as parseWorkerIdentityV22,
2824
2827
  workerIdentityFromEnvironment
2825
2828
  } from "@ctliz/agent-intercom-core/boss";
2829
+ import { readTeamManifestAsync, TeamManifestError } from "@ctliz/agent-intercom-core/team-manifest";
2826
2830
  var LEGACY_LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "running", "idle", "needs_attention", "stopping"]);
2827
2831
  var CANONICAL_LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "registering", "ready", "working", "waiting", "paused", "stalled", "blocked", "unreachable"]);
2828
2832
  var stringValue = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
@@ -2830,12 +2834,25 @@ var connectedTo = (sessions, target) => {
2830
2834
  const normalized = target.toLowerCase();
2831
2835
  return sessions.some((session) => session.id === target || session.name?.toLowerCase() === normalized);
2832
2836
  };
2837
+ function hasBossEnvironmentKeys(env) {
2838
+ const bossKeys = [
2839
+ "AGENT_INTERCOM_BOSS_RUN_ID",
2840
+ "AGENT_INTERCOM_PARTICIPANT_ID",
2841
+ "AGENT_INTERCOM_BINDING_EPOCH",
2842
+ "AGENT_INTERCOM_WORKER_INCARNATION_ID",
2843
+ "AGENT_INTERCOM_WORKER_GENERATION"
2844
+ ];
2845
+ return bossKeys.some((key) => env[key] !== void 0);
2846
+ }
2833
2847
  function bossIdentityFromEnvironment(env) {
2834
- const bossKeys = ["AGENT_INTERCOM_BOSS_RUN_ID", "AGENT_INTERCOM_PARTICIPANT_ID", "AGENT_INTERCOM_BINDING_EPOCH"];
2835
- if (!bossKeys.some((key) => env[key] !== void 0)) return void 0;
2836
- const identity = workerIdentityFromEnvironment(env);
2837
- if (!("bossRunId" in identity)) throw new Error("Incomplete Boss worker identity cannot discover a team");
2838
- return identity;
2848
+ if (!hasBossEnvironmentKeys(env)) return void 0;
2849
+ try {
2850
+ const identity = workerIdentityFromEnvironment(env);
2851
+ if (!("bossRunId" in identity)) return void 0;
2852
+ return identity;
2853
+ } catch {
2854
+ return void 0;
2855
+ }
2839
2856
  }
2840
2857
  function canonicalWorker(value) {
2841
2858
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("worker must be an object");
@@ -2889,25 +2906,138 @@ async function readWorkers(agentDir) {
2889
2906
  return { version: 1, workers: [] };
2890
2907
  }
2891
2908
  }
2909
+ async function resolveNonAuthoritativeTeam(input, env) {
2910
+ if (env.AGENT_INTERCOM_TEAM_MANIFEST !== void 0) {
2911
+ const rawPath = env.AGENT_INTERCOM_TEAM_MANIFEST.trim();
2912
+ if (!rawPath) {
2913
+ throw new TeamManifestError("ERR_TEAM_MANIFEST_INVALID");
2914
+ }
2915
+ const manifest = await readTeamManifestAsync(rawPath);
2916
+ if (!manifest) {
2917
+ throw new TeamManifestError("ERR_TEAM_MANIFEST_INVALID");
2918
+ }
2919
+ const selfMember = manifest.members.find((m) => m.sessionId === input.selfId);
2920
+ if (!selfMember) {
2921
+ throw new TeamManifestError("ERR_TEAM_MANIFEST_INVALID");
2922
+ }
2923
+ const isManager = input.selfId === manifest.leadId;
2924
+ const managerTarget2 = manifest.leadId;
2925
+ const managerConnected = connectedTo(input.sessions, managerTarget2);
2926
+ const coworkers = manifest.members.filter((m) => m.sessionId !== input.selfId).filter((m) => isManager || m.sessionId !== managerTarget2).map((m) => {
2927
+ const live = input.sessions.find((s) => s.id === m.sessionId || s.name === m.sessionId);
2928
+ return {
2929
+ id: m.sessionId,
2930
+ target: m.sessionId,
2931
+ role: m.role,
2932
+ connected: Boolean(live)
2933
+ };
2934
+ });
2935
+ return {
2936
+ teamId: manifest.runId,
2937
+ self: { id: input.selfId, isManager },
2938
+ manager: { target: managerTarget2, connected: managerConnected },
2939
+ coworkers,
2940
+ source: "manifest"
2941
+ };
2942
+ }
2943
+ if (stringValue(env.AGENT_INTERCOM_SCOPE_ID)) {
2944
+ const managerTarget2 = stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID);
2945
+ const role = stringValue(env.AGENT_INTERCOM_ROLE)?.toLowerCase();
2946
+ const isManager = role === "manager" || managerTarget2 !== void 0 && managerTarget2 === input.selfId || managerTarget2 === void 0 && role !== "worker";
2947
+ const effectiveManagerTarget = isManager ? input.selfId : managerTarget2;
2948
+ const coworkers = input.sessions.filter((session) => session.id !== input.selfId).filter((session) => session.model !== "human").filter((session) => session.id !== effectiveManagerTarget).map((session) => ({
2949
+ id: session.id,
2950
+ target: session.id,
2951
+ ...session.model ? { harness: session.model } : {},
2952
+ connected: true
2953
+ }));
2954
+ const manager = effectiveManagerTarget ? {
2955
+ target: effectiveManagerTarget,
2956
+ connected: isManager ? true : connectedTo(input.sessions, effectiveManagerTarget)
2957
+ } : void 0;
2958
+ return {
2959
+ teamId: effectiveManagerTarget ?? input.selfId,
2960
+ self: { id: input.selfId, isManager },
2961
+ ...manager ? { manager } : {},
2962
+ coworkers,
2963
+ source: "live"
2964
+ };
2965
+ }
2966
+ const managerTarget = stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID);
2967
+ return {
2968
+ teamId: managerTarget ?? input.selfId,
2969
+ self: { id: input.selfId, isManager: !managerTarget },
2970
+ manager: managerTarget ? { target: managerTarget, connected: connectedTo(input.sessions, managerTarget) } : { target: input.selfId, connected: true },
2971
+ coworkers: [],
2972
+ source: "standalone"
2973
+ };
2974
+ }
2892
2975
  async function resolveIntercomTeam(input) {
2893
2976
  const env = input.env ?? process.env;
2894
2977
  const snapshot = await readWorkers(input.agentDir ?? getAgentDirPath());
2895
2978
  const workers = snapshot.workers;
2896
2979
  const workerId = stringValue(env.AGENT_INTERCOM_WORKER_ID);
2980
+ const selfSession = input.sessions.find((session) => session.id === input.selfId);
2981
+ const isBossContext = selfSession?.boss !== void 0 || hasBossEnvironmentKeys(env);
2897
2982
  const bossIdentity = bossIdentityFromEnvironment(env);
2898
2983
  const runId = stringValue(env.AGENT_INTERCOM_RUN_ID);
2899
- const currentMatches = workerId ? workers.filter((worker) => stringValue(worker.id) === workerId && (bossIdentity === void 0 ? !runId || stringValue(worker.runId) === runId : snapshot.version === 2 && worker.canonicalIdentity?.workerId === bossIdentity.workerId && worker.canonicalIdentity.workerIncarnationId === bossIdentity.workerIncarnationId && worker.canonicalIdentity.workerGeneration === bossIdentity.workerGeneration && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId && worker.canonicalIdentity.participantId === bossIdentity.participantId && worker.canonicalIdentity.bindingEpoch === bossIdentity.bindingEpoch)) : [];
2984
+ if (isBossContext && bossIdentity === void 0) {
2985
+ return {
2986
+ self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: false },
2987
+ coworkers: [],
2988
+ source: "boss"
2989
+ };
2990
+ }
2991
+ const currentMatches = workerId ? workers.filter((worker) => stringValue(worker.id) === workerId && (bossIdentity === void 0 ? snapshot.version === 1 && (!runId || stringValue(worker.runId) === runId) : snapshot.version === 2 && worker.canonicalIdentity?.workerId === bossIdentity.workerId && worker.canonicalIdentity.workerIncarnationId === bossIdentity.workerIncarnationId && worker.canonicalIdentity.workerGeneration === bossIdentity.workerGeneration && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId && worker.canonicalIdentity.participantId === bossIdentity.participantId && worker.canonicalIdentity.bindingEpoch === bossIdentity.bindingEpoch)) : [];
2900
2992
  const current = bossIdentity === void 0 ? currentMatches[0] : currentMatches.length === 1 ? currentMatches[0] : void 0;
2901
2993
  const currentTarget = stringValue(current?.intercomTarget);
2902
2994
  const exactCurrentProjection = current !== void 0 && currentTarget === input.selfId && workers.filter((worker) => stringValue(worker.id) === workerId).length === 1 && workers.filter((worker) => stringValue(worker.intercomTarget) === currentTarget).length === 1 && exactBossRosterSession(input.sessions, current) !== void 0;
2903
2995
  if (bossIdentity !== void 0 && !exactCurrentProjection) {
2904
- return { self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: false }, coworkers: [] };
2996
+ return { self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: false }, coworkers: [], source: "boss" };
2997
+ }
2998
+ if (bossIdentity === void 0 && !current) {
2999
+ if (workerId === void 0 && snapshot.version === 1) {
3000
+ const ownedCoworkers = workers.filter((worker) => worker.owned === true).filter((worker) => {
3001
+ const mgr = stringValue(worker.managerSessionId);
3002
+ return mgr !== void 0 && mgr === input.selfId;
3003
+ }).filter((worker) => LEGACY_LIVE_STATES.has(stringValue(worker.state) ?? "")).filter((worker) => stringValue(worker.id) !== input.selfId).map((worker) => {
3004
+ const id = stringValue(worker.id);
3005
+ if (!id) return void 0;
3006
+ const target = stringValue(worker.intercomTarget) ?? id;
3007
+ return {
3008
+ id,
3009
+ target,
3010
+ ...stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {},
3011
+ ...stringValue(worker.role) ? { role: stringValue(worker.role) } : {},
3012
+ ...stringValue(worker.state) ? { state: stringValue(worker.state) } : {},
3013
+ connected: connectedTo(input.sessions, target)
3014
+ };
3015
+ }).filter((member) => Boolean(member));
3016
+ if (ownedCoworkers.length > 0) {
3017
+ return {
3018
+ teamId: input.selfId,
3019
+ self: { id: input.selfId, isManager: true },
3020
+ manager: { target: input.selfId, connected: true },
3021
+ coworkers: ownedCoworkers,
3022
+ source: "orchestrator"
3023
+ };
3024
+ }
3025
+ }
3026
+ return resolveNonAuthoritativeTeam(input, env);
2905
3027
  }
2906
- const managerTarget = stringValue(current?.managerSessionId) ?? (bossIdentity === void 0 ? stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID) : void 0);
2907
- const teamId = managerTarget ?? input.selfId;
2908
3028
  const currentRole = stringValue(current?.role);
3029
+ const selfIsManager = bossIdentity !== void 0 ? currentRole === "manager" : void 0;
3030
+ const managerTarget = bossIdentity !== void 0 ? selfIsManager ? input.selfId : stringValue(current?.managerSessionId) : stringValue(current?.managerSessionId) ?? stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID);
3031
+ if (bossIdentity === void 0 && !managerTarget) {
3032
+ return {
3033
+ self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: false },
3034
+ coworkers: [],
3035
+ source: "orchestrator"
3036
+ };
3037
+ }
3038
+ const teamId = bossIdentity !== void 0 ? "bossRunId" in bossIdentity ? bossIdentity.bossRunId : managerTarget ?? input.selfId : managerTarget ?? input.selfId;
2909
3039
  const canDiscoverOwnedRoster = bossIdentity === void 0 || currentRole === "manager" || currentRole === "controller";
2910
- const coworkers = (canDiscoverOwnedRoster ? workers : []).filter((worker) => worker.owned === true).filter((worker) => bossIdentity === void 0 || snapshot.version === 2 && worker.canonicalIdentity !== void 0 && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId).filter((worker) => stringValue(worker.managerSessionId) === teamId).filter((worker) => stringValue(worker.intercomTarget) !== managerTarget).filter((worker) => (snapshot.version === 2 ? CANONICAL_LIVE_STATES : LEGACY_LIVE_STATES).has(stringValue(worker.state) ?? "")).filter((worker) => stringValue(worker.id) !== workerId).map((worker) => {
3040
+ const coworkers = (canDiscoverOwnedRoster ? workers : []).filter((worker) => worker.owned === true).filter((worker) => bossIdentity === void 0 || snapshot.version === 2 && worker.canonicalIdentity !== void 0 && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId).filter((worker) => stringValue(worker.managerSessionId) === (bossIdentity !== void 0 ? managerTarget : teamId)).filter((worker) => stringValue(worker.intercomTarget) !== managerTarget).filter((worker) => (snapshot.version === 2 ? CANONICAL_LIVE_STATES : LEGACY_LIVE_STATES).has(stringValue(worker.state) ?? "")).filter((worker) => stringValue(worker.id) !== workerId && stringValue(worker.id) !== input.selfId).map((worker) => {
2911
3041
  const id = stringValue(worker.id);
2912
3042
  if (!id) return void 0;
2913
3043
  const target = stringValue(worker.intercomTarget) ?? id;
@@ -2924,11 +3054,13 @@ async function resolveIntercomTeam(input) {
2924
3054
  }).filter((member) => Boolean(member));
2925
3055
  const managerWorker = managerTarget === void 0 ? void 0 : workers.find((worker) => stringValue(worker.intercomTarget) === managerTarget && (bossIdentity === void 0 || snapshot.version === 2 && stringValue(worker.role) === "manager" && worker.canonicalIdentity !== void 0 && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId));
2926
3056
  const managerConnected = managerTarget === void 0 ? true : bossIdentity === void 0 ? connectedTo(input.sessions, managerTarget) : managerWorker !== void 0 && exactBossRosterSession(input.sessions, managerWorker) !== void 0;
3057
+ const isManager = bossIdentity !== void 0 ? selfIsManager : false;
2927
3058
  return {
2928
3059
  teamId,
2929
- self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: bossIdentity === void 0 && !managerTarget },
2930
- ...managerTarget ? { manager: { target: managerTarget, connected: managerConnected } } : bossIdentity === void 0 ? { manager: { target: input.selfId, connected: true } } : {},
2931
- coworkers
3060
+ self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager },
3061
+ ...managerTarget ? { manager: { target: managerTarget, connected: isManager ? true : managerConnected } } : {},
3062
+ coworkers,
3063
+ source: bossIdentity !== void 0 ? "boss" : "orchestrator"
2932
3064
  };
2933
3065
  }
2934
3066
  function formatIntercomTeam(team) {
@@ -3802,7 +3934,7 @@ function runDetachedClipboardCommand(command, args, text, env, timeoutMs) {
3802
3934
  };
3803
3935
  const timeout = setTimeout(() => finish({ ok: false, error: `${command} did not start within ${timeoutMs}ms` }), timeoutMs);
3804
3936
  try {
3805
- const proc = spawn3(command, args, { env, stdio: ["pipe", "ignore", "ignore"] });
3937
+ const proc = spawn3(command, args, { env, stdio: ["pipe", "ignore", "ignore"], shell: false });
3806
3938
  proc.once("error", (error) => finish({ ok: false, error: error.message }));
3807
3939
  proc.once("spawn", () => {
3808
3940
  proc.stdin.on("error", () => {
@@ -3833,7 +3965,7 @@ function runClipboardCommand(command, args, text, env, timeoutMs) {
3833
3965
  }, timeoutMs);
3834
3966
  let proc;
3835
3967
  try {
3836
- proc = spawn3(command, args, { env, stdio: ["pipe", "ignore", "pipe"] });
3968
+ proc = spawn3(command, args, { env, stdio: ["pipe", "ignore", "pipe"], shell: false });
3837
3969
  proc.stderr?.setEncoding("utf8");
3838
3970
  proc.stderr?.on("data", (chunk) => {
3839
3971
  if (stderr.length < 4096) stderr += chunk;
@@ -4066,7 +4198,8 @@ function gitString(cwd, args) {
4066
4198
  const result = spawnSync2("git", args, {
4067
4199
  cwd,
4068
4200
  encoding: "utf8",
4069
- stdio: ["ignore", "pipe", "ignore"]
4201
+ stdio: ["ignore", "pipe", "ignore"],
4202
+ shell: false
4070
4203
  });
4071
4204
  if (result.status !== 0) return null;
4072
4205
  const trimmed = result.stdout.trim();
@@ -4397,7 +4530,7 @@ function terminalNotification(message) {
4397
4530
  async function runInteractiveTui(command, args, refreshArgs, cwd, onAltI, onAltM, installRefresh, protectedBossClient, launchEnv = process.env) {
4398
4531
  assertHardenedBossProviderAuthority(protectedBossClient);
4399
4532
  const runInherited = async () => {
4400
- const tui2 = spawn4(command, args, { cwd, env: launchEnv, stdio: "inherit" });
4533
+ const tui2 = spawn4(command, args, { cwd, env: launchEnv, stdio: "inherit", shell: false });
4401
4534
  const [code, signal] = await once2(tui2, "exit");
4402
4535
  if (typeof code === "number") return code;
4403
4536
  return signal === "SIGINT" ? 130 : 1;
@@ -4517,7 +4650,8 @@ async function runCoi(options, env = process.env) {
4517
4650
  const help = spawn4(options.codexCommand, options.codexArgs, {
4518
4651
  cwd: options.cwd,
4519
4652
  env,
4520
- stdio: "inherit"
4653
+ stdio: "inherit",
4654
+ shell: false
4521
4655
  });
4522
4656
  const [code, signal] = await once2(help, "exit");
4523
4657
  if (typeof code === "number") return code;
@@ -4549,7 +4683,8 @@ async function runCoi(options, env = process.env) {
4549
4683
  const appServer = spawn4(options.codexCommand, buildCodexAppServerArgs(options.codexArgs, socketPath, env), {
4550
4684
  cwd: options.cwd,
4551
4685
  env,
4552
- stdio: ["ignore", "ignore", "pipe"]
4686
+ stdio: ["ignore", "ignore", "pipe"],
4687
+ shell: false
4553
4688
  });
4554
4689
  appServer.stderr?.on("data", (chunk) => {
4555
4690
  if (env.CODEX_INTERCOM_DEBUG) process.stderr.write(String(chunk));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctliz/agent-intercom-codex",
3
- "version": "0.11.0-connect.2",
3
+ "version": "0.12.0-connect.2",
4
4
  "description": "Codex MCP plugin for direct local messaging with Pi and Codex coding-agent sessions.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "type": "module",
@@ -63,13 +63,13 @@
63
63
  "pi"
64
64
  ],
65
65
  "peerDependencies": {
66
- "@ctliz/agent-intercom-core": "0.1.0"
66
+ "@ctliz/agent-intercom-core": "0.2.0"
67
67
  },
68
68
  "optionalDependencies": {
69
69
  "node-pty": "^1.1.0"
70
70
  },
71
71
  "devDependencies": {
72
- "@ctliz/agent-intercom-core": "git+https://github.com/ctliz/agent-intercom-core.git#37e074970e2a9de32a16fc325607c3b476b0bd45",
72
+ "@ctliz/agent-intercom-core": "0.2.0",
73
73
  "@types/node": "^24.0.0",
74
74
  "esbuild": "^0.28.1",
75
75
  "tsx": "^4.20.0",