cross-agent-teams-mcp 0.5.14 → 0.5.16

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.
@@ -72,6 +72,7 @@ function findClaudeUiPid(startPpid = process.ppid, reader = readPsRow) {
72
72
  }
73
73
 
74
74
  // plugins/cross-agent-teams-channel/src/daemon-client.ts
75
+ var DEFAULT_BACKOFF_SCHEDULE_MS = [1e3, 1e4, 6e4, 6e5];
75
76
  async function parseToolResult(resp) {
76
77
  const r = resp;
77
78
  const text = r.content?.[0]?.text;
@@ -82,6 +83,23 @@ async function parseToolResult(resp) {
82
83
  return {};
83
84
  }
84
85
  }
86
+ function resolveBackoffSchedule(config) {
87
+ if (config.backoffScheduleMs && config.backoffScheduleMs.length > 0) {
88
+ return config.backoffScheduleMs.map((ms) => Math.max(1, ms));
89
+ }
90
+ if (config.backoffInitialMs !== void 0 || config.backoffMaxMs !== void 0) {
91
+ const initial = config.backoffInitialMs ?? DEFAULT_BACKOFF_SCHEDULE_MS[0];
92
+ const max = config.backoffMaxMs ?? DEFAULT_BACKOFF_SCHEDULE_MS[DEFAULT_BACKOFF_SCHEDULE_MS.length - 1];
93
+ const schedule = [];
94
+ let next = Math.max(1, initial);
95
+ while (schedule.length < DEFAULT_BACKOFF_SCHEDULE_MS.length) {
96
+ schedule.push(Math.min(next, max));
97
+ next *= 2;
98
+ }
99
+ return schedule;
100
+ }
101
+ return DEFAULT_BACKOFF_SCHEDULE_MS;
102
+ }
85
103
  async function runRegistrationSequence(config) {
86
104
  const order = [];
87
105
  const requestInit = config.token ? { headers: { Authorization: `Bearer ${config.token}` } } : void 0;
@@ -97,56 +115,76 @@ async function runRegistrationSequence(config) {
97
115
  };
98
116
  }
99
117
  await client.connect(transport);
