codeam-cli 2.53.4 → 2.55.1

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 +23 -0
  2. package/dist/index.js +245 -135
  3. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,29 @@ 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.54.0] — 2026-07-06
8
+
9
+ ### Added
10
+
11
+ - **shared:** Rename to @codeam/shared and make it publishable to npm (Phase 2 PR-0)
12
+ - **shared:** Agent capability flags + identity module + headroom manifest + beads superset (Phase 2 PR-1)
13
+
14
+ ### Fixed
15
+
16
+ - **cli:** Windows CI green — derive claude exe suffix from platformKey; win32-native install-target expectations
17
+ - **cli:** Self-update can actually spawn npm on Windows — shell for the .cmd shim
18
+
19
+ ## [2.53.4] — 2026-07-06
20
+
21
+ ### Fixed
22
+
23
+ - **jetbrains:** Eliminate all 8 internal-API usages flagged by Plugin Verifier
24
+ - **shared:** Phase 1 anti-drift — sync wire contracts, USER_EVENTS constants, zod RemoteCommand guard, JB respondWith + drift tests
25
+
26
+ ### Tests
27
+
28
+ - **cli:** Backend contract fixtures for beads_action + migrate off deprecated BeadsActionPayload alias
29
+
7
30
  ## [2.53.3] — 2026-07-06
8
31
 
9
32
  ### Fixed
package/dist/index.js CHANGED
@@ -199,20 +199,6 @@ function renderToLines(raw) {
199
199
  return screen;
200
200
  }
201
201
 
202
- // ../../packages/shared/src/protocol/remote-command.ts
203
- var import_zod = require("zod");
204
- var remoteCommandSchema = import_zod.z.object({
205
- id: import_zod.z.string(),
206
- sessionId: import_zod.z.string(),
207
- pluginId: import_zod.z.string(),
208
- type: import_zod.z.string(),
209
- // The backend may omit `payload` (or send null) for payload-less commands;
210
- // clients have always normalized that to `{}` — keep that behavior here.
211
- payload: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).nullish(),
212
- status: import_zod.z.string(),
213
- createdAt: import_zod.z.number()
214
- });
215
-
216
202
  // ../../packages/shared/src/models/pricing.ts
