codeam-cli 2.73.9 → 2.74.0

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.js +287 -23
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to `codeam-cli` are documented here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.73.10] — 2026-09-03
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Re-resolve the integrations manifest from the backend at session start (#697)
12
+
13
+ ## [2.73.9] — 2026-09-03
14
+
15
+ ### Tests
16
+
17
+ - **cli:** Move the MCP licence audit to apps/cli — it broke the shared publish (#695)
18
+
7
19
  ## [2.73.8] — 2026-09-03
8
20
 
9
21
  ### Fixed
package/dist/index.js CHANGED
@@ -8136,7 +8136,7 @@ function readAnonId() {
8136
8136
  }
8137
8137
  function superProperties() {
8138
8138
  return {
8139
- cliVersion: true ? "2.73.9" : "0.0.0-dev",
8139
+ cliVersion: true ? "2.74.0" : "0.0.0-dev",
8140
8140
  nodeVersion: process.version,
8141
8141
  platform: process.platform,
8142
8142
  arch: process.arch,
@@ -8378,7 +8378,7 @@ var http = __toESM(require("http"));
8378
8378
  // package.json
8379
8379
  var package_default = {
8380
8380
  name: "codeam-cli",
8381
- version: "2.73.9",
8381
+ version: "2.74.0",
8382
8382
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
8383
8383
  type: "commonjs",
8384
8384
  main: "dist/index.js",
@@ -9971,7 +9971,7 @@ var CommandRelayService = class _CommandRelayService {
9971
9971
  // fresh + clear the "CLI update available" banner after a self-update
9972
9972
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
9973
9973
  // pair/reconnect). Older backends ignore the extra field.
9974
- ..."2.73.9" ? { ideVersion: "2.73.9" } : {}
9974
+ ..."2.74.0" ? { ideVersion: "2.74.0" } : {}
9975
9975
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
9976
9976
  }
9977
9977
  /**
@@ -22037,11 +22037,35 @@ function readHouseProxyChildEnv() {
22037
22037
  return {};
22038
22038
  }
22039
22039
  }
22040
+ var HOUSE_MODEL_CONTEXT_TOKENS = "512000";
22040
22041
  function buildHouseProxyChildEnv(cfg) {
22041
22042
  const env = {
22042
22043
  ANTHROPIC_BASE_URL: cfg.baseUrl,
22043
22044
  ANTHROPIC_AUTH_TOKEN: cfg.token,
22044
- CLAUDE_CODE_AUTO_COMPACT_WINDOW: "512000",
22045
+ // ⚠️ MAX_CONTEXT_TOKENS, not (only) AUTO_COMPACT_WINDOW — and this is
22046
+ // HOUSE-AGENT ONLY: it lives in this builder precisely so a user's own
22047
+ // Claude Code (a real Anthropic model claude knows) is never touched.
22048
+ //
22049
+ // claude does not know `MiniMax-M3`, so it falls back to its
22050
+ // unknown-model default (200k). `CLAUDE_CODE_AUTO_COMPACT_WINDOW` is then
22051
+ // applied as `Math.min(assumedWindow, env)` — it can only SHRINK the
22052
+ // window, never grow it — so the 512000 we used to set here was inert:
22053
+ // claude kept believing 200k. Meanwhile a session with a dozen linked
22054
+ // integrations carries ~175k tokens of MCP tool schemas on EVERY turn
22055
+ // (measured: 475 tools ≈ 698 KB on a real box), so the context started
22056
+ // ~88% full and a 28-character prompt triggered a compaction, then the
22057
+ // next small tool result triggered another: "Autocompact is thrashing:
22058
+ // the context refilled to the limit within 3 turns", 13 compactions in
22059
+ // one session (rafaelph90.br@gmail.com, 2026-09-03).
22060
+ //
22061
+ // claude's own unknown-model notice names the fix: "set
22062
+ // CLAUDE_CODE_MAX_CONTEXT_TOKENS to its real window" — that variable is
22063
+ // what `wL()` returns for an unrecognised model. MiniMax-M3 is a 1M-context
22064
+ // model with a guaranteed minimum of 512K, so 512000 here is the
22065
+ // conservative truth, not a wish. AUTO_COMPACT_WINDOW stays as the
22066
+ // compaction threshold beneath it.
22067
+ CLAUDE_CODE_MAX_CONTEXT_TOKENS: HOUSE_MODEL_CONTEXT_TOKENS,
22068
+ CLAUDE_CODE_AUTO_COMPACT_WINDOW: HOUSE_MODEL_CONTEXT_TOKENS,
22045
22069
  API_TIMEOUT_MS: "3000000"
22046
22070
  };
22047
22071
  if (cfg.openRouter === true) {
@@ -22066,6 +22090,7 @@ var HOUSE_PROXY_ENV_KEYS = [
22066
22090
  "ANTHROPIC_DEFAULT_OPUS_MODEL",
22067
22091
  "ANTHROPIC_DEFAULT_HAIKU_MODEL",
22068
22092
  "CLAUDE_CODE_AUTO_COMPACT_WINDOW",
22093
+ "CLAUDE_CODE_MAX_CONTEXT_TOKENS",
22069
22094
  "API_TIMEOUT_MS",
22070
22095
  "CLAUDE_CONFIG_DIR"
22071
22096
  ];
@@ -22247,7 +22272,7 @@ async function autoUpgradeBeforeCriticalCommand() {
22247
22272
  if (process.env.NODE_ENV === "test") return;
22248
22273
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
22249
22274
  if (process.env.CI) return;
22250
- const current2 = true ? "2.73.9" : null;
22275
+ const current2 = true ? "2.74.0" : null;
22251
22276
  if (!current2) return;
22252
22277
  const cache = readCache();
22253
22278
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -22264,7 +22289,7 @@ function checkForUpdates() {
22264
22289
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
22265
22290
  if (process.env.CI) return;
22266
22291
  if (!process.stdout.isTTY) return;
22267
- const current2 = true ? "2.73.9" : null;
22292
+ const current2 = true ? "2.74.0" : null;
22268
22293
  if (!current2) return;
22269
22294
  const cache = readCache();
22270
22295
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -22287,7 +22312,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
22287
22312
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
22288
22313
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
22289
22314
  function currentCliVersion() {
22290
- return true ? "2.73.9" : null;
22315
+ return true ? "2.74.0" : null;
22291
22316
  }
22292
22317
  async function installedVersion(deps) {
22293
22318
  const ls = await deps.run(
@@ -23464,20 +23489,7 @@ var HostAgentSupervisor = class {
23464
23489
  if (payload.houseProxy) {
23465
23490
  const { baseUrl, token, agentKind, openRouter } = payload.houseProxy;
23466
23491
  childEnv = {
23467
- ANTHROPIC_BASE_URL: baseUrl,
23468
- ANTHROPIC_AUTH_TOKEN: token,
23469
- // House pins MiniMax; OpenRouter routes the real Claude model names the
23470
- // agent sends, so it sets NO model overrides — but it DOES need
23471
- // ANTHROPIC_API_KEY empty so a stale key can't override the Bearer auth
23472
- // token (OpenRouter's Claude Code setup requires ANTHROPIC_API_KEY="").
23473
- ...openRouter ? { ANTHROPIC_API_KEY: "" } : {
23474
- ANTHROPIC_MODEL: "MiniMax-M3",
23475
- ANTHROPIC_DEFAULT_SONNET_MODEL: "MiniMax-M3",
23476
- ANTHROPIC_DEFAULT_OPUS_MODEL: "MiniMax-M3",
23477
- ANTHROPIC_DEFAULT_HAIKU_MODEL: "MiniMax-M3"
23478
- },
23479
- CLAUDE_CODE_AUTO_COMPACT_WINDOW: "512000",
23480
- API_TIMEOUT_MS: "3000000",
23492
+ ...buildHouseProxyChildEnv({ baseUrl, token, openRouter: openRouter === true }),
23481
23493
  CODEAM_AUTO_TOKEN: payload.autoPairToken
23482
23494
  };
23483
23495
  const houseConfigDir = path52.join(
@@ -39580,6 +39592,172 @@ var packStatusH = async (ctx) => {
39580
39592
  await ctx.relay.sendResult(ctx.cmd.id, "completed", { state });
39581
39593
  };
39582
39594
 
39595
+ // src/integrations/mcp-router.ts
39596
+ var MCP_ROUTER_SERVER_NAME = "codeam";
39597
+ var CALL_TIMEOUT_MS = (() => {
39598
+ const raw = Number(process.env.CODEAM_MCP_TOOL_TIMEOUT_MS);
39599
+ return Number.isFinite(raw) && raw > 0 ? raw : 12e4;
39600
+ })();
39601
+ var CHILD_START_TIMEOUT_MS = 9e4;
39602
+ function qualifiedName(integration, tool) {
39603
+ return `${integration}/${tool}`;
39604
+ }
39605
+ function splitQualifiedName(q2) {
39606
+ const i = q2.indexOf("/");
39607
+ if (i <= 0 || i === q2.length - 1) return null;
39608
+ return { integration: q2.slice(0, i), tool: q2.slice(i + 1) };
39609
+ }
39610
+ function searchTools(tools, query, integration) {
39611
+ const words = query.toLowerCase().split(/\s+/).filter(Boolean);
39612
+ return tools.filter((t2) => !integration || t2.integration === integration).filter((t2) => {
39613
+ if (words.length === 0) return true;
39614
+ const hay = `${t2.integration} ${t2.name} ${t2.description}`.toLowerCase();
39615
+ return words.every((w3) => hay.includes(w3));
39616
+ }).sort((a, b) => {
39617
+ const an = words.some((w3) => a.name.toLowerCase().includes(w3)) ? 0 : 1;
39618
+ const bn = words.some((w3) => b.name.toLowerCase().includes(w3)) ? 0 : 1;
39619
+ return an - bn || a.integration.localeCompare(b.integration) || a.name.localeCompare(b.name);
39620
+ });
39621
+ }
39622
+ function routerToolDefinitions() {
39623
+ return [
39624
+ {
39625
+ name: "list_integrations",
39626
+ description: "List the integrations attached to this session (Jira, Linear, ClickUp, Postman, \u2026) with how many tools each exposes and whether it is available. Call this first when the user asks what you can do with an external service.",
39627
+ inputSchema: { type: "object", properties: {}, additionalProperties: false }
39628
+ },
39629
+ {
39630
+ name: "search_tools",
39631
+ description: 'Search the tools of the attached integrations by keywords (e.g. "create task", "list boards", "send email"). Returns each match as `<integration>/<tool>` with a one-line description. Use `describe_tool` for the full argument schema, then `call_tool` to run it.',
39632
+ inputSchema: {
39633
+ type: "object",
39634
+ properties: {
39635
+ query: { type: "string", description: "Keywords to match against tool names and descriptions." },
39636
+ integration: { type: "string", description: 'Optional: restrict to one integration id, e.g. "clickup".' },
39637
+ limit: { type: "integer", minimum: 1, maximum: 50, default: 20 }
39638
+ },
39639
+ required: ["query"],
39640
+ additionalProperties: false
39641
+ }
39642
+ },
39643
+ {
39644
+ name: "describe_tool",
39645
+ description: "Return the full JSON input schema of one tool, given its `<integration>/<tool>` name from `search_tools`.",
39646
+ inputSchema: {
39647
+ type: "object",
39648
+ properties: { tool: { type: "string", description: '`<integration>/<tool>`, e.g. "clickup/create_task".' } },
39649
+ required: ["tool"],
39650
+ additionalProperties: false
39651
+ }
39652
+ },
39653
+ {
39654
+ name: "call_tool",
39655
+ description: "Invoke one integration tool by its `<integration>/<tool>` name with the arguments its schema expects. The result is returned verbatim from the integration.",
39656
+ inputSchema: {
39657
+ type: "object",
39658
+ properties: {
39659
+ tool: { type: "string", description: "`<integration>/<tool>`." },
39660
+ arguments: { type: "object", description: "Arguments matching the schema from `describe_tool`.", additionalProperties: true }
39661
+ },
39662
+ required: ["tool"],
39663
+ additionalProperties: false
39664
+ }
39665
+ }
39666
+ ];
39667
+ }
39668
+ async function startChild(id, env, sdk) {
39669
+ const state = { id, client: null, tools: [], error: null };
39670
+ const timer = new Promise((_2, rej) => {
39671
+ const t2 = setTimeout(
39672
+ () => rej(new Error(`timed out after ${CHILD_START_TIMEOUT_MS / 1e3}s starting`)),
39673
+ CHILD_START_TIMEOUT_MS
39674
+ );
39675
+ t2.unref?.();
39676
+ });
39677
+ try {
39678
+ const transport = new sdk.StdioClientTransport({
39679
+ command: process.execPath,
39680
+ args: [process.argv[1], "mcp-run", id],
39681
+ env: Object.fromEntries(
39682
+ Object.entries(env).filter((e) => typeof e[1] === "string")
39683
+ ),
39684
+ stderr: "inherit"
39685
+ });
39686
+ const client3 = new sdk.Client({ name: "codeam-router", version: "1.0.0" }, { capabilities: {} });
39687
+ await Promise.race([client3.connect(transport), timer]);
39688
+ const listed = await Promise.race([client3.listTools(), timer]);
39689
+ state.client = client3;
39690
+ state.tools = listed.tools.map((t2) => ({
39691
+ integration: id,
39692
+ name: t2.name,
39693
+ description: t2.description ?? "",
39694
+ inputSchema: t2.inputSchema
39695
+ }));
39696
+ log.info("mcpRouter", `${id}: ${state.tools.length} tool(s) ready`);
39697
+ } catch (err) {
39698
+ state.error = err instanceof Error ? err.message : String(err);
39699
+ log.warn("mcpRouter", `${id}: unavailable \u2014 ${state.error}`);
39700
+ }
39701
+ return state;
39702
+ }
39703
+ async function mcpRouter() {
39704
+ const manifest = readIntegrationsManifest();
39705
+ const ids = (manifest?.integrations ?? []).filter((e) => e.delivery?.mcp).map((e) => e.id);
39706
+ const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
39707
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
39708
+ const { ListToolsRequestSchema, CallToolRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
39709
+ const { Client: ClientCtor } = await import("@modelcontextprotocol/sdk/client/index.js");
39710
+ const { StdioClientTransport: TransportCtor } = await import("@modelcontextprotocol/sdk/client/stdio.js");
39711
+ const sdk = { Client: ClientCtor, StdioClientTransport: TransportCtor };
39712
+ const children = await Promise.all(ids.map((id) => startChild(id, process.env, sdk)));
39713
+ const byId = new Map(children.map((c2) => [c2.id, c2]));
39714
+ const allTools = children.flatMap((c2) => c2.tools);
39715
+ log.info("mcpRouter", `fronting ${children.filter((c2) => c2.client).length}/${ids.length} integration server(s), ${allTools.length} tools behind 4`);
39716
+ const server = new Server({ name: MCP_ROUTER_SERVER_NAME, version: "1.0.0" }, { capabilities: { tools: {} } });
39717
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: routerToolDefinitions() }));
39718
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
39719
+ const name = req.params.name;
39720
+ const args2 = req.params.arguments ?? {};
39721
+ const text = (v, isError2 = false) => ({ content: [{ type: "text", text: typeof v === "string" ? v : JSON.stringify(v, null, 2) }], isError: isError2 });
39722
+ if (name === "list_integrations") {
39723
+ return text(children.map((c2) => ({ integration: c2.id, available: c2.client !== null, tools: c2.tools.length, ...c2.error ? { error: c2.error } : {} })));
39724
+ }
39725
+ if (name === "search_tools") {
39726
+ const q2 = typeof args2.query === "string" ? args2.query : "";
39727
+ const integration = typeof args2.integration === "string" ? args2.integration : void 0;
39728
+ const limit = typeof args2.limit === "number" ? Math.max(1, Math.min(50, args2.limit)) : 20;
39729
+ const hits = searchTools(allTools, q2, integration).slice(0, limit);
39730
+ return text(hits.map((t2) => ({ tool: qualifiedName(t2.integration, t2.name), description: t2.description })));
39731
+ }
39732
+ if (name === "describe_tool" || name === "call_tool") {
39733
+ const q2 = typeof args2.tool === "string" ? args2.tool : "";
39734
+ const parts = splitQualifiedName(q2);
39735
+ if (!parts) return text(`Expected "<integration>/<tool>", got "${q2}". Use search_tools to find the name.`, true);
39736
+ const child = byId.get(parts.integration);
39737
+ if (!child) return text(`No integration "${parts.integration}" is attached to this session. Use list_integrations.`, true);
39738
+ if (!child.client) return text(`Integration "${parts.integration}" is unavailable: ${child.error ?? "failed to start"}.`, true);
39739
+ const tool = child.tools.find((t2) => t2.name === parts.tool);
39740
+ if (!tool) return text(`No tool "${parts.tool}" in "${parts.integration}". Use search_tools.`, true);
39741
+ if (name === "describe_tool") return text({ tool: q2, description: tool.description, inputSchema: tool.inputSchema });
39742
+ const timer = new Promise((_2, rej) => setTimeout(() => rej(new Error(`"${q2}" did not answer within ${CALL_TIMEOUT_MS / 1e3}s`)), CALL_TIMEOUT_MS).unref?.());
39743
+ try {
39744
+ const res = await Promise.race([
39745
+ child.client.callTool({ name: parts.tool, arguments: args2.arguments ?? {} }),
39746
+ timer
39747
+ ]);
39748
+ return { content: res.content ?? [], isError: res.isError === true };
39749
+ } catch (err) {
39750
+ return text(`${q2} failed: ${err instanceof Error ? err.message : String(err)}`, true);
39751
+ }
39752
+ }
39753
+ return text(`Unknown tool "${name}".`, true);
39754
+ });
39755
+ await server.connect(new StdioServerTransport());
39756
+ process.stdin.on("close", () => {
39757
+ for (const c2 of children) void c2.client?.close().catch(() => void 0);
39758
+ });
39759
+ }
39760
+
39583
39761
  // src/integrations/provision.ts
39584
39762
  function buildMcpServersForStart(ctx) {
39585
39763
  const manifest = readIntegrationsManifest();
@@ -39608,6 +39786,30 @@ function buildMcpServersForStart(ctx) {
39608
39786
  env
39609
39787
  });
39610
39788
  }
39789
+ if (manifest.toolRouter === true && servers.length > 0) {
39790
+ const routed = servers.map((s) => s.name);
39791
+ log.info(
39792
+ "integrations",
39793
+ `tool router ON \u2014 fronting ${routed.length} MCP server(s) behind one: ${routed.join(", ")}`
39794
+ );
39795
+ return [
39796
+ {
39797
+ name: MCP_ROUTER_SERVER_NAME,
39798
+ command: process.execPath,
39799
+ args: [process.argv[1], "mcp-router"],
39800
+ // The router re-spawns each shim with THIS env: the same session /
39801
+ // plugin id / token / secret the shims got when injected directly.
39802
+ // No per-integration id — each child receives its own via
39803
+ // `mcp-run <id>` argv, which the shim prefers.
39804
+ env: [
39805
+ { name: "CODEAM_MCP_SESSION_ID", value: ctx.sessionId },
39806
+ { name: "CODEAM_MCP_PLUGIN_ID", value: ctx.pluginId },
39807
+ { name: "CODEAM_MCP_PLUGIN_TOKEN", value: ctx.pluginAuthToken },
39808
+ ...ctx.pollSecret ? [{ name: "CODEAM_MCP_POLL_SECRET", value: ctx.pollSecret }] : []
39809
+ ]
39810
+ }
39811
+ ];
39812
+ }
39611
39813
  if (servers.length) {
39612
39814
  log.info(
39613
39815
  "integrations",
@@ -43268,6 +43470,54 @@ ${BEADS_HINT}
43268
43470
  }
43269
43471
  }
43270
43472
 
43473
+ // src/integrations/refresh-manifest.ts
43474
+ function fingerprint(m) {
43475
+ const norm = [...m.integrations].map((e) => ({ id: e.id, delivery: e.delivery })).sort((a, b) => a.id.localeCompare(b.id));
43476
+ return JSON.stringify(norm);
43477
+ }
43478
+ async function refreshIntegrationsManifest(ctx, fetchImpl = fetch, timeoutMs = 8e3) {
43479
+ const url2 = `${resolveApiBaseUrl()}/api/plugin/integrations/manifest`;
43480
+ const ac = new AbortController();
43481
+ const timer = setTimeout(() => ac.abort(), timeoutMs);
43482
+ try {
43483
+ const resp = await fetchImpl(url2, {
43484
+ method: "POST",
43485
+ headers: { "Content-Type": "application/json", "X-Plugin-Auth-Token": ctx.pluginAuthToken },
43486
+ body: JSON.stringify({
43487
+ sessionId: ctx.sessionId,
43488
+ pluginId: ctx.pluginId,
43489
+ ...ctx.agent ? { agent: ctx.agent } : {}
43490
+ }),
43491
+ signal: ac.signal
43492
+ });
43493
+ if (!resp.ok) {
43494
+ return { status: "skipped", reason: `HTTP ${resp.status}` };
43495
+ }
43496
+ const json = await resp.json();
43497
+ const fresh = json.data;
43498
+ if (!json.success || !fresh || !Array.isArray(fresh.integrations)) {
43499
+ return { status: "skipped", reason: "malformed response" };
43500
+ }
43501
+ const onDisk = readIntegrationsManifest();
43502
+ if (onDisk && fingerprint(onDisk) === fingerprint(fresh)) return { status: "unchanged" };
43503
+ persistIntegrationsManifest(fresh);
43504
+ const result = {
43505
+ status: "rewritten",
43506
+ before: onDisk?.integrations.length ?? 0,
43507
+ after: fresh.integrations.length
43508
+ };
43509
+ log.info(
43510
+ "integrations",
43511
+ `manifest refreshed from backend at start (${result.before} \u2192 ${result.after} entr${result.after === 1 ? "y" : "ies"}) \u2014 the on-disk copy was stale`
43512
+ );
43513
+ return result;
43514
+ } catch (err) {
43515
+ return { status: "skipped", reason: err instanceof Error ? err.message : String(err) };
43516
+ } finally {
43517
+ clearTimeout(timer);
43518
+ }
43519
+ }
43520
+
43271
43521
  // src/skills/provision.ts
43272
43522
  var import_node_os13 = __toESM(require("os"));
43273
43523
  function provisionSkillsForStart(home = import_node_os13.default.homedir()) {
@@ -44328,6 +44578,17 @@ async function start(requestedAgent, presetSession) {
44328
44578
  beads = started;
44329
44579
  return started;
44330
44580
  });
44581
+ if (session.pluginAuthToken) {
44582
+ await refreshIntegrationsManifest({
44583
+ sessionId: session.id,
44584
+ pluginId,
44585
+ pluginAuthToken: session.pluginAuthToken,
44586
+ // The house agent runs the `claude` runtime, so `session.agent` cannot
44587
+ // tell it apart; the managed-proxy env can (`isHouseProxyEnv`). The
44588
+ // backend turns the tool router on for the house agent ONLY.
44589
+ agent: isHouseProxyEnv(process.env) ? HOUSE_AGENT_ID : session.agent
44590
+ });
44591
+ }
44331
44592
  const mcpServers = mergeWithLocalMcpServers(
44332
44593
  buildMcpServersForStart({
44333
44594
  sessionId: session.id,
@@ -47176,7 +47437,7 @@ function checkChokidar() {
47176
47437
  }
47177
47438
  async function doctor(args2 = []) {
47178
47439
  const json = args2.includes("--json");
47179
- const cliVersion = true ? "2.73.9" : "0.0.0-dev";
47440
+ const cliVersion = true ? "2.74.0" : "0.0.0-dev";
47180
47441
  const apiBase2 = resolveApiBaseUrl();
47181
47442
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
47182
47443
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -47647,7 +47908,7 @@ async function mcpWarm(argv = []) {
47647
47908
  // src/commands/version.ts
47648
47909
  var import_picocolors15 = __toESM(require("picocolors"));
47649
47910
  function version2() {
47650
- const v = true ? "2.73.9" : "unknown";
47911
+ const v = true ? "2.74.0" : "unknown";
47651
47912
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
47652
47913
  }
47653
47914
 
@@ -47851,6 +48112,9 @@ async function main() {
47851
48112
  if (command === "mcp-warm") {
47852
48113
  return mcpWarm(args);
47853
48114
  }
48115
+ if (command === "mcp-router") {
48116
+ return mcpRouter();
48117
+ }
47854
48118
  if (command === "deploy") {
47855
48119
  if (args[0] === "ls" || args[0] === "list") return deployList();
47856
48120
  if (args[0] === "stop" || args[0] === "remove") return deployStop();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.73.9",
3
+ "version": "2.74.0",
4
4
  "description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",