100
- const registerArgs = {
101
- agent_type: "custom",
102
- agent_type_name: "cross-agent-teams-channel",
103
- model: "proxy",
104
- role: "__channel_proxy__",
105
- name: `channel-proxy-${process.pid}`,
106
- team: "default",
107
- claude_ui_pid: findClaudeUiPid(),
108
- delivery: {
109
- kind: "claude-channel",
110
- channel_session_id: config.channel_session_id
111
- }
112
- };
113
- if (config.device !== void 0) {
114
- registerArgs.device = config.device;
115
- }
116
- const registerResp = await client.callTool({
117
- name: "register_agent",
118
- arguments: registerArgs
119
- });
120
- order.push("register_agent");
121
- const regResult = await parseToolResult(registerResp);
122
- if (!("agent_id" in regResult)) {
123
- throw new Error(`register_agent failed: ${JSON.stringify(regResult)}`);
124
- }
125
- const subResp = await client.callTool({
126
- name: "subscribe_channel_wake",
127
- arguments: { channel_session_id: config.channel_session_id }
128
- });
129
- order.push("subscribe_channel_wake");
130
- const subResult = await parseToolResult(subResp);
131
- if (!("ok" in subResult) || subResult.ok !== true) {
132
- throw new Error(`subscribe_channel_wake failed: ${JSON.stringify(subResult)}`);
133
- }
134
- return {
135
- order,
136
- lastSubscribeResult: subResult,
137
- client,
138
- transport,
139
- close: async () => {
140
- try {
141
- await client.close();
142
- } catch {
118
+ try {
119
+ const registerArgs = {
120
+ agent_type: "custom",
121
+ agent_type_name: "cross-agent-teams-channel",
122
+ model: "proxy",
123
+ role: "__channel_proxy__",
124
+ name: `channel-proxy-${process.pid}`,
125
+ team: "default",
126
+ claude_ui_pid: findClaudeUiPid(),
127
+ delivery: {
128
+ kind: "claude-channel",
129
+ channel_session_id: config.channel_session_id
143
130
  }
144
- try {
145
- await transport.close();
146
- } catch {
131
+ };
132
+ if (config.device !== void 0) {
133
+ registerArgs.device = config.device;
134
+ }
135
+ const registerResp = await client.callTool({
136
+ name: "register_agent",
137
+ arguments: registerArgs
138
+ });
139
+ order.push("register_agent");
140
+ const regResult = await parseToolResult(registerResp);
141
+ if (!("agent_id" in regResult)) {
142
+ throw new Error(`register_agent failed: ${JSON.stringify(regResult)}`);
143
+ }
144
+ const subResp = await client.callTool({
145
+ name: "subscribe_channel_wake",
146
+ arguments: { channel_session_id: config.channel_session_id }
147
+ });
148
+ order.push("subscribe_channel_wake");
149
+ const subResult = await parseToolResult(subResp);
150
+ if (!("ok" in subResult) || subResult.ok !== true) {
151
+ throw new Error(`subscribe_channel_wake failed: ${JSON.stringify(subResult)}`);
152
+ }
153
+ return {
154
+ order,
155
+ lastSubscribeResult: subResult,
156
+ client,
157
+ transport,
158
+ close: async () => {
159
+ try {
160
+ await transport.terminateSession();
161
+ } catch {
162
+ }
163
+ try {
164
+ await client.close();
165
+ } catch {
166
+ }
167
+ try {
168
+ await transport.close();
169
+ } catch {
170
+ }
147
171
  }
172
+ };
173
+ } catch (err) {
174
+ try {
175
+ await transport.terminateSession();
176
+ } catch {
148
177
  }
149
- };
178
+ try {
179
+ await client.close();
180
+ } catch {
181
+ }
182
+ try {
183
+ await transport.close();
184
+ } catch {
185
+ }
186
+ throw err;
187
+ }
150
188
  }
151
189
  async function waitForDisconnect(seq, opts = {}) {
152
190
  const interval = opts.healthCheckIntervalMs ?? 3e4;
@@ -186,10 +224,14 @@ async function waitForDisconnect(seq, opts = {}) {
186
224
  function runReconnectingProxy(config) {
187
225
  let stopped = false;
188
226
  let currentSeq = null;
227
+ const backoffScheduleMs = resolveBackoffSchedule(config);
228
+ let backoffIndex = 0;
189
229
  async function loop() {
190
230
  while (!stopped) {
231
+ let failed = false;
191
232
  try {
192
233
  const seq = await runRegistrationSequence(config);
234
+ backoffIndex = 0;
193
235
  currentSeq = seq;
194
236
  if (config.onSequenceComplete) config.onSequenceComplete([...seq.order]);
195
237
  await waitForDisconnect(seq, {
@@ -203,9 +245,11 @@ function runReconnectingProxy(config) {
203
245
  }
204
246
  currentSeq = null;
205
247
  } catch {
248
+ failed = true;
206
249
  }
207
250
  if (stopped) break;
208
- const wait = config.backoffInitialMs ?? 500;
251
+ const wait = backoffScheduleMs[Math.min(backoffIndex, backoffScheduleMs.length - 1)];
252
+ if (failed) backoffIndex += 1;
209
253
  await new Promise((r) => setTimeout(r, wait));
210
254
  }
211
255
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../plugins/cross-agent-teams-channel/src/cli.ts","../plugins/cross-agent-teams-channel/src/proxy.ts","../plugins/cross-agent-teams-channel/src/daemon-client.ts","../plugins/cross-agent-teams-channel/src/find-claude-pid.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { randomUUID } from 'node:crypto'\nimport { realpathSync } from 'node:fs'\nimport { hostname } from 'node:os'\nimport { fileURLToPath } from 'node:url'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { createProxyServer, relayChannelWake } from './proxy.js'\nimport { runReconnectingProxy } from './daemon-client.js'\n\ninterface CliArgs {\n daemonUrl: string\n token?: string\n // Omitted when the user did not pass --device. The daemon then auto-fills\n // its own local label on loopback registrations, which keeps zero-config\n // proxies working against a daemon whose operator chose a custom --device.\n device?: string\n}\n\nexport class CliArgError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'CliArgError'\n }\n}\n\nexport function buildStartupHint(\n csid: string,\n device?: string\n): { content: string; meta: { source: string; kind: string } } {\n // Local default path (no --device): emit the original same-host hint with NO\n // device-related noise. Pure-local users never need to think about device.\n // Cross-host path (--device passed): surface the device value in both the\n // user-facing ask and the register_agent call so the agent and the human\n // both know it is required (daemon returns device_required_from_remote\n // otherwise).\n const isCrossHost = device !== undefined\n const deviceClause = isCrossHost ? `, device: \"${device}\"` : ''\n const deviceRegisterFragment = isCrossHost\n ? ` (this proxy is running with --device \"${device}\", so include device: \"${device}\" verbatim — the daemon enforces per-origin device validation, and on a remote daemon omitting it returns device_required_from_remote)`\n : ''\n const ask = isCrossHost\n ? `'To join cross-agent-teams (xats) and message other agents, reply with: Register to xats — name: your-agent-name, team: your-team-name (optional; defaults to the current working directory basename), device: ${device} (cross-host setup — keep this value verbatim).'`\n : `'To join cross-agent-teams (xats) and message other agents, reply with: Register to xats — name: your-agent-name, team: your-team-name (optional; defaults to the current working directory basename).'`\n const content = [\n `cross-agent-teams-mcp: your channel_session_id is ${csid}.`,\n `Do NOT register automatically. First ask the user (in English) to register this session so it can talk to other agents — use exactly this wording: ${ask}`,\n `Once the user provides a name (and optionally a team), call register_agent({agent_type: \"claude-code\", name: \"<name from user>\", team: \"<team from user, omit if not provided>\"${deviceClause}, ui_pid: $PPID, project_dir: \"<current working directory>\"})${deviceRegisterFragment}. Do NOT pass channel_session_id here; the daemon auto-binds via ui_pid.`,\n `bind_channel({channel_session_id: \"${csid}\"}) is the low-level rebind tool for an already-registered Claude host that needs to switch to a fresh csid; it is NOT the primary registration path.`,\n `Do not use curl or another external HTTP client for Claude registration here — that would create a different MCP session, and follow-up tools in Claude Code could still see unknown_agent.`\n ].join(' ')\n return {\n content,\n meta: { source: 'cross_agent_teams_mcp', kind: 'startup_bind_hint' }\n }\n}\n\nexport function parseCliArgs(argv: readonly string[], env: NodeJS.ProcessEnv = process.env): CliArgs {\n let daemonUrl: string | undefined\n let token: string | undefined\n let explicitDevice: string | undefined\n\n for (let i = 0; i < argv.length; i++) {\n const flag = argv[i]\n const next = argv[i + 1]\n switch (flag) {\n case '--daemon-url':\n daemonUrl = next; i++; break\n case '--token':\n token = next; i++; break\n case '--device':\n explicitDevice = next; i++; break\n default:\n // Ignore unknown flags for forward-compat (including legacy\n // --agent-team / --agent-name, which are no longer honored).\n break\n }\n }\n\n if (!daemonUrl || daemonUrl.length === 0) {\n daemonUrl = env.CROSS_AGENT_TEAMS_MCP_DAEMON_URL\n }\n if (!token || token.length === 0) {\n token = env.CROSS_AGENT_TEAMS_MCP_TOKEN\n }\n\n if (!daemonUrl || daemonUrl.length === 0) {\n throw new CliArgError(\n 'missing --daemon-url (or CROSS_AGENT_TEAMS_MCP_DAEMON_URL env var)'\n )\n }\n // Only validate / pass the device when the user explicitly provided one.\n // Leaving it undefined lets daemon-client omit the field on register_agent\n // so the daemon's loopback auto-fill resolves it to the daemon's localDevice.\n const device =\n explicitDevice !== undefined ? resolveDeviceLabel(explicitDevice) : undefined\n return { daemonUrl, token, device }\n}\n\nexport async function main(\n argv: readonly string[] = process.argv.slice(2),\n env: NodeJS.ProcessEnv = process.env\n): Promise<void> {\n let args: CliArgs\n try {\n args = parseCliArgs(argv, env)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n process.stderr.write(`cross-agent-teams-proxy: ${msg}\\n`)\n process.exit(2)\n }\n\n // Fresh csid per startup — no persistence. Multi-instance safe.\n const csid = randomUUID()\n\n const hostServer = createProxyServer()\n const stdioTransport = new StdioServerTransport()\n\n let registrationEverSucceeded = false\n const controller = runReconnectingProxy({\n daemonUrl: args.daemonUrl,\n token: args.token,\n device: args.device,\n channel_session_id: csid,\n notificationHandler: (params) => {\n relayChannelWake(hostServer, params as { content: string; meta: Record<string, string> })\n },\n onSequenceComplete: () => {\n registrationEverSucceeded = true\n // Announce csid to Claude via host-facing channel notification so Claude\n // can call bind_channel({channel_session_id}) to bind its own agent row.\n const hint = buildStartupHint(csid, args.device)\n relayChannelWake(hostServer, hint)\n }\n })\n\n let stopped = false\n const shutdown = async (): Promise<void> => {\n if (stopped) return\n stopped = true\n try { await controller.stop() } catch { /* best-effort */ }\n try { await hostServer.close() } catch { /* best-effort */ }\n if (!registrationEverSucceeded) {\n process.stderr.write(`cross-agent-teams-proxy: daemon unreachable at ${args.daemonUrl}\\n`)\n process.exit(1)\n }\n process.exit(0)\n }\n\n stdioTransport.onclose = () => { void shutdown() }\n\n await hostServer.connect(stdioTransport)\n\n process.on('SIGTERM', () => { void shutdown() })\n process.on('SIGINT', () => { void shutdown() })\n}\n\n// Entry-point check. The naive `import.meta.url === \\`file://${process.argv[1]}\\``\n// breaks when launched via an npm `.bin` symlink (npx, `npm install -g`):\n// process.argv[1] is the symlink path, while import.meta.url is already\n// resolved. Compare realpath-resolved file paths instead.\nfunction isEntry(): boolean {\n try {\n const metaPath = fileURLToPath(import.meta.url)\n const argvPath = realpathSync(process.argv[1])\n return metaPath === argvPath\n } catch {\n return false\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-misused-promises\nif (isEntry()) {\n void main()\n}\n\nfunction resolveDeviceLabel(explicit?: string): string {\n const raw = explicit ?? hostname()\n if (raw.includes(':')) {\n throw new CliArgError('invalid_device_label')\n }\n const normalized = raw\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '-')\n const label = normalized.length > 0 ? normalized : 'local'\n if (label.length > 64) {\n throw new CliArgError('invalid_device_label')\n }\n return label\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nexport function createProxyServer(): McpServer {\n return new McpServer(\n { name: 'cross-agent-teams-channel', version: '0.1.0' },\n { capabilities: { experimental: { 'claude/channel': {} } } }\n )\n}\n\nexport interface ChannelWakeParams {\n content: string\n meta: Record<string, string>\n}\n\nexport function relayChannelWake(server: McpServer, params: ChannelWakeParams): void {\n try {\n const notif = {\n method: 'notifications/claude/channel',\n params: params as unknown as Record<string, unknown>\n }\n const p = (server.server.notification as (n: typeof notif) => Promise<void>)(notif)\n if (p && typeof p.catch === 'function') {\n p.catch(() => { /* host closed — drop silently */ })\n }\n } catch {\n // host transport closed or not yet connected — drop silently\n }\n}\n","import { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'\nimport { findClaudeUiPid } from './find-claude-pid.js'\n\nexport interface RegistrationConfig {\n daemonUrl: string\n token?: string\n device?: string\n channel_session_id: string\n backoffInitialMs?: number\n backoffMaxMs?: number\n notificationHandler?: (payload: unknown) => void\n}\n\nexport interface ReconnectingProxyConfig extends RegistrationConfig {\n onSequenceComplete?: (order: string[]) => void\n onDisconnect?: () => void\n healthCheckIntervalMs?: number\n}\n\nexport interface ReconnectingProxyController {\n stop(): Promise<void>\n}\n\nexport interface RegistrationSequenceResult {\n order: string[]\n lastSubscribeResult: unknown\n client: Client\n transport: StreamableHTTPClientTransport\n close: () => Promise<void>\n}\n\ntype ToolResult = Record<string, unknown>\n\nasync function parseToolResult(resp: unknown): Promise<ToolResult> {\n const r = resp as { content?: Array<{ text?: string }> }\n const text = r.content?.[0]?.text\n if (typeof text !== 'string') return {}\n try { return JSON.parse(text) as ToolResult } catch { return {} }\n}\n\nexport async function runRegistrationSequence(\n config: RegistrationConfig\n): Promise<RegistrationSequenceResult> {\n const order: string[] = []\n const requestInit = config.token\n ? { headers: { Authorization: `Bearer ${config.token}` } }\n : undefined\n const transport = new StreamableHTTPClientTransport(new URL(config.daemonUrl), {\n requestInit,\n })\n const client = new Client({ name: 'cross-agent-teams-proxy', version: '0.1.0' })\n\n if (config.notificationHandler) {\n client.fallbackNotificationHandler = async (n) => {\n if (n.method === 'notifications/channel_wake') {\n config.notificationHandler!(n.params)\n }\n }\n }\n\n await client.connect(transport)\n\n // 1. register_agent as proxy — identity keyed on pid, stable across reconnects\n // so the (device, team, name) ON CONFLICT upsert reuses the same row instead of spamming new rows.\n // Only send `device` when the caller explicitly set one; otherwise let the daemon auto-fill\n // its local label (loopback) or reject (remote, which requires explicit device).\n const registerArgs: Record<string, unknown> = {\n agent_type: 'custom',\n agent_type_name: 'cross-agent-teams-channel',\n model: 'proxy',\n role: '__channel_proxy__',\n name: `channel-proxy-${process.pid}`,\n team: 'default',\n claude_ui_pid: findClaudeUiPid(),\n delivery: {\n kind: 'claude-channel',\n channel_session_id: config.channel_session_id,\n },\n }\n if (config.device !== undefined) {\n registerArgs.device = config.device\n }\n const registerResp = await client.callTool({\n name: 'register_agent',\n arguments: registerArgs,\n })\n order.push('register_agent')\n const regResult = await parseToolResult(registerResp)\n if (!('agent_id' in regResult)) {\n throw new Error(`register_agent failed: ${JSON.stringify(regResult)}`)\n }\n\n // 2. subscribe_channel_wake — proxy's csid is fresh per startup\n const subResp = await client.callTool({\n name: 'subscribe_channel_wake',\n arguments: { channel_session_id: config.channel_session_id }\n })\n order.push('subscribe_channel_wake')\n const subResult = await parseToolResult(subResp)\n if (!('ok' in subResult) || subResult.ok !== true) {\n throw new Error(`subscribe_channel_wake failed: ${JSON.stringify(subResult)}`)\n }\n\n return {\n order,\n lastSubscribeResult: subResult,\n client,\n transport,\n close: async () => {\n try { await client.close() } catch { /* best-effort */ }\n try { await transport.close() } catch { /* best-effort */ }\n }\n }\n}\n\nexport interface WaitForDisconnectInput {\n client: Pick<Client, 'callTool'>\n transport: { onclose?: (() => void) | null | undefined }\n}\n\nexport interface WaitForDisconnectOptions {\n healthCheckIntervalMs?: number\n shouldStop?: () => boolean\n}\n\nexport async function waitForDisconnect(\n seq: WaitForDisconnectInput,\n opts: WaitForDisconnectOptions = {}\n): Promise<void> {\n const interval = opts.healthCheckIntervalMs ?? 30_000\n const shouldStop = opts.shouldStop ?? (() => false)\n let disconnected = false\n let wakeup: (() => void) | null = null\n const closeHandler = (): void => {\n disconnected = true\n wakeup?.()\n }\n const prevOnClose = seq.transport.onclose\n seq.transport.onclose = (): void => { prevOnClose?.(); closeHandler() }\n while (!disconnected && !shouldStop()) {\n await new Promise<void>((resolve) => {\n const timer = setTimeout(() => { wakeup = null; resolve() }, interval)\n wakeup = (): void => { clearTimeout(timer); wakeup = null; resolve() }\n })\n if (disconnected || shouldStop()) break\n try {\n await seq.client.callTool({ name: 'echo', arguments: { msg: 'hb' } })\n } catch {\n disconnected = true\n break\n }\n }\n}\n\nexport function runReconnectingProxy(config: ReconnectingProxyConfig): ReconnectingProxyController {\n let stopped = false\n let currentSeq: RegistrationSequenceResult | null = null\n\n async function loop(): Promise<void> {\n while (!stopped) {\n try {\n const seq = await runRegistrationSequence(config)\n currentSeq = seq\n if (config.onSequenceComplete) config.onSequenceComplete([...seq.order])\n\n await waitForDisconnect(seq, {\n healthCheckIntervalMs: config.healthCheckIntervalMs,\n shouldStop: () => stopped,\n })\n if (config.onDisconnect) config.onDisconnect()\n try { await seq.close() } catch { /* best-effort */ }\n currentSeq = null\n } catch {\n // register/subscribe failed — wait and retry.\n }\n if (stopped) break\n const wait = config.backoffInitialMs ?? 500\n await new Promise(r => setTimeout(r, wait))\n }\n }\n\n void loop()\n\n return {\n stop: async () => {\n stopped = true\n if (currentSeq) {\n try { await currentSeq.close() } catch { /* best-effort */ }\n }\n }\n }\n}\n","import { execFileSync } from 'node:child_process'\n\nconst MAX_HOPS = 8\n\ninterface PsRow {\n ppid: number\n cmd: string\n}\n\nexport function readPsRow(pid: number): PsRow | null {\n try {\n const out = execFileSync('ps', ['-o', 'ppid=,args=', '-p', String(pid)], {\n encoding: 'utf8',\n timeout: 1000,\n stdio: ['ignore', 'pipe', 'ignore']\n })\n const trimmed = out.trim()\n if (!trimmed) return null\n const m = /^\\s*(\\d+)\\s+(.*)$/.exec(trimmed)\n if (!m) return null\n return { ppid: parseInt(m[1], 10), cmd: m[2] }\n } catch {\n return null\n }\n}\n\nexport function isClaudeCmd(cmd: string): boolean {\n const first = cmd.trim().split(/\\s+/)[0]\n if (!first) return false\n const base = first.replace(/^.*\\//, '')\n return base === 'claude'\n}\n\nexport function findClaudeUiPid(\n startPpid: number = process.ppid,\n reader: (pid: number) => PsRow | null = readPsRow\n): number {\n let pid = startPpid\n for (let i = 0; i < MAX_HOPS; i++) {\n const row = reader(pid)\n if (!row) break\n if (isClaudeCmd(row.cmd)) return pid\n if (row.ppid <= 1 || row.ppid === pid) break\n pid = row.ppid\n }\n return startPpid\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../plugins/cross-agent-teams-channel/src/cli.ts","../plugins/cross-agent-teams-channel/src/proxy.ts","../plugins/cross-agent-teams-channel/src/daemon-client.ts","../plugins/cross-agent-teams-channel/src/find-claude-pid.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { randomUUID } from 'node:crypto'\nimport { realpathSync } from 'node:fs'\nimport { hostname } from 'node:os'\nimport { fileURLToPath } from 'node:url'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { createProxyServer, relayChannelWake } from './proxy.js'\nimport { runReconnectingProxy } from './daemon-client.js'\n\ninterface CliArgs {\n daemonUrl: string\n token?: string\n // Omitted when the user did not pass --device. The daemon then auto-fills\n // its own local label on loopback registrations, which keeps zero-config\n // proxies working against a daemon whose operator chose a custom --device.\n device?: string\n}\n\nexport class CliArgError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'CliArgError'\n }\n}\n\nexport function buildStartupHint(\n csid: string,\n device?: string\n): { content: string; meta: { source: string; kind: string } } {\n // Local default path (no --device): emit the original same-host hint with NO\n // device-related noise. Pure-local users never need to think about device.\n // Cross-host path (--device passed): surface the device value in both the\n // user-facing ask and the register_agent call so the agent and the human\n // both know it is required (daemon returns device_required_from_remote\n // otherwise).\n const isCrossHost = device !== undefined\n const deviceClause = isCrossHost ? `, device: \"${device}\"` : ''\n const deviceRegisterFragment = isCrossHost\n ? ` (this proxy is running with --device \"${device}\", so include device: \"${device}\" verbatim — the daemon enforces per-origin device validation, and on a remote daemon omitting it returns device_required_from_remote)`\n : ''\n const ask = isCrossHost\n ? `'To join cross-agent-teams (xats) and message other agents, reply with: Register to xats — name: your-agent-name, team: your-team-name (optional; defaults to the current working directory basename), device: ${device} (cross-host setup — keep this value verbatim).'`\n : `'To join cross-agent-teams (xats) and message other agents, reply with: Register to xats — name: your-agent-name, team: your-team-name (optional; defaults to the current working directory basename).'`\n const content = [\n `cross-agent-teams-mcp: your channel_session_id is ${csid}.`,\n `Do NOT register automatically. First ask the user (in English) to register this session so it can talk to other agents — use exactly this wording: ${ask}`,\n `Once the user provides a name (and optionally a team), call register_agent({agent_type: \"claude-code\", name: \"<name from user>\", team: \"<team from user, omit if not provided>\"${deviceClause}, ui_pid: $PPID, project_dir: \"<current working directory>\"})${deviceRegisterFragment}. Do NOT pass channel_session_id here; the daemon auto-binds via ui_pid.`,\n `bind_channel({channel_session_id: \"${csid}\"}) is the low-level rebind tool for an already-registered Claude host that needs to switch to a fresh csid; it is NOT the primary registration path.`,\n `Do not use curl or another external HTTP client for Claude registration here — that would create a different MCP session, and follow-up tools in Claude Code could still see unknown_agent.`\n ].join(' ')\n return {\n content,\n meta: { source: 'cross_agent_teams_mcp', kind: 'startup_bind_hint' }\n }\n}\n\nexport function parseCliArgs(argv: readonly string[], env: NodeJS.ProcessEnv = process.env): CliArgs {\n let daemonUrl: string | undefined\n let token: string | undefined\n let explicitDevice: string | undefined\n\n for (let i = 0; i < argv.length; i++) {\n const flag = argv[i]\n const next = argv[i + 1]\n switch (flag) {\n case '--daemon-url':\n daemonUrl = next; i++; break\n case '--token':\n token = next; i++; break\n case '--device':\n explicitDevice = next; i++; break\n default:\n // Ignore unknown flags for forward-compat (including legacy\n // --agent-team / --agent-name, which are no longer honored).\n break\n }\n }\n\n if (!daemonUrl || daemonUrl.length === 0) {\n daemonUrl = env.CROSS_AGENT_TEAMS_MCP_DAEMON_URL\n }\n if (!token || token.length === 0) {\n token = env.CROSS_AGENT_TEAMS_MCP_TOKEN\n }\n\n if (!daemonUrl || daemonUrl.length === 0) {\n throw new CliArgError(\n 'missing --daemon-url (or CROSS_AGENT_TEAMS_MCP_DAEMON_URL env var)'\n )\n }\n // Only validate / pass the device when the user explicitly provided one.\n // Leaving it undefined lets daemon-client omit the field on register_agent\n // so the daemon's loopback auto-fill resolves it to the daemon's localDevice.\n const device =\n explicitDevice !== undefined ? resolveDeviceLabel(explicitDevice) : undefined\n return { daemonUrl, token, device }\n}\n\nexport async function main(\n argv: readonly string[] = process.argv.slice(2),\n env: NodeJS.ProcessEnv = process.env\n): Promise<void> {\n let args: CliArgs\n try {\n args = parseCliArgs(argv, env)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n process.stderr.write(`cross-agent-teams-proxy: ${msg}\\n`)\n process.exit(2)\n }\n\n // Fresh csid per startup — no persistence. Multi-instance safe.\n const csid = randomUUID()\n\n const hostServer = createProxyServer()\n const stdioTransport = new StdioServerTransport()\n\n let registrationEverSucceeded = false\n const controller = runReconnectingProxy({\n daemonUrl: args.daemonUrl,\n token: args.token,\n device: args.device,\n channel_session_id: csid,\n notificationHandler: (params) => {\n relayChannelWake(hostServer, params as { content: string; meta: Record<string, string> })\n },\n onSequenceComplete: () => {\n registrationEverSucceeded = true\n // Announce csid to Claude via host-facing channel notification so Claude\n // can call bind_channel({channel_session_id}) to bind its own agent row.\n const hint = buildStartupHint(csid, args.device)\n relayChannelWake(hostServer, hint)\n }\n })\n\n let stopped = false\n const shutdown = async (): Promise<void> => {\n if (stopped) return\n stopped = true\n try { await controller.stop() } catch { /* best-effort */ }\n try { await hostServer.close() } catch { /* best-effort */ }\n if (!registrationEverSucceeded) {\n process.stderr.write(`cross-agent-teams-proxy: daemon unreachable at ${args.daemonUrl}\\n`)\n process.exit(1)\n }\n process.exit(0)\n }\n\n stdioTransport.onclose = () => { void shutdown() }\n\n await hostServer.connect(stdioTransport)\n\n process.on('SIGTERM', () => { void shutdown() })\n process.on('SIGINT', () => { void shutdown() })\n}\n\n// Entry-point check. The naive `import.meta.url === \\`file://${process.argv[1]}\\``\n// breaks when launched via an npm `.bin` symlink (npx, `npm install -g`):\n// process.argv[1] is the symlink path, while import.meta.url is already\n// resolved. Compare realpath-resolved file paths instead.\nfunction isEntry(): boolean {\n try {\n const metaPath = fileURLToPath(import.meta.url)\n const argvPath = realpathSync(process.argv[1])\n return metaPath === argvPath\n } catch {\n return false\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-misused-promises\nif (isEntry()) {\n void main()\n}\n\nfunction resolveDeviceLabel(explicit?: string): string {\n const raw = explicit ?? hostname()\n if (raw.includes(':')) {\n throw new CliArgError('invalid_device_label')\n }\n const normalized = raw\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '-')\n const label = normalized.length > 0 ? normalized : 'local'\n if (label.length > 64) {\n throw new CliArgError('invalid_device_label')\n }\n return label\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nexport function createProxyServer(): McpServer {\n return new McpServer(\n { name: 'cross-agent-teams-channel', version: '0.1.0' },\n { capabilities: { experimental: { 'claude/channel': {} } } }\n )\n}\n\nexport interface ChannelWakeParams {\n content: string\n meta: Record<string, string>\n}\n\nexport function relayChannelWake(server: McpServer, params: ChannelWakeParams): void {\n try {\n const notif = {\n method: 'notifications/claude/channel',\n params: params as unknown as Record<string, unknown>\n }\n const p = (server.server.notification as (n: typeof notif) => Promise<void>)(notif)\n if (p && typeof p.catch === 'function') {\n p.catch(() => { /* host closed — drop silently */ })\n }\n } catch {\n // host transport closed or not yet connected — drop silently\n }\n}\n","import { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'\nimport { findClaudeUiPid } from './find-claude-pid.js'\n\nexport interface RegistrationConfig {\n daemonUrl: string\n token?: string\n device?: string\n channel_session_id: string\n backoffInitialMs?: number\n backoffMaxMs?: number\n backoffScheduleMs?: readonly number[]\n notificationHandler?: (payload: unknown) => void\n}\n\nexport interface ReconnectingProxyConfig extends RegistrationConfig {\n onSequenceComplete?: (order: string[]) => void\n onDisconnect?: () => void\n healthCheckIntervalMs?: number\n}\n\nexport interface ReconnectingProxyController {\n stop(): Promise<void>\n}\n\nexport interface RegistrationSequenceResult {\n order: string[]\n lastSubscribeResult: unknown\n client: Client\n transport: StreamableHTTPClientTransport\n close: () => Promise<void>\n}\n\ntype ToolResult = Record<string, unknown>\n\nconst DEFAULT_BACKOFF_SCHEDULE_MS = [1_000, 10_000, 60_000, 600_000] as const\n\nasync function parseToolResult(resp: unknown): Promise<ToolResult> {\n const r = resp as { content?: Array<{ text?: string }> }\n const text = r.content?.[0]?.text\n if (typeof text !== 'string') return {}\n try { return JSON.parse(text) as ToolResult } catch { return {} }\n}\n\nfunction resolveBackoffSchedule(config: ReconnectingProxyConfig): readonly number[] {\n if (config.backoffScheduleMs && config.backoffScheduleMs.length > 0) {\n return config.backoffScheduleMs.map(ms => Math.max(1, ms))\n }\n if (config.backoffInitialMs !== undefined || config.backoffMaxMs !== undefined) {\n const initial = config.backoffInitialMs ?? DEFAULT_BACKOFF_SCHEDULE_MS[0]\n const max = config.backoffMaxMs\n ?? DEFAULT_BACKOFF_SCHEDULE_MS[DEFAULT_BACKOFF_SCHEDULE_MS.length - 1]\n const schedule: number[] = []\n let next = Math.max(1, initial)\n while (schedule.length < DEFAULT_BACKOFF_SCHEDULE_MS.length) {\n schedule.push(Math.min(next, max))\n next *= 2\n }\n return schedule\n }\n return DEFAULT_BACKOFF_SCHEDULE_MS\n}\n\nexport async function runRegistrationSequence(\n config: RegistrationConfig\n): Promise<RegistrationSequenceResult> {\n const order: string[] = []\n const requestInit = config.token\n ? { headers: { Authorization: `Bearer ${config.token}` } }\n : undefined\n const transport = new StreamableHTTPClientTransport(new URL(config.daemonUrl), {\n requestInit,\n })\n const client = new Client({ name: 'cross-agent-teams-proxy', version: '0.1.0' })\n\n if (config.notificationHandler) {\n client.fallbackNotificationHandler = async (n) => {\n if (n.method === 'notifications/channel_wake') {\n config.notificationHandler!(n.params)\n }\n }\n }\n\n await client.connect(transport)\n\n try {\n // 1. register_agent as proxy — identity keyed on pid, stable across reconnects\n // so the (device, team, name) ON CONFLICT upsert reuses the same row instead of spamming new rows.\n // Only send `device` when the caller explicitly set one; otherwise let the daemon auto-fill\n // its local label (loopback) or reject (remote, which requires explicit device).\n const registerArgs: Record<string, unknown> = {\n agent_type: 'custom',\n agent_type_name: 'cross-agent-teams-channel',\n model: 'proxy',\n role: '__channel_proxy__',\n name: `channel-proxy-${process.pid}`,\n team: 'default',\n claude_ui_pid: findClaudeUiPid(),\n delivery: {\n kind: 'claude-channel',\n channel_session_id: config.channel_session_id,\n },\n }\n if (config.device !== undefined) {\n registerArgs.device = config.device\n }\n const registerResp = await client.callTool({\n name: 'register_agent',\n arguments: registerArgs,\n })\n order.push('register_agent')\n const regResult = await parseToolResult(registerResp)\n if (!('agent_id' in regResult)) {\n throw new Error(`register_agent failed: ${JSON.stringify(regResult)}`)\n }\n\n // 2. subscribe_channel_wake — proxy's csid is fresh per startup\n const subResp = await client.callTool({\n name: 'subscribe_channel_wake',\n arguments: { channel_session_id: config.channel_session_id }\n })\n order.push('subscribe_channel_wake')\n const subResult = await parseToolResult(subResp)\n if (!('ok' in subResult) || subResult.ok !== true) {\n throw new Error(`subscribe_channel_wake failed: ${JSON.stringify(subResult)}`)\n }\n\n return {\n order,\n lastSubscribeResult: subResult,\n client,\n transport,\n close: async () => {\n try { await transport.terminateSession() } catch { /* best-effort */ }\n try { await client.close() } catch { /* best-effort */ }\n try { await transport.close() } catch { /* best-effort */ }\n }\n }\n } catch (err) {\n try { await transport.terminateSession() } catch { /* best-effort */ }\n try { await client.close() } catch { /* best-effort */ }\n try { await transport.close() } catch { /* best-effort */ }\n throw err\n }\n}\n\nexport interface WaitForDisconnectInput {\n client: Pick<Client, 'callTool'>\n transport: { onclose?: (() => void) | null | undefined }\n}\n\nexport interface WaitForDisconnectOptions {\n healthCheckIntervalMs?: number\n shouldStop?: () => boolean\n}\n\nexport async function waitForDisconnect(\n seq: WaitForDisconnectInput,\n opts: WaitForDisconnectOptions = {}\n): Promise<void> {\n const interval = opts.healthCheckIntervalMs ?? 30_000\n const shouldStop = opts.shouldStop ?? (() => false)\n let disconnected = false\n let wakeup: (() => void) | null = null\n const closeHandler = (): void => {\n disconnected = true\n wakeup?.()\n }\n const prevOnClose = seq.transport.onclose\n seq.transport.onclose = (): void => { prevOnClose?.(); closeHandler() }\n while (!disconnected && !shouldStop()) {\n await new Promise<void>((resolve) => {\n const timer = setTimeout(() => { wakeup = null; resolve() }, interval)\n wakeup = (): void => { clearTimeout(timer); wakeup = null; resolve() }\n })\n if (disconnected || shouldStop()) break\n try {\n await seq.client.callTool({ name: 'echo', arguments: { msg: 'hb' } })\n } catch {\n disconnected = true\n break\n }\n }\n}\n\nexport function runReconnectingProxy(config: ReconnectingProxyConfig): ReconnectingProxyController {\n let stopped = false\n let currentSeq: RegistrationSequenceResult | null = null\n const backoffScheduleMs = resolveBackoffSchedule(config)\n let backoffIndex = 0\n\n async function loop(): Promise<void> {\n while (!stopped) {\n let failed = false\n try {\n const seq = await runRegistrationSequence(config)\n backoffIndex = 0\n currentSeq = seq\n if (config.onSequenceComplete) config.onSequenceComplete([...seq.order])\n\n await waitForDisconnect(seq, {\n healthCheckIntervalMs: config.healthCheckIntervalMs,\n shouldStop: () => stopped,\n })\n if (config.onDisconnect) config.onDisconnect()\n try { await seq.close() } catch { /* best-effort */ }\n currentSeq = null\n } catch {\n failed = true\n // register/subscribe failed — wait and retry.\n }\n if (stopped) break\n const wait = backoffScheduleMs[Math.min(backoffIndex, backoffScheduleMs.length - 1)]\n if (failed) backoffIndex += 1\n await new Promise(r => setTimeout(r, wait))\n }\n }\n\n void loop()\n\n return {\n stop: async () => {\n stopped = true\n if (currentSeq) {\n try { await currentSeq.close() } catch { /* best-effort */ }\n }\n }\n }\n}\n","import { execFileSync } from 'node:child_process'\n\nconst MAX_HOPS = 8\n\ninterface PsRow {\n ppid: number\n cmd: string\n}\n\nexport function readPsRow(pid: number): PsRow | null {\n try {\n const out = execFileSync('ps', ['-o', 'ppid=,args=', '-p', String(pid)], {\n encoding: 'utf8',\n timeout: 1000,\n stdio: ['ignore', 'pipe', 'ignore']\n })\n const trimmed = out.trim()\n if (!trimmed) return null\n const m = /^\\s*(\\d+)\\s+(.*)$/.exec(trimmed)\n if (!m) return null\n return { ppid: parseInt(m[1], 10), cmd: m[2] }\n } catch {\n return null\n }\n}\n\nexport function isClaudeCmd(cmd: string): boolean {\n const first = cmd.trim().split(/\\s+/)[0]\n if (!first) return false\n const base = first.replace(/^.*\\//, '')\n return base === 'claude'\n}\n\nexport function findClaudeUiPid(\n startPpid: number = process.ppid,\n reader: (pid: number) => PsRow | null = readPsRow\n): number {\n let pid = startPpid\n for (let i = 0; i < MAX_HOPS; i++) {\n const row = reader(pid)\n if (!row) break\n if (isClaudeCmd(row.cmd)) return pid\n if (row.ppid <= 1 || row.ppid === pid) break\n pid = row.ppid\n }\n return startPpid\n}\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -4109,7 +4109,7 @@ var DEFAULT_KEEP_ALIVE_TIMEOUT_MS = 12e4;
4109
4109
  var DEFAULT_ORPHAN_GC_INTERVAL_MS = 6e4;
4110
4110
  var DEFAULT_ORPHAN_GC_IDLE_MS = 3e5;
4111
4111
  var DEFAULT_ORPHAN_GC_MAX_AGE_MS = 3e5;
4112
- var DEFAULT_ORPHAN_GC_MAX_SESSIONS = 100;
4112
+ var DEFAULT_ORPHAN_GC_MAX_SESSIONS = 500;
4113
4113
  function parsePositiveInt(raw, fallback) {
4114
4114
  const n = Number(raw);
4115
4115
  return Number.isInteger(n) && n > 0 ? n : fallback;