217
203
  var MODEL_PRICING = {
218
204
  // ── Anthropic / Claude ────────────────────────────────────
@@ -295,7 +281,11 @@ var AGENT_REGISTRY = {
295
281
  // capabilities). `setup_token` is the bare `sk-ant-oat01-…` from
296
282
  // `claude setup-token` → delivered via CLAUDE_CODE_OAUTH_TOKEN.
297
283
  supportedAuthKinds: ["setup_token", "oauth_token", "api_key"],
298
- preferredAuthKind: "setup_token"
284
+ preferredAuthKind: "setup_token",
285
+ headroomWrappable: true,
286
+ headroomKind: "claude",
287
+ // npm adapter `@agentclientprotocol/claude-agent-acp`.
288
+ acp: true
299
289
  },
300
290
  codex: {
301
291
  id: "codex",
@@ -303,7 +293,15 @@ var AGENT_REGISTRY = {
303
293
  binaryName: "codex",
304
294
  enabled: true,
305
295
  supportedAuthKinds: ["oauth_token", "api_key"],
306
- preferredAuthKind: "oauth_token"
296
+ preferredAuthKind: "oauth_token",
297
+ headroomWrappable: true,
298
+ headroomKind: "codex",
299
+ // npm adapter `@agentclientprotocol/codex-acp`.
300
+ acp: true,
301
+ // OAuth device-code flow; the user_code on the OpenAI page IS a real
302
+ // human-typed code — surfaces render it (with a copy affordance).
303
+ deviceFlow: true,
304
+ showsUserCode: true
307
305
  },
308
306
  copilot: {
309
307
  id: "copilot",
@@ -311,7 +309,12 @@ var AGENT_REGISTRY = {
311
309
  binaryName: "gh",
312
310
  enabled: false,
313
311
  supportedAuthKinds: ["oauth_token"],
314
- preferredAuthKind: "oauth_token"
312
+ preferredAuthKind: "oauth_token",
313
+ // `headroom init --global copilot` exists even though the agent is
314
+ // still disabled here (no runtime builder yet).
315
+ headroomWrappable: true,
316
+ headroomKind: "copilot",
317
+ acp: false
315
318
  },
316
319
  coderabbit: {
317
320
  id: "coderabbit",
@@ -321,7 +324,10 @@ var AGENT_REGISTRY = {
321
324
  // Backend registry is authoritative: CodeRabbit links via a real
322
325
  // API key only (no OAuth flow exists in api-v2).
323
326
  supportedAuthKinds: ["api_key"],
324
- preferredAuthKind: "api_key"
327
+ preferredAuthKind: "api_key",
328
+ headroomWrappable: false,
329
+ // Legacy PTY runtime — no ACP adapter registered.
330
+ acp: false
325
331
  },
326
332
  cursor: {
327
333
  id: "cursor",
@@ -334,7 +340,16 @@ var AGENT_REGISTRY = {
334
340
  // api_key rows may still exist server-side, but the link surface
335
341
  // no longer offers api_key.
336
342
  supportedAuthKinds: ["oauth_token"],
337
- preferredAuthKind: "oauth_token"
343
+ preferredAuthKind: "oauth_token",
344
+ // `headroom wrap cursor` is "manual/print-only" (IDE settings; the
345
+ // headless cursor-agent CLI has no base-URL override) — runs native.
346
+ headroomWrappable: false,
347
+ // Native ACP server: `cursor-agent acp`.
348
+ acp: true,
349
+ // Reverse-engineered device/poll flow; `userCode` is the secret PKCE
350
+ // verifier echoed back on poll — NEVER human-facing.
351
+ deviceFlow: true,
352
+ showsUserCode: false
338
353
  },
339
354
  aider: {
340
355
  id: "aider",
@@ -345,7 +360,10 @@ var AGENT_REGISTRY = {
345
360
  // / etc. env vars or `~/.aider.conf.yml`. The link flow surfaces
346
361
  // this via the existing --api-key escape hatch in commands/link.ts.
347
362
  supportedAuthKinds: ["api_key"],
348
- preferredAuthKind: "api_key"
363
+ preferredAuthKind: "api_key",
364
+ headroomWrappable: false,
365
+ // Legacy PTY runtime — no ACP adapter registered.
366
+ acp: false
349
367
  },
350
368
  gemini: {
351
369
  id: "gemini",
@@ -357,7 +375,11 @@ var AGENT_REGISTRY = {
357
375
  // accepted by the backend's GeminiProvisioningStrategy and propagated
358
376
  // into codespace deploys.
359
377
  supportedAuthKinds: ["oauth_token", "api_key"],
360
- preferredAuthKind: "oauth_token"
378
+ preferredAuthKind: "oauth_token",
379
+ // Not listed by `headroom wrap --help` — runs native.
380
+ headroomWrappable: false,
381
+ // Native ACP server: `gemini --skip-trust --acp`.
382
+ acp: true
361
383
  }
362
384
  };
363
385
  function getEnabledAgents() {
@@ -372,6 +394,56 @@ function isKnownAgentId(id) {
372
394
  return id in AGENT_REGISTRY;
373
395
  }
374
396
 
397
+ // ../../packages/shared/src/agents/identity.ts
398
+ var HOUSE_AGENT_ID = "house-codeagent-cloud";
399
+ var PUBLIC_TO_INTERNAL = {
400
+ claude_code: "claude",
401
+ // CLI-side extra: self-hosted deploy payloads may carry the internal id.
402
+ claude: "claude",
403
+ codex: "codex",
404
+ // CLI-side extra: copilot has no public LinkedAgentId (backend doesn't
405
+ // expose it) but the self-hosted path accepts it.
406
+ copilot: "copilot",
407
+ cursor: "cursor",
408
+ aider: "aider",
409
+ coderabbit: "coderabbit",
410
+ gemini: "gemini",
411
+ // The house agent runs Claude Code under the hood (pointed at the
412
+ // MiniMax proxy). Its internal runtime is therefore `claude`.
413
+ [HOUSE_AGENT_ID]: "claude"
414
+ };
415
+ var TERMINAL_AGENT_PREFIX = "__terminal__:";
416
+ var AGENT_ID_ALIASES = {
417
+ claude_code: "claude",
418
+ "claude-code": "claude",
419
+ "anthropic.claude-code": "claude",
420
+ "anthropics.claude": "claude",
421
+ "anthropic.claude-ce": "claude",
422
+ "anthropic.claude": "claude",
423
+ "com.anthropic.claudecode": "claude",
424
+ "com.anthropic.claude": "claude",
425
+ "openai.chatgpt": "codex",
426
+ "coderabbitai.coderabbit-vscode": "coderabbit"
427
+ };
428
+ function normalizeAgentId(raw) {
429
+ const value = (raw ?? "").trim().toLowerCase();
430
+ if (!value) return null;
431
+ if (isKnownAgentId(value)) return value;
432
+ const unprefixed = value.startsWith(TERMINAL_AGENT_PREFIX) ? value.slice(TERMINAL_AGENT_PREFIX.length) : value;
433
+ if (isKnownAgentId(unprefixed)) return unprefixed;
434
+ return AGENT_ID_ALIASES[unprefixed] ?? null;
435
+ }
436
+ function headroomKindFor(agentId) {
437
+ const normalized = (agentId ?? "").toLowerCase().replace(/[_-]/g, "");
438
+ if (!normalized) return null;
439
+ for (const meta of Object.values(AGENT_REGISTRY)) {
440
+ if (meta.headroomKind !== void 0 && normalized.startsWith(meta.id)) {
441
+ return meta.headroomKind;
442
+ }
443
+ }
444
+ return null;
445
+ }
446
+
375
447
  // ../../packages/shared/src/api-url.ts
376
448
  var DEFAULT_API_BASE_URL = "https://api.codeagent-mobile.com";
377
449
  var DEV_API_BASE_URL = "https://dev-api.codeagent-mobile.com";
@@ -384,6 +456,44 @@ function resolveApiBaseUrl() {
384
456
  return DEFAULT_API_BASE_URL;
385
457
  }
386
458
 
459
+ // ../../packages/shared/src/headroom/manifest.ts
460
+ var HEADROOM_PIP_COMPANIONS = [
461
+ "fastapi",
462
+ "uvicorn",
463
+ "httpx[http2]",
464
+ "websockets",
465
+ "zstandard"
466
+ ];
467
+ var HEADROOM_EXTRAS_BY_SURFACE = {
468
+ codespace: ["proxy", "code"],
469
+ selfHosted: ["proxy", "code"],
470
+ onDemand: ["proxy", "code", "image"]
471
+ };
472
+ function headroomPipPackage(extras) {
473
+ return `headroom-ai[${extras.join(",")}]`;
474
+ }
475
+ var HEADROOM_MODELS = [
476
+ {
477
+ repo: "chopratejas/kompress-v2-base",
478
+ allowPatterns: ["*.json", "onnx/*.onnx", "kompress-int8-wo.onnx"]
479
+ },
480
+ {
481
+ repo: "answerdotai/ModernBERT-base",
482
+ allowPatterns: ["*.json", "tokenizer*", "*.txt", "vocab*", "merges*"]
483
+ }
484
+ ];
485
+ function headroomSnapshotDownloadLine(model, opts = {}) {
486
+ const sep7 = opts.spaceAfterComma ? ", " : ",";
487
+ const patterns = model.allowPatterns.map((p2) => `"${p2}"`).join(sep7);
488
+ return `snapshot_download("${model.repo}", allow_patterns=[${patterns}])`;
489
+ }
490
+ function headroomModelPredownloadScript(opts = {}) {
491
+ return [
492
+ "from huggingface_hub import snapshot_download",
493
+ ...HEADROOM_MODELS.map((m) => headroomSnapshotDownloadLine(m, opts))
494
+ ].join("\n");
495
+ }
496
+
387
497
  // ../../packages/shared/src/types/events.ts
388
498
  var USER_EVENTS = {
389
499
  PAIRED_SESSION_STATUS: "paired_session_status",
@@ -5511,7 +5621,7 @@ function readAnonId() {
5511
5621
  }
5512
5622
  function superProperties() {
5513
5623
  return {
5514
- cliVersion: true ? "2.53.4" : "0.0.0-dev",
5624
+ cliVersion: true ? "2.55.1" : "0.0.0-dev",
5515
5625
  nodeVersion: process.version,
5516
5626
  platform: process.platform,
5517
5627
  arch: process.arch,
@@ -5692,7 +5802,7 @@ var os4 = __toESM(require("os"));
5692
5802
  // package.json
5693
5803
  var package_default = {
5694
5804
  name: "codeam-cli",
5695
- version: "2.53.4",
5805
+ version: "2.55.1",
5696
5806
  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.",
5697
5807
  type: "commonjs",
5698
5808
  main: "dist/index.js",
@@ -5780,7 +5890,7 @@ var package_default = {
5780
5890
  "@beads/bd": "1.0.5"
5781
5891
  },
5782
5892
  devDependencies: {
5783
- "@codeagent/shared": "*",
5893
+ "@codeam/shared": "*",
5784
5894
  "@types/node": "^22.0.0",
5785
5895
  "@types/qrcode-terminal": "^0.12.0",
5786
5896
  "@types/which": "^3.0.4",
@@ -6045,6 +6155,8 @@ async function postBeadsProvisioning(input) {
6045
6155
  try {
6046
6156
  await _transport.postJsonAuthed(
6047
6157
  `${API_BASE}/api/beads/provisioning`,
6158
+ // The CLI is the producer of this hop — the body is the shared wire
6159
+ // shape (`@codeam/shared` `BeadsProvisioningPayload`), validated here.
6048
6160
  {
6049
6161
  sessionId: input.sessionId,
6050
6162
  pluginId: input.pluginId,
@@ -6726,7 +6838,7 @@ var CommandRelayService = class {
6726
6838
  // fresh + clear the "CLI update available" banner after a self-update
6727
6839
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
6728
6840
  // pair/reconnect). Older backends ignore the extra field.
6729
- ..."2.53.4" ? { ideVersion: "2.53.4" } : {}
6841
+ ..."2.55.1" ? { ideVersion: "2.55.1" } : {}
6730
6842
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
6731
6843
  }
6732
6844
  /**
@@ -8108,68 +8220,68 @@ var import_child_process22 = require("child_process");
8108
8220
  var import_which2 = __toESM(require("which"));
8109
8221
 
8110
8222
  // src/lib/payload.ts
8111
- var import_zod2 = require("zod");
8112
- var fileEntrySchema = import_zod2.z.object({
8113
- filename: import_zod2.z.string().min(1).max(256),
8114
- mimeType: import_zod2.z.string(),
8115
- base64: import_zod2.z.string()
8223
+ var import_zod = require("zod");
8224
+ var fileEntrySchema = import_zod.z.object({
8225
+ filename: import_zod.z.string().min(1).max(256),
8226
+ mimeType: import_zod.z.string(),
8227
+ base64: import_zod.z.string()
8116
8228
  });
8117
- var startCommandSchema = import_zod2.z.object({
8118
- prompt: import_zod2.z.string().optional(),
8119
- files: import_zod2.z.array(fileEntrySchema).optional(),
8120
- input: import_zod2.z.string().optional(),
8121
- index: import_zod2.z.number().optional(),
8122
- from: import_zod2.z.number().optional(),
8123
- id: import_zod2.z.string().optional(),
8124
- auto: import_zod2.z.boolean().optional(),
8229
+ var startCommandSchema = import_zod.z.object({
8230
+ prompt: import_zod.z.string().optional(),
8231
+ files: import_zod.z.array(fileEntrySchema).optional(),
8232
+ input: import_zod.z.string().optional(),
8233
+ index: import_zod.z.number().optional(),
8234
+ from: import_zod.z.number().optional(),
8235
+ id: import_zod.z.string().optional(),
8236
+ auto: import_zod.z.boolean().optional(),
8125
8237
  // `read_file` / `write_file` for the mobile + landing mini-IDE modal.
8126
8238
  // `path` is bounded to 4096 chars (a comfortable POSIX path max) so a
8127
8239
  // malformed payload can't blow up the disk-side validator.
8128
- path: import_zod2.z.string().min(1).max(4096).optional(),
8129
- content: import_zod2.z.string().optional(),
8240
+ path: import_zod.z.string().min(1).max(4096).optional(),
8241
+ content: import_zod.z.string().optional(),
8130
8242
  // Mini-IDE / project ops. `paths` (plural, strings) is used for
8131
8243
  // git_commit's optional file selection — distinct from `files`
8132
8244
  // (FileEntry[]) used by `start_task` for attachments.
8133
- query: import_zod2.z.string().max(256).optional(),
8134
- message: import_zod2.z.string().max(8e3).optional(),
8135
- paths: import_zod2.z.array(import_zod2.z.string().max(4096)).optional(),
8245
+ query: import_zod.z.string().max(256).optional(),
8246
+ message: import_zod.z.string().max(8e3).optional(),
8247
+ paths: import_zod.z.array(import_zod.z.string().max(4096)).optional(),
8136
8248
  // `show_install_command` — backend pushes the self-hosted install
8137
8249
  // one-liner the user copies onto their own box. DISPLAY-ONLY: the
8138
8250
  // CLI prints it to the terminal and never executes it. Bounded to
8139
8251
  // 8192 chars so a malformed payload can't flood the terminal.
8140
- command: import_zod2.z.string().min(1).max(8192).optional(),
8141
- side: import_zod2.z.enum(["ours", "theirs"]).optional(),
8142
- limit: import_zod2.z.number().int().min(1).max(500).optional(),
8252
+ command: import_zod.z.string().min(1).max(8192).optional(),
8253
+ side: import_zod.z.enum(["ours", "theirs"]).optional(),
8254
+ limit: import_zod.z.number().int().min(1).max(500).optional(),
8143
8255
  // search_files options. `query` is the haystack/needle string,
8144
8256
  // declared above for list_files. The rest mirror VS Code's
8145
8257
  // search panel toggles + the @codeam/ide-core SearchOptions
8146
8258
  // contract.
8147
- caseSensitive: import_zod2.z.boolean().optional(),
8148
- wholeWord: import_zod2.z.boolean().optional(),
8149
- regex: import_zod2.z.boolean().optional(),
8150
- include: import_zod2.z.array(import_zod2.z.string().max(512)).max(64).optional(),
8151
- exclude: import_zod2.z.array(import_zod2.z.string().max(512)).max(64).optional(),
8152
- maxResults: import_zod2.z.number().int().min(1).max(500).optional(),
8259
+ caseSensitive: import_zod.z.boolean().optional(),
8260
+ wholeWord: import_zod.z.boolean().optional(),
8261
+ regex: import_zod.z.boolean().optional(),
8262
+ include: import_zod.z.array(import_zod.z.string().max(512)).max(64).optional(),
8263
+ exclude: import_zod.z.array(import_zod.z.string().max(512)).max(64).optional(),
8264
+ maxResults: import_zod.z.number().int().min(1).max(500).optional(),
8153
8265
  // terminal_open / _write / _resize / _close. `sessionId` is the
8154
8266
  // opaque uuid returned by `terminal_open` and required by every
8155
8267
  // subsequent op. `data` carries keystrokes (any UTF-8 string).
8156
8268
  // `cwd` lets the host pin the spawn directory.
8157
- sessionId: import_zod2.z.string().min(1).max(128).optional(),
8158
- data: import_zod2.z.string().max(64 * 1024).optional(),
8159
- cwd: import_zod2.z.string().max(4096).optional(),
8160
- cols: import_zod2.z.number().int().min(1).max(500).optional(),
8161
- rows: import_zod2.z.number().int().min(1).max(200).optional(),
8269
+ sessionId: import_zod.z.string().min(1).max(128).optional(),
8270
+ data: import_zod.z.string().max(64 * 1024).optional(),
8271
+ cwd: import_zod.z.string().max(4096).optional(),
8272
+ cols: import_zod.z.number().int().min(1).max(500).optional(),
8273
+ rows: import_zod.z.number().int().min(1).max(200).optional(),
8162
8274
  // `apply_file_review` (Epic B follow-up — backend pushes this when
8163
8275
  // the user clicks APPROVE_CHANGES / REJECT_CHANGES in the diff
8164
8276
  // drawer). `filePath` is relative to the enclosing git repo; the
8165
8277
  // handler walks up from it to find `.git/` and runs `git add` or
8166
8278
  // `git restore` from there. `action='approved'` stages the edit,
8167
8279
  // `action='rejected'` discards every worktree change on the file.
8168
- filePath: import_zod2.z.string().min(1).max(4096).optional(),
8169
- action: import_zod2.z.enum(["approved", "rejected", "enable", "disable", "status"]).optional(),
8280
+ filePath: import_zod.z.string().min(1).max(4096).optional(),
8281
+ action: import_zod.z.enum(["approved", "rejected", "enable", "disable", "status"]).optional(),
8170
8282
  // `headroom_configure` — savings ingest URL delivered from the session
8171
8283
  // when enabling Headroom on-demand. Bounded to 2048 chars.
8172
- savingsIngestUrl: import_zod2.z.string().url().max(2048).optional(),
8284
+ savingsIngestUrl: import_zod.z.string().url().max(2048).optional(),
8173
8285
  // `request_link_credentials` — backend fires this from the
8174
8286
  // heartbeat handler when it notices the user is running an agent
8175
8287
  // they haven't vaulted yet. Also reused by `get_context` /
@@ -8185,7 +8297,7 @@ var startCommandSchema = import_zod2.z.object({
8185
8297
  // Without nullable() zod rejects the whole payload, the dispatcher
8186
8298
  // logs "Ignoring malformed list_models payload" and the model
8187
8299
  // picker / context never load.
8188
- agentId: import_zod2.z.string().max(64).nullable().optional(),
8300
+ agentId: import_zod.z.string().max(64).nullable().optional(),
8189
8301
  // `request_ai_summary` / `request_ai_insight` — backend fires
8190
8302
  // these on turn-end (+ file selection) when LinkedAgent.
8191
8303
  // aiInsightsEnabled is true. The CLI spawns the agent in
@@ -8199,37 +8311,37 @@ var startCommandSchema = import_zod2.z.object({
8199
8311
  // unchanged on the result POST so the backend doesn't have to
8200
8312
  // recompute (numbers came from file_changes; the agent only
8201
8313
  // writes the narrative).
8202
- turnId: import_zod2.z.string().max(128).optional(),
8203
- fileChangeId: import_zod2.z.string().max(128).optional(),
8204
- stats: import_zod2.z.object({
8205
- added: import_zod2.z.number().int(),
8206
- removed: import_zod2.z.number().int(),
8207
- complexityShift: import_zod2.z.number().int()
8314
+ turnId: import_zod.z.string().max(128).optional(),
8315
+ fileChangeId: import_zod.z.string().max(128).optional(),
8316
+ stats: import_zod.z.object({
8317
+ added: import_zod.z.number().int(),
8318
+ removed: import_zod.z.number().int(),
8319
+ complexityShift: import_zod.z.number().int()
8208
8320
  }).optional(),
8209
8321
  // `preview_start` carries the agent-detected `PreviewDetection`
8210
8322
  // shape from the mobile / web confirmation sheet. Mirrors
8211
- // `@codeagent/shared`'s `PreviewDetection` byte-for-byte. Kept
8323
+ // `@codeam/shared`'s `PreviewDetection` byte-for-byte. Kept
8212
8324
  // loose (`unknown` for env / setup_commands) so a CLI version
8213
8325
  // running against a newer backend that adds optional fields still
8214
8326
  // accepts the payload; the handler validates the shape it needs
8215
8327
  // before spawning.
8216
- detection: import_zod2.z.object({
8217
- framework: import_zod2.z.string().max(64),
8218
- command: import_zod2.z.string().min(1).max(256),
8219
- args: import_zod2.z.array(import_zod2.z.string().max(1024)).max(64),
8220
- port: import_zod2.z.number().int().min(1).max(65535),
8221
- ready_pattern: import_zod2.z.string().min(1).max(4096),
8222
- env: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.string().max(8192)).optional(),
8328
+ detection: import_zod.z.object({
8329
+ framework: import_zod.z.string().max(64),
8330
+ command: import_zod.z.string().min(1).max(256),
8331
+ args: import_zod.z.array(import_zod.z.string().max(1024)).max(64),
8332
+ port: import_zod.z.number().int().min(1).max(65535),
8333
+ ready_pattern: import_zod.z.string().min(1).max(4096),
8334
+ env: import_zod.z.record(import_zod.z.string(), import_zod.z.string().max(8192)).optional(),
8223
8335
  // The agent emits entries as either {cmd,args} objects OR bare command
8224
8336
  // strings ("npx prisma generate"). Accept both and normalize strings to
8225
8337
  // {cmd,args} so a string entry doesn't reject the whole detection (which
8226
8338
  // would silently drop the preview) and downstream always gets objects.
8227
- setup_commands: import_zod2.z.array(
8228
- import_zod2.z.union([
8229
- import_zod2.z.string().min(1).max(1024),
8230
- import_zod2.z.object({
8231
- cmd: import_zod2.z.string().min(1).max(256),
8232
- args: import_zod2.z.array(import_zod2.z.string().max(1024)).max(64)
8339
+ setup_commands: import_zod.z.array(
8340
+ import_zod.z.union([
8341
+ import_zod.z.string().min(1).max(1024),
8342
+ import_zod.z.object({
8343
+ cmd: import_zod.z.string().min(1).max(256),
8344
+ args: import_zod.z.array(import_zod.z.string().max(1024)).max(64)
8233
8345
  })
8234
8346
  ]).transform((entry) => {
8235
8347
  if (typeof entry !== "string") return entry;
@@ -8237,15 +8349,15 @@ var startCommandSchema = import_zod2.z.object({
8237
8349
  return { cmd: parts[0] ?? "", args: parts.slice(1) };
8238
8350
  })
8239
8351
  ).max(32).optional(),
8240
- notes: import_zod2.z.string().max(4096).nullable().optional()
8352
+ notes: import_zod.z.string().max(4096).nullable().optional()
8241
8353
  }).optional(),
8242
8354
  // `env_write` carries the full desired set of environment variables
8243
8355
  // for the project `.env`. Bounded so a malformed payload can't flood
8244
8356
  // the disk-side serializer. `env_read` / `preview_restart` send no payload.
8245
- vars: import_zod2.z.array(
8246
- import_zod2.z.object({
8247
- key: import_zod2.z.string().min(1).max(256),
8248
- value: import_zod2.z.string().max(32768)
8357
+ vars: import_zod.z.array(
8358
+ import_zod.z.object({
8359
+ key: import_zod.z.string().min(1).max(256),
8360
+ value: import_zod.z.string().max(32768)
8249
8361
  })
8250
8362
  ).max(512).optional()
8251
8363
  });
@@ -8910,7 +9022,7 @@ __export(dist_exports, {
8910
9022
  S_ERROR: () => ge,
8911
9023
  S_INFO: () => he,
8912
9024
  S_PASSWORD_MASK: () => xe,
8913
- S_RADIO_ACTIVE: () => z4,
9025
+ S_RADIO_ACTIVE: () => z3,
8914
9026
  S_RADIO_INACTIVE: () => H2,
8915
9027
  S_STEP_ACTIVE: () => _e,
8916
9028
  S_STEP_CANCEL: () => oe,
@@ -9389,7 +9501,7 @@ function w(r, t2) {
9389
9501
  const e = r;
9390
9502
  e.isTTY && e.setRawMode(t2);
9391
9503
  }
9392
- function z3({ input: r = import_node_process.stdin, output: t2 = import_node_process.stdout, overwrite: e = true, hideCursor: s = true } = {}) {
9504
+ function z2({ input: r = import_node_process.stdin, output: t2 = import_node_process.stdout, overwrite: e = true, hideCursor: s = true } = {}) {
9393
9505
  const i = _.createInterface({ input: r, output: t2, prompt: "", tabSize: 1 });
9394
9506
  _.emitKeypressEvents(r, i), r instanceof import_node_tty.ReadStream && r.isTTY && r.setRawMode(true);
9395
9507
  const n = (o, { name: a, sequence: h }) => {
@@ -10001,7 +10113,7 @@ var d2 = w2("\u2502", "|");
10001
10113
  var E2 = w2("\u2514", "\u2014");
10002
10114
  var Ie = w2("\u2510", "T");
10003
10115
  var Ee = w2("\u2518", "\u2014");
10004
- var z4 = w2("\u25CF", ">");
10116
+ var z3 = w2("\u25CF", ">");
10005
10117
  var H2 = w2("\u25CB", " ");
10006
10118
  var te = w2("\u25FB", "[\u2022]");
10007
10119
  var U = w2("\u25FC", "[+]");
@@ -10093,7 +10205,7 @@ var Ae = (e) => new H({ options: e.options, initialValue: e.initialValue ? [e.in
10093
10205
  const $2 = Me(a), y2 = a.hint && a.value === this.focusedValue ? (0, import_node_util2.styleText)("dim", ` (${a.hint})`) : "";
10094
10206
  switch (l) {
10095
10207
  case "active":
10096
- return `${(0, import_node_util2.styleText)("green", z4)} ${$2}${y2}`;
10208
+ return `${(0, import_node_util2.styleText)("green", z3)} ${$2}${y2}`;
10097
10209
  case "inactive":
10098
10210
  return `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", $2)}`;
10099
10211
  case "disabled":
@@ -10206,9 +10318,9 @@ ${(0, import_node_util2.styleText)("gray", d2)}` : ""}`;
10206
10318
  }
10207
10319
  default: {
10208
10320
  const l = r ? `${(0, import_node_util2.styleText)("cyan", d2)} ` : "", $2 = r ? (0, import_node_util2.styleText)("cyan", E2) : "";
10209
- return `${c2}${l}${this.value ? `${(0, import_node_util2.styleText)("green", z4)} ${i}` : `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", i)}`}${e.vertical ? r ? `
10321
+ return `${c2}${l}${this.value ? `${(0, import_node_util2.styleText)("green", z3)} ${i}` : `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", i)}`}${e.vertical ? r ? `
10210
10322
  ${(0, import_node_util2.styleText)("cyan", d2)} ` : `
10211
- ` : ` ${(0, import_node_util2.styleText)("dim", "/")} `}${this.value ? `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", s)}` : `${(0, import_node_util2.styleText)("green", z4)} ${s}`}
10323
+ ` : ` ${(0, import_node_util2.styleText)("dim", "/")} `}${this.value ? `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", s)}` : `${(0, import_node_util2.styleText)("green", z3)} ${s}`}
10212
10324
  ${$2}
10213
10325
  `;
10214
10326
  }
@@ -10539,7 +10651,7 @@ var fe = ({ indicator: e = "dots", onCancel: i, output: s = process.stdout, canc
10539
10651
  const A2 = (performance.now() - _2) / 1e3, k2 = Math.floor(A2 / 60), L2 = Math.floor(A2 % 60);
10540
10652
  return k2 > 0 ? `[${k2}m ${L2}s]` : `[${L2}s]`;
10541
10653
  }, D2 = a.withGuide ?? u.withGuide, ie = (_2 = "") => {
10542
- p2 = true, $2 = z3({ output: s }), g = R2(_2), h = performance.now(), D2 && s.write(`${(0, import_node_util2.styleText)("gray", d2)}
10654
+ p2 = true, $2 = z2({ output: s }), g = R2(_2), h = performance.now(), D2 && s.write(`${(0, import_node_util2.styleText)("gray", d2)}
10543
10655
  `);
10544
10656
  let A2 = 0, k2 = 0;
10545
10657
  x(), y2 = setInterval(() => {
@@ -10610,7 +10722,7 @@ var _t = (e) => {
10610
10722
  case "selected":
10611
10723
  return `${re(u2, (n) => (0, import_node_util2.styleText)("dim", n))}`;
10612
10724
  case "active":
10613
- return `${(0, import_node_util2.styleText)("green", z4)} ${u2}${s.hint ? ` ${(0, import_node_util2.styleText)("dim", `(${s.hint})`)}` : ""}`;
10725
+ return `${(0, import_node_util2.styleText)("green", z3)} ${u2}${s.hint ? ` ${(0, import_node_util2.styleText)("dim", `(${s.hint})`)}` : ""}`;
10614
10726
  case "cancelled":
10615
10727
  return `${re(u2, (n) => (0, import_node_util2.styleText)(["strikethrough", "dim"], n))}`;
10616
10728
  default:
@@ -15226,7 +15338,7 @@ async function autoUpgradeBeforeCriticalCommand() {
15226
15338
  if (process.env.NODE_ENV === "test") return;
15227
15339
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
15228
15340
  if (process.env.CI) return;
15229
- const current = true ? "2.53.4" : null;
15341
+ const current = true ? "2.55.1" : null;
15230
15342
  if (!current) return;
15231
15343
  const cache = readCache();
15232
15344
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -15243,7 +15355,7 @@ function checkForUpdates() {
15243
15355
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
15244
15356
  if (process.env.CI) return;
15245
15357
  if (!process.stdout.isTTY) return;
15246
- const current = true ? "2.53.4" : null;
15358
+ const current = true ? "2.55.1" : null;
15247
15359
  if (!current) return;
15248
15360
  const cache = readCache();
15249
15361
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -15406,7 +15518,10 @@ var CONTROL_AGENT_META = {
15406
15518
  binaryName: "codeam",
15407
15519
  enabled: true,
15408
15520
  supportedAuthKinds: ["oauth_token"],
15409
- preferredAuthKind: "oauth_token"
15521
+ preferredAuthKind: "oauth_token",
15522
+ // Synthetic control channel — capability flags are moot (no agent runs).
15523
+ headroomWrappable: false,
15524
+ acp: false
15410
15525
  };
15411
15526
  var PEP668_MARKER = "externally-managed-environment";
15412
15527
  var PM_INSTALL_TIMEOUT_MS = 18e4;
@@ -15577,15 +15692,10 @@ async function ensurePip(runner) {
15577
15692
  return true;
15578
15693
  }
15579
15694
  function agentIdToHeadroomKind(agentId) {
15580
- const normalized = (agentId ?? "").toLowerCase().replace(/[_-]/g, "");
15581
- if (normalized.startsWith("claude")) return "claude";
15582
- if (normalized.startsWith("codex")) return "codex";
15583
- if (normalized.startsWith("copilot")) return "copilot";
15584
- return "claude";
15695
+ return headroomKindFor(agentId) ?? "claude";
15585
15696
  }
15586
15697
  function isHeadroomSupportedAgent(agentId) {
15587
- const n = (agentId ?? "").toLowerCase().replace(/[_-]/g, "");
15588
- return n.startsWith("claude") || n.startsWith("codex") || n.startsWith("copilot");
15698
+ return headroomKindFor(agentId) !== null;
15589
15699
  }
15590
15700
  function headroomConfigPath() {
15591
15701
  return path38.join(os31.homedir(), ".codeam", "headroom-config.json");
@@ -15838,10 +15948,10 @@ async function ensureModernPython(runner) {
15838
15948
  return py;
15839
15949
  }
15840
15950
  async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner, opts = {}) {
15841
- const extras = opts.extras ?? ["proxy", "code"];
15951
+ const extras = opts.extras ?? [...HEADROOM_EXTRAS_BY_SURFACE.selfHosted];
15842
15952
  const onProgress = opts.onProgress ?? (() => {
15843
15953
  });
15844
- const SERVER_DEPS = ["fastapi", "uvicorn", "httpx[http2]", "websockets", "zstandard"];
15954
+ const SERVER_DEPS = HEADROOM_PIP_COMPANIONS;
15845
15955
  const pipAvailable = await ensurePip(runner);
15846
15956
  if (!pipAvailable) {
15847
15957
  return false;
@@ -15866,7 +15976,7 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
15866
15976
  return false;
15867
15977
  };
15868
15978
  onProgress("pip");
15869
- const headroomPkg = `headroom-ai[${extras.join(",")}]`;
15979
+ const headroomPkg = headroomPipPackage(extras);
15870
15980
  const installOk = await pipInstall([headroomPkg, ...SERVER_DEPS], [], ENGINE_INSTALL_TIMEOUT_MS);
15871
15981
  if (!installOk) {
15872
15982
  log.warn("host-agent", `headroom engine install failed (${headroomPkg}) \u2014 skipping Headroom`);
@@ -15874,11 +15984,7 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
15874
15984
  }
15875
15985
  log.info("host-agent", `headroom + ONNX engines installed (${headroomPkg})`);
15876
15986
  onProgress("model");
15877
- const predownloadPy = [
15878
- "from huggingface_hub import snapshot_download",
15879
- 'snapshot_download("chopratejas/kompress-v2-base", allow_patterns=["*.json","onnx/*.onnx","kompress-int8-wo.onnx"])',
15880
- 'snapshot_download("answerdotai/ModernBERT-base", allow_patterns=["*.json","tokenizer*","*.txt","vocab*","merges*"])'
15881
- ].join("\n");
15987
+ const predownloadPy = headroomModelPredownloadScript();
15882
15988
  const dl = await runner.run(py, ["-c", predownloadPy], {
15883
15989
  timeoutMs: ENGINE_INSTALL_TIMEOUT_MS
15884
15990
  });
@@ -15954,7 +16060,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process16.s
15954
16060
  detached: false
15955
16061
  });
15956
16062
  function currentCliVersion() {
15957
- return true ? "2.53.4" : null;
16063
+ return true ? "2.55.1" : null;
15958
16064
  }
15959
16065
  function runCmd(cmd, args2, timeoutMs) {
15960
16066
  return new Promise((resolve7) => {
@@ -16666,7 +16772,9 @@ async function configureHeadroom(action, ctx, deps) {
16666
16772
  if (action === "enable") {
16667
16773
  if (!isHeadroomSupportedAgent(ctx.agent)) return { supported: false };
16668
16774
  const ok = await deps.setup(ctx.agent, void 0, {
16669
- extras: ["proxy", "code", "image"],
16775
+ // On-demand surface ships the extra `image` compression support — see
16776
+ // the shared Headroom manifest (`HEADROOM_EXTRAS_BY_SURFACE`).
16777
+ extras: [...HEADROOM_EXTRAS_BY_SURFACE.onDemand],
16670
16778
  onProgress: (step) => deps.emit({ type: USER_EVENTS.HEADROOM_PROGRESS, step })
16671
16779
  });
16672
16780
  if (!ok) {
@@ -18512,7 +18620,7 @@ var BeadsWatcher = class {
18512
18620
  issues,
18513
18621
  // We don't track edges in the P0 snapshot yet, so send an empty array.
18514
18622
  // (The backend DTO marks `dependencies` optional and tolerates absence,
18515
- // but the wire contract in @codeagent/shared says "always sent" — keep
18623
+ // but the wire contract in @codeam/shared says "always sent" — keep
18516
18624
  // the payload deterministic.)
18517
18625
  dependencies: [],
18518
18626
  memories: [],
@@ -19119,7 +19227,7 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
19119
19227
  }
19120
19228
  const savingsIngestUrl = parsed.savingsIngestUrl;
19121
19229
  let rawAgentId = ctx.agentId || (typeof parsed.agentId === "string" ? parsed.agentId : "");
19122
- if (rawAgentId === "claude_code") rawAgentId = "claude";
19230
+ rawAgentId = normalizeAgentId(rawAgentId) ?? rawAgentId;
19123
19231
  let configuredAgent = rawAgentId;
19124
19232
  if (!configuredAgent) {
19125
19233
  try {
@@ -19233,9 +19341,9 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
19233
19341
  var headroomBudgetH = async (ctx, cmd) => {
19234
19342
  const payload = cmd.payload;
19235
19343
  let rawAgentId = ctx.agentId || (typeof payload.agentId === "string" ? payload.agentId : "");
19236
- if (rawAgentId === "claude_code") rawAgentId = "claude";
19344
+ rawAgentId = normalizeAgentId(rawAgentId) ?? rawAgentId;
19237
19345
  let payloadAgentId = typeof payload.agentId === "string" ? payload.agentId : "";
19238
- if (payloadAgentId === "claude_code") payloadAgentId = "claude";
19346
+ payloadAgentId = normalizeAgentId(payloadAgentId) ?? payloadAgentId;
19239
19347
  if (!rawAgentId || !isHeadroomSupportedAgent(rawAgentId)) {
19240
19348
  await ctx.relay.sendResult(cmd.id, "completed", { applied: false });
19241
19349
  return;
@@ -19295,7 +19403,7 @@ var beadsConfigureH = async (ctx, cmd, parsed) => {
19295
19403
  return;
19296
19404
  }
19297
19405
  let rawAgentId = ctx.agentId || (typeof parsed.agentId === "string" ? parsed.agentId : "");
19298
- if (rawAgentId === "claude_code") rawAgentId = "claude";
19406
+ rawAgentId = normalizeAgentId(rawAgentId) ?? rawAgentId;
19299
19407
  const agentIds = rawAgentId && isKnownAgentId(rawAgentId) ? [rawAgentId] : [];
19300
19408
  const deps = {
19301
19409
  provision: async () => {
@@ -19390,12 +19498,14 @@ function buildNpmInstallInvocation(opts) {
19390
19498
  async function runNpmInstallLatest() {
19391
19499
  let lastError = "";
19392
19500
  const invocation = buildNpmInstallInvocation();
19501
+ const useShell = process.platform === "win32";
19502
+ const command2 = useShell && invocation.command.includes(" ") ? `"${invocation.command}"` : invocation.command;
19393
19503
  for (let attempt = 1; attempt <= CLI_UPDATE_MAX_ATTEMPTS; attempt++) {
19394
19504
  const result = await new Promise((resolve7) => {
19395
19505
  (0, import_child_process22.execFile)(
19396
- invocation.command,
19506
+ command2,
19397
19507
  invocation.args,
19398
- { timeout: CLI_UPDATE_INSTALL_TIMEOUT_MS },
19508
+ { timeout: CLI_UPDATE_INSTALL_TIMEOUT_MS, shell: useShell },
19399
19509
  (err, _stdout, stderr) => {
19400
19510
  if (!err) {
19401
19511
  resolve7({ ok: true });
@@ -19562,9 +19672,9 @@ var requestLinkCredentialsH = (ctx, _cmd, parsed) => {
19562
19672
  log.trace("auto-link", "skipped \u2014 no pluginAuthToken on this paired session");
19563
19673
  return;
19564
19674
  }
19565
- const internalId = publicId === "claude_code" ? "claude" : publicId;
19566
- if (!isKnownAgentId(internalId) || !AGENT_REGISTRY[internalId].enabled) {
19567
- log.trace("auto-link", `unknown / disabled agent: ${internalId}`);
19675
+ const internalId = normalizeAgentId(publicId);
19676
+ if (!internalId || !AGENT_REGISTRY[internalId].enabled) {
19677
+ log.trace("auto-link", `unknown / disabled agent: ${publicId}`);
19568
19678
  return;
19569
19679
  }
19570
19680
  const pluginAuthToken = ctx.pluginAuthToken;
@@ -21352,7 +21462,7 @@ function resolveClaudeNativeBinary(deps = {}) {
21352
21462
  const sdkDir = deps.sdkDir !== void 0 ? deps.sdkDir : defaultSdkDir();
21353
21463
  if (!sdkDir) return null;
21354
21464
  const scopeDir = import_path8.default.dirname(sdkDir);
21355
- const binName = process.platform === "win32" ? "claude.exe" : "claude";
21465
+ const binName = platformKey.startsWith("win32-") ? "claude.exe" : "claude";
21356
21466
  const candidate = import_path8.default.join(scopeDir, `claude-agent-sdk-${platformKey}`, binName);
21357
21467
  return existsSync21(candidate) ? candidate : null;
21358
21468
  }
@@ -21554,15 +21664,15 @@ var path54 = __toESM(require("path"));
21554
21664
  var os40 = __toESM(require("os"));
21555
21665
  var https7 = __toESM(require("https"));
21556
21666
  var http6 = __toESM(require("http"));
21557
- var import_zod3 = require("zod");
21558
- var historyRecordSchema = import_zod3.z.object({
21559
- type: import_zod3.z.string().optional(),
21560
- timestamp: import_zod3.z.union([import_zod3.z.string(), import_zod3.z.number()]).optional(),
21561
- uuid: import_zod3.z.string().optional(),
21562
- isMeta: import_zod3.z.boolean().optional(),
21563
- message: import_zod3.z.object({
21667
+ var import_zod2 = require("zod");
21668
+ var historyRecordSchema = import_zod2.z.object({
21669
+ type: import_zod2.z.string().optional(),
21670
+ timestamp: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
21671
+ uuid: import_zod2.z.string().optional(),
21672
+ isMeta: import_zod2.z.boolean().optional(),
21673
+ message: import_zod2.z.object({
21564
21674
  // Claude content is either a string or an array of typed blocks.
21565
- content: import_zod3.z.union([import_zod3.z.string(), import_zod3.z.array(import_zod3.z.unknown())]).optional()
21675
+ content: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.array(import_zod2.z.unknown())]).optional()
21566
21676
  }).passthrough().optional()
21567
21677
  }).passthrough();
21568
21678
  var API_BASE8 = resolveApiBaseUrl();
@@ -31816,7 +31926,7 @@ function checkChokidar() {
31816
31926
  }
31817
31927
  async function doctor(args2 = []) {
31818
31928
  const json = args2.includes("--json");
31819
- const cliVersion = true ? "2.53.4" : "0.0.0-dev";
31929
+ const cliVersion = true ? "2.55.1" : "0.0.0-dev";
31820
31930
  const apiBase2 = resolveApiBaseUrl();
31821
31931
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
31822
31932
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -32015,7 +32125,7 @@ async function completion(args2) {
32015
32125
  // src/commands/version.ts
32016
32126
  var import_picocolors15 = __toESM(require("picocolors"));
32017
32127
  function version2() {
32018
- const v = true ? "2.53.4" : "unknown";
32128
+ const v = true ? "2.55.1" : "unknown";
32019
32129
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
32020
32130
  }
32021
32131
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.53.4",
3
+ "version": "2.55.1",
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",
@@ -88,7 +88,7 @@
88
88
  "@beads/bd": "1.0.5"
89
89
  },
90
90
  "devDependencies": {
91
- "@codeagent/shared": "*",
91
+ "@codeam/shared": "*",
92
92
  "@types/node": "^22.0.0",
93
93
  "@types/qrcode-terminal": "^0.12.0",
94
94
  "@types/which": "^3.0.4",