cross-agent-teams-mcp 0.6.0 → 0.6.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/README.md CHANGED
@@ -4,6 +4,16 @@
4
4
 
5
5
  A local MCP daemon that lets multiple AI coding agents (Claude Code, Codex, opencode) running on the same machine talk to each other. Agents register, send 1-to-1 messages, broadcast to a team or role, and wake each other up — all over a single daemon, no external services.
6
6
 
7
+ ## Why not just use Claude Code's agent teams?
8
+
9
+ Claude Code already ships its own agent teams feature. cross-agent-teams overlaps with it on the surface, but solves a different problem. Three concrete reasons to reach for this project:
10
+
11
+ **Cross-agent support.** Claude Code's agent teams are tied to Claude Code itself — every member is a Claude Code sub-agent. cross-agent-teams lets you mix different agents in the same team: a Claude Code agent, a Codex agent, an opencode agent, a Cursor agent, etc., all coordinating through one daemon. Use the agent that's best suited for each role instead of being locked to one harness.
12
+
13
+ **Better persistence and controllability.** In this design, each agent process is started and stopped manually. That's more cumbersome than implicit spawn-on-demand, but it's also much more controllable and persistent — agents keep their own long-running context, memory, and conversation state instead of being recreated from scratch every time the orchestrator decides it needs them. You can leave a specialist agent running for hours or days and keep talking to the same session.
14
+
15
+ **Cross-device / cross-user collaboration.** The daemon recently grew support for building teams across physical machines (see [section 4](#4-cross-host--cross-device-collaboration)). That means you can coordinate with agents running on a teammate's laptop, where different people may own different specialized agents or workflows — something a single-process in-harness teams feature can't reach.
16
+
7
17
  ## Quick start
8
18
 
9
19
  ### Claude Code
package/README.zh-CN.md CHANGED
@@ -4,6 +4,16 @@
4
4
 
5
5
  一个本地 MCP daemon, 让同一台机器上的多个 AI 编码 agent (Claude Code, Codex, opencode) 互相通信. agent 注册到 daemon, 互发 1-to-1 消息, 在 team 或 role 内广播, 互相唤醒 — 全部通过一个本地 daemon 完成, 不依赖任何外部服务.
6
6
 
7
+ ## 为什么不直接用 Claude Code 自带的 agent teams?
8
+
9
+ Claude Code 自己也有 agent teams 功能, cross-agent-teams 表面上和它有重叠, 但解决的是不同的问题. 三个具体的理由:
10
+
11
+ **跨 agent 支持.** Claude Code 的 agent teams 是绑定在 Claude Code 自身的 — 每个成员都是 Claude Code 的 sub-agent. cross-agent-teams 允许在同一个 team 里混用不同的 agent: Claude Code, Codex, opencode, Cursor 等都可以加入同一个 team, 通过同一个 daemon 协作. 按场景选最合适的 agent, 而不是被某一个 harness 锁死.
12
+
13
+ **更强的持久性与可控性.** 本项目的设计是每个 agent 进程都由你手动启动和停止. 这比"按需隐式拉起"麻烦, 但也更可控, 更持久 — agent 自己保留长期上下文, 记忆, 会话状态, 不会被编排器隐式重建. 一个专家 agent 可以挂着跑几小时甚至几天, 你一直跟同一个 session 对话.
14
+
15
+ **跨设备 / 跨用户协作.** daemon 最近新增了跨物理机组 team 的能力 (见 [第 4 节](#4-跨主机--跨设备协作)). 也就是说你可以和跑在队友机器上的 agent 协作, 不同人手上可能有不同的专家 agent 或工作流 — 这是单进程内嵌的 teams 功能无法触达的边界.
16
+
7
17
  ## 快速开始
8
18
 
9
19
  ### Claude Code
@@ -301,7 +301,7 @@ function buildStartupHint(csid, device) {
301
301
  `cross-agent-teams-mcp: your channel_session_id is ${csid}.`,
302
302
  `Do NOT register automatically. First ask the user (in English) to register this session so it can talk to other agents \u2014 use exactly this wording: ${ask}`,
303
303
  `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.`,
304
- `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.`,
304
+ `If you were already registered in this Claude process earlier (your $PPID is unchanged) and this is a reconnect after a context clear, resume, or channel re-attach, call reconnect({ui_pid: $PPID}) to recover your prior (team, name) and rebind to this new csid in one step \u2014 it returns need_register if you have never registered in this process. bind_channel({channel_session_id: "${csid}"}) only rebinds when your CURRENT MCP session is already bound to your agent; on a fresh or resumed MCP session it returns unknown_agent, so use reconnect instead. Neither is the primary first-time registration path.`,
305
305
  `Do not use curl or another external HTTP client for Claude registration here \u2014 that would create a different MCP session, and follow-up tools in Claude Code could still see unknown_agent.`
306
306
  ].join(" ");
307
307
  return {
@@ -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 AND the daemon is on loopback.\n // The daemon then auto-fills its own local label on loopback registrations,\n // which keeps zero-config proxies working against a daemon whose operator\n // chose a custom --device. For non-loopback daemons the proxy auto-derives\n // a label from os.hostname() (see deviceAutoDerivedNotice).\n device?: string\n // Set when --device was not supplied and we auto-derived one because the\n // daemon is non-loopback. Surfaced so main() can emit a one-line stderr\n // notice; daemon-side validation may still reject the derived label\n // (e.g. device_spoofing_local_label_from_remote).\n deviceAutoDerivedNotice?: string\n}\n\nexport interface ParseCliArgsDeps {\n hostname?: () => string\n}\n\nexport class CliArgError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'CliArgError'\n }\n}\n\nconst LOOPBACK_HOSTS = new Set(['localhost', '0.0.0.0', '::', '::1'])\n\nexport function isNonLoopbackDaemonUrl(daemonUrl: string): boolean {\n try {\n const parsed = new URL(daemonUrl)\n // WHATWG URL keeps IPv6 literals wrapped in brackets in `hostname` —\n // strip them so `::1` matches the loopback set.\n const host = parsed.hostname.toLowerCase().replace(/^\\[|\\]$/g, '')\n if (host === '') return false\n if (LOOPBACK_HOSTS.has(host)) return false\n if (host.startsWith('127.')) return false\n return true\n } catch {\n // Unparseable URL: assume remote so we still try to derive a device label;\n // daemon-client will surface the real connect error shortly.\n return true\n }\n}\n\nexport function deriveHostnameDeviceLabel(hostnameValue: string): string | null {\n const normalized = hostnameValue\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '-')\n .replace(/^-+|-+$/g, '')\n if (normalized.length === 0 || normalized.length > 64) return null\n return normalized\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(\n argv: readonly string[],\n env: NodeJS.ProcessEnv = process.env,\n deps: ParseCliArgsDeps = {}\n): 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\n // Explicit --device wins. Otherwise: loopback daemons let the daemon\n // auto-fill its own localDevice (zero-config); non-loopback daemons require\n // a device on register_agent — the daemon returns device_required_from_remote\n // when it is missing, which would put the proxy in a register/fail/respawn\n // loop. Auto-derive from os.hostname() with a stderr notice; fail-fast when\n // the hostname yields nothing usable.\n let device: string | undefined\n let deviceAutoDerivedNotice: string | undefined\n if (explicitDevice !== undefined) {\n device = resolveDeviceLabel(explicitDevice)\n } else if (isNonLoopbackDaemonUrl(daemonUrl)) {\n const hostnameFn = deps.hostname ?? hostname\n const derived = deriveHostnameDeviceLabel(hostnameFn())\n if (derived === null) {\n throw new CliArgError(\n `--device is required when --daemon-url is non-loopback (got ${daemonUrl}); ` +\n `os.hostname() did not yield a usable label`\n )\n }\n device = derived\n deviceAutoDerivedNotice =\n `--device not supplied; auto-derived \"${derived}\" from os.hostname() for remote daemon ${daemonUrl}. ` +\n `Pass --device <label> explicitly to silence this notice and pin the device label.`\n }\n return { daemonUrl, token, device, deviceAutoDerivedNotice }\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 if (args.deviceAutoDerivedNotice !== undefined) {\n process.stderr.write(`cross-agent-teams-proxy: ${args.deviceAutoDerivedNotice}\\n`)\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":[]}
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 AND the daemon is on loopback.\n // The daemon then auto-fills its own local label on loopback registrations,\n // which keeps zero-config proxies working against a daemon whose operator\n // chose a custom --device. For non-loopback daemons the proxy auto-derives\n // a label from os.hostname() (see deviceAutoDerivedNotice).\n device?: string\n // Set when --device was not supplied and we auto-derived one because the\n // daemon is non-loopback. Surfaced so main() can emit a one-line stderr\n // notice; daemon-side validation may still reject the derived label\n // (e.g. device_spoofing_local_label_from_remote).\n deviceAutoDerivedNotice?: string\n}\n\nexport interface ParseCliArgsDeps {\n hostname?: () => string\n}\n\nexport class CliArgError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'CliArgError'\n }\n}\n\nconst LOOPBACK_HOSTS = new Set(['localhost', '0.0.0.0', '::', '::1'])\n\nexport function isNonLoopbackDaemonUrl(daemonUrl: string): boolean {\n try {\n const parsed = new URL(daemonUrl)\n // WHATWG URL keeps IPv6 literals wrapped in brackets in `hostname` —\n // strip them so `::1` matches the loopback set.\n const host = parsed.hostname.toLowerCase().replace(/^\\[|\\]$/g, '')\n if (host === '') return false\n if (LOOPBACK_HOSTS.has(host)) return false\n if (host.startsWith('127.')) return false\n return true\n } catch {\n // Unparseable URL: assume remote so we still try to derive a device label;\n // daemon-client will surface the real connect error shortly.\n return true\n }\n}\n\nexport function deriveHostnameDeviceLabel(hostnameValue: string): string | null {\n const normalized = hostnameValue\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '-')\n .replace(/^-+|-+$/g, '')\n if (normalized.length === 0 || normalized.length > 64) return null\n return normalized\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 `If you were already registered in this Claude process earlier (your $PPID is unchanged) and this is a reconnect after a context clear, resume, or channel re-attach, call reconnect({ui_pid: $PPID}) to recover your prior (team, name) and rebind to this new csid in one step — it returns need_register if you have never registered in this process. bind_channel({channel_session_id: \"${csid}\"}) only rebinds when your CURRENT MCP session is already bound to your agent; on a fresh or resumed MCP session it returns unknown_agent, so use reconnect instead. Neither is the primary first-time 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(\n argv: readonly string[],\n env: NodeJS.ProcessEnv = process.env,\n deps: ParseCliArgsDeps = {}\n): 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\n // Explicit --device wins. Otherwise: loopback daemons let the daemon\n // auto-fill its own localDevice (zero-config); non-loopback daemons require\n // a device on register_agent — the daemon returns device_required_from_remote\n // when it is missing, which would put the proxy in a register/fail/respawn\n // loop. Auto-derive from os.hostname() with a stderr notice; fail-fast when\n // the hostname yields nothing usable.\n let device: string | undefined\n let deviceAutoDerivedNotice: string | undefined\n if (explicitDevice !== undefined) {\n device = resolveDeviceLabel(explicitDevice)\n } else if (isNonLoopbackDaemonUrl(daemonUrl)) {\n const hostnameFn = deps.hostname ?? hostname\n const derived = deriveHostnameDeviceLabel(hostnameFn())\n if (derived === null) {\n throw new CliArgError(\n `--device is required when --daemon-url is non-loopback (got ${daemonUrl}); ` +\n `os.hostname() did not yield a usable label`\n )\n }\n device = derived\n deviceAutoDerivedNotice =\n `--device not supplied; auto-derived \"${derived}\" from os.hostname() for remote daemon ${daemonUrl}. ` +\n `Pass --device <label> explicitly to silence this notice and pin the device label.`\n }\n return { daemonUrl, token, device, deviceAutoDerivedNotice }\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 if (args.deviceAutoDerivedNotice !== undefined) {\n process.stderr.write(`cross-agent-teams-proxy: ${args.deviceAutoDerivedNotice}\\n`)\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":[]}