codeam-cli 2.60.51 → 2.60.52

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/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ 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.60.51] — 2026-07-12
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Recover kimi ACP 'Session is closed' by re-establishing the session + retry (multi-turn)
12
+
13
+ ### Tests
14
+
15
+ - **cli:** Reusable ACP-provision smoke harness (per-agent real handshake auth check; automates CLAUDE.md Step 8)
16
+
7
17
  ## [2.60.50] — 2026-07-11
8
18
 
9
19
  ### Fixed
package/dist/index.js CHANGED
@@ -478,13 +478,27 @@ function headroomKindFor(agentId) {
478
478
  // ../../packages/shared/src/integrations/registry.ts
479
479
  var INTEGRATION_REGISTRY = {
480
480
  jira: {
481
+ // Registry id kept 'jira' for id-stability (existing LinkedIntegration rows
482
+ // stay valid — zero data migration); only the DISPLAY is "Atlassian". The
483
+ // one mcp-atlassian server serves BOTH Jira and Confluence, so the same
484
+ // entry now requests Confluence scopes too.
481
485
  id: "jira",
482
- name: "Jira",
486
+ name: "Atlassian",
483
487
  icon: "jira",
484
488
  enabled: true,
485
489
  auth: {
486
490
  kind: "oauth_redirect",
487
- scopes: ["read:jira-work", "write:jira-work", "offline_access"]
491
+ // Jira + Confluence 3LO granular scopes. The Confluence pair
492
+ // (read:confluence-content.all / write:confluence-content) is the set
493
+ // mcp-atlassian's own Authentication docs recommend for full read+write
494
+ // Confluence (matches its documented env scope string).
495
+ scopes: [
496
+ "read:jira-work",
497
+ "write:jira-work",
498
+ "read:confluence-content.all",
499
+ "write:confluence-content",
500
+ "offline_access"
501
+ ]
488
502
  },
489
503
  delivery: {
490
504
  mcp: {
@@ -5733,7 +5747,7 @@ function readAnonId() {
5733
5747
  }
5734
5748
  function superProperties() {
5735
5749
  return {
5736
- cliVersion: true ? "2.60.51" : "0.0.0-dev",
5750
+ cliVersion: true ? "2.60.52" : "0.0.0-dev",
5737
5751
  nodeVersion: process.version,
5738
5752
  platform: process.platform,
5739
5753
  arch: process.arch,
@@ -5914,7 +5928,7 @@ var os4 = __toESM(require("os"));
5914
5928
  // package.json
5915
5929
  var package_default = {
5916
5930
  name: "codeam-cli",
5917
- version: "2.60.51",
5931
+ version: "2.60.52",
5918
5932
  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.",
5919
5933
  type: "commonjs",
5920
5934
  main: "dist/index.js",
@@ -7023,7 +7037,7 @@ var CommandRelayService = class {
7023
7037
  // fresh + clear the "CLI update available" banner after a self-update
7024
7038
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
7025
7039
  // pair/reconnect). Older backends ignore the extra field.
7026
- ..."2.60.51" ? { ideVersion: "2.60.51" } : {}
7040
+ ..."2.60.52" ? { ideVersion: "2.60.52" } : {}
7027
7041
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
7028
7042
  }
7029
7043
  /**
@@ -17592,7 +17606,7 @@ async function autoUpgradeBeforeCriticalCommand() {
17592
17606
  if (process.env.NODE_ENV === "test") return;
17593
17607
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17594
17608
  if (process.env.CI) return;
17595
- const current = true ? "2.60.51" : null;
17609
+ const current = true ? "2.60.52" : null;
17596
17610
  if (!current) return;
17597
17611
  const cache = readCache();
17598
17612
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -17609,7 +17623,7 @@ function checkForUpdates() {
17609
17623
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17610
17624
  if (process.env.CI) return;
17611
17625
  if (!process.stdout.isTTY) return;
17612
- const current = true ? "2.60.51" : null;
17626
+ const current = true ? "2.60.52" : null;
17613
17627
  if (!current) return;
17614
17628
  const cache = readCache();
17615
17629
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -17629,7 +17643,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
17629
17643
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
17630
17644
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
17631
17645
  function currentCliVersion() {
17632
- return true ? "2.60.51" : null;
17646
+ return true ? "2.60.52" : null;
17633
17647
  }
17634
17648
  function runCmd(cmd, args2, timeoutMs) {
17635
17649
  return new Promise((resolve7) => {
@@ -27136,13 +27150,22 @@ var AcpClient = class {
27136
27150
  * on the SAME still-running adapter process, so the pending prompt can be
27137
27151
  * retried. Prefers `session/load` (resume) when the agent advertised
27138
27152
  * `loadSession` — proven live to re-open kimi's closed session with the
27139
- * conversation context intact, and kimi's `session/load` emits NO history
27140
- * replay as `session/update`, so no streaming pollution (unlike the baton's
27141
- * Claude load, which is why this needs no `beginLoadReplay` bracket). Calls
27142
- * `connection.loadSession` DIRECTLY the public {@link loadSession} skips a
27143
- * same-id load (a Claude relaunch-wedge guard), which is exactly the load we
27144
- * MUST perform here (the closed id IS the active id). Falls back to a fresh
27145
- * `session/new` (context lost) only when the agent can't resume.
27153
+ * conversation context intact. Calls `connection.loadSession` DIRECTLY the
27154
+ * public {@link loadSession} skips a same-id load (a Claude relaunch-wedge
27155
+ * guard), which is exactly the load we MUST perform here (the closed id IS the
27156
+ * active id). Falls back to a fresh `session/new` (context lost) only when the
27157
+ * agent can't resume.
27158
+ *
27159
+ * ⚠️ kimi's `session/load` (0.23.6, live-verified) REPLAYS the ENTIRE prior
27160
+ * conversation as `session/update` notifications BEFORE it resolves — the
27161
+ * earlier assumption that it emitted no replay was wrong and caused a P0: the
27162
+ * replayed prior-turn text was published as live chunks and prepended to the
27163
+ * recovered turn's real reply. So the load is bracketed with
27164
+ * `beginLoadReplay()/endLoadReplay()` (wired by the runner to StreamingState)
27165
+ * so those replay updates are swallowed, exactly like the baton's load. The
27166
+ * retried `session/prompt` (after the load resolves) then streams the REAL new
27167
+ * reply cleanly. The `finally` guarantees the guard clears even if the load
27168
+ * throws, so a failed recovery can't wedge streaming off.
27146
27169
  */
27147
27170
  async reestablishSession() {
27148
27171
  if (!this.connection) throw new Error("AcpClient.reestablishSession: no connection");
@@ -27151,7 +27174,12 @@ var AcpClient = class {
27151
27174
  const closedSid = this.sessionId;
27152
27175
  if (this.supportsLoadSession && closedSid) {
27153
27176
  log.info("acpClient", `reestablish \u2192 loadSession(resume) sid=${closedSid.slice(0, 8)}`);
27154
- await this.connection.loadSession({ sessionId: closedSid, cwd, mcpServers });
27177
+ this.opts.beginLoadReplay?.();
27178
+ try {
27179
+ await this.connection.loadSession({ sessionId: closedSid, cwd, mcpServers });
27180
+ } finally {
27181
+ this.opts.endLoadReplay?.();
27182
+ }
27155
27183
  log.info("acpClient", "reestablish \u2190 loadSession ok");
27156
27184
  return;
27157
27185
  }
@@ -30048,6 +30076,13 @@ async function runAcpSession(opts) {
30048
30076
  streaming.append(delta);
30049
30077
  }
30050
30078
  },
30079
+ // kimi post-turn "Session is closed" recovery brackets its `session/load`
30080
+ // with these so the conversation history the load REPLAYS as session/update
30081
+ // notifications is swallowed by StreamingState instead of published as live
30082
+ // chunks (which prepended a prior turn's text to the recovered reply — P0).
30083
+ // Same guard the baton uses; the happy path never calls loadSession.
30084
+ beginLoadReplay: () => streaming.beginLoadReplay(),
30085
+ endLoadReplay: () => streaming.endLoadReplay(),
30051
30086
  onRequestPermission: async (request) => {
30052
30087
  if (opts.autoApprovePermissions) {
30053
30088
  const allow = pickAllowOption(request.options);
@@ -31845,6 +31880,14 @@ async function runBatonSession(opts) {
31845
31880
  onSessionUpdate: (notification) => {
31846
31881
  for (const delta of mapSessionUpdate(notification)) streaming.append(delta);
31847
31882
  },
31883
+ // Swallow the ACP session/load history replay during the kimi "Session is
31884
+ // closed" recovery (AcpClient.reestablishSession) — kimi 0.23.6 replays the
31885
+ // whole conversation as session/update, which would otherwise prepend a
31886
+ // prior turn's text to the recovered reply on the baton's mobile stream too
31887
+ // (same leak the plain ACP runner wires at runner.ts). Same `streaming`
31888
+ // instance the baton's own AcpDriver.start bracketing uses.
31889
+ beginLoadReplay: () => streaming.beginLoadReplay(),
31890
+ endLoadReplay: () => streaming.endLoadReplay(),
31848
31891
  onRequestPermission: async (request) => {
31849
31892
  const { event, optionIdByLabel } = mapPermissionRequest(request);
31850
31893
  await publisher.publishAwaitingAnswer(event);
@@ -34928,7 +34971,7 @@ function checkChokidar() {
34928
34971
  }
34929
34972
  async function doctor(args2 = []) {
34930
34973
  const json = args2.includes("--json");
34931
- const cliVersion = true ? "2.60.51" : "0.0.0-dev";
34974
+ const cliVersion = true ? "2.60.52" : "0.0.0-dev";
34932
34975
  const apiBase2 = resolveApiBaseUrl();
34933
34976
  const diagnosticId = (0, import_node_crypto12.randomUUID)();
34934
34977
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -35405,7 +35448,7 @@ async function mcpRun(args2) {
35405
35448
  // src/commands/version.ts
35406
35449
  var import_picocolors15 = __toESM(require("picocolors"));
35407
35450
  function version2() {
35408
- const v = true ? "2.60.51" : "unknown";
35451
+ const v = true ? "2.60.52" : "unknown";
35409
35452
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
35410
35453
  }
35411
35454
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.60.51",
3
+ "version": "2.60.52",
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",