codeam-cli 2.65.11 → 2.65.12
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 +36 -0
- package/dist/index.js +250 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ 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.65.11] — 2026-08-19
|
|
8
|
+
|
|
9
|
+
### Documentation
|
|
10
|
+
|
|
11
|
+
- **cli:** Record the 2026-08-18 baton hand-off + goodbye-heartbeat invariants
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **cli:** Take-control never wedges on "Switching…" again
|
|
16
|
+
- **cli:** Say goodbye before exiting so mobile stops showing ONLINE
|
|
17
|
+
- **cli:** LOCAL_DRIVE mirrors the transcript only — no raw TUI bytes to mobile
|
|
18
|
+
|
|
19
|
+
### Tests
|
|
20
|
+
|
|
21
|
+
- **cli:** Real baton local integration test (take-control before first turn, handback, goodbye heartbeat)
|
|
22
|
+
|
|
23
|
+
## [2.65.10] — 2026-08-19
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **cli:** Codespace sessions report the user's repo, not the shared wrapper hostname
|
|
28
|
+
- **cli:** Dedupe the native ACP model list and validate model/tier at the wire boundary
|
|
29
|
+
- **cli:** Welcome card greets first-time users correctly and never shows a raw internal path
|
|
30
|
+
|
|
31
|
+
## [2.65.9] — 2026-08-19
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **cli:** Report an ineligible-tier credential to the backend on startup failure
|
|
36
|
+
|
|
37
|
+
## [2.65.8] — 2026-08-18
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **cli:** Baton re-affirms its state on the relay heartbeat so the 1h backend snapshot never expires under a live session
|
|
42
|
+
|
|
7
43
|
## [2.65.7] — 2026-08-15
|
|
8
44
|
|
|
9
45
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -8079,7 +8079,7 @@ function readAnonId() {
|
|
|
8079
8079
|
}
|
|
8080
8080
|
function superProperties() {
|
|
8081
8081
|
return {
|
|
8082
|
-
cliVersion: true ? "2.65.
|
|
8082
|
+
cliVersion: true ? "2.65.12" : "0.0.0-dev",
|
|
8083
8083
|
nodeVersion: process.version,
|
|
8084
8084
|
platform: process.platform,
|
|
8085
8085
|
arch: process.arch,
|
|
@@ -8321,7 +8321,7 @@ var http = __toESM(require("http"));
|
|
|
8321
8321
|
// package.json
|
|
8322
8322
|
var package_default = {
|
|
8323
8323
|
name: "codeam-cli",
|
|
8324
|
-
version: "2.65.
|
|
8324
|
+
version: "2.65.12",
|
|
8325
8325
|
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.",
|
|
8326
8326
|
type: "commonjs",
|
|
8327
8327
|
main: "dist/index.js",
|
|
@@ -9842,7 +9842,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9842
9842
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9843
9843
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9844
9844
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9845
|
-
..."2.65.
|
|
9845
|
+
..."2.65.12" ? { ideVersion: "2.65.12" } : {}
|
|
9846
9846
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9847
9847
|
}
|
|
9848
9848
|
/**
|
|
@@ -15283,6 +15283,7 @@ function parseHistoryFile(filePath) {
|
|
|
15283
15283
|
if (!msg) continue;
|
|
15284
15284
|
const text = extractText(msg["content"]).trim();
|
|
15285
15285
|
if (!text) continue;
|
|
15286
|
+
if (type === "user" && isLocalCommandEcho(text)) continue;
|
|
15286
15287
|
const ts = r["timestamp"];
|
|
15287
15288
|
const timestamp = typeof ts === "string" ? ts : typeof ts === "number" ? new Date(ts).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
|
|
15288
15289
|
const uuid = typeof r["uuid"] === "string" ? r["uuid"] : `${Date.now()}-${Math.random()}`;
|
|
@@ -15347,6 +15348,175 @@ function listResumableSessions(cwd) {
|
|
|
15347
15348
|
out2.sort((a, b) => b.timestamp - a.timestamp);
|
|
15348
15349
|
return out2;
|
|
15349
15350
|
}
|
|
15351
|
+
function isLocalCommandEcho(text) {
|
|
15352
|
+
return /^\s*<(command-name|local-command-[a-z]+)>/.test(text);
|
|
15353
|
+
}
|
|
15354
|
+
var CLEAR_COMMAND_ECHO = "<command-name>/clear</command-name>";
|
|
15355
|
+
var SWITCH_PROBE_BYTES = 256 * 1024;
|
|
15356
|
+
function isClearedConversationFile(filePath) {
|
|
15357
|
+
let fd;
|
|
15358
|
+
try {
|
|
15359
|
+
fd = fs17.openSync(filePath, "r");
|
|
15360
|
+
} catch {
|
|
15361
|
+
return false;
|
|
15362
|
+
}
|
|
15363
|
+
let raw;
|
|
15364
|
+
try {
|
|
15365
|
+
const buf = Buffer.alloc(SWITCH_PROBE_BYTES);
|
|
15366
|
+
const n = fs17.readSync(fd, buf, 0, SWITCH_PROBE_BYTES, 0);
|
|
15367
|
+
raw = buf.toString("utf8", 0, n);
|
|
15368
|
+
} catch {
|
|
15369
|
+
return false;
|
|
15370
|
+
} finally {
|
|
15371
|
+
fs17.closeSync(fd);
|
|
15372
|
+
}
|
|
15373
|
+
if (!raw.includes(CLEAR_COMMAND_ECHO) && !raw.includes("SessionStart:clear")) return false;
|
|
15374
|
+
for (const line of raw.split("\n")) {
|
|
15375
|
+
if (!line.trim()) continue;
|
|
15376
|
+
let r;
|
|
15377
|
+
try {
|
|
15378
|
+
r = JSON.parse(line);
|
|
15379
|
+
} catch {
|
|
15380
|
+
continue;
|
|
15381
|
+
}
|
|
15382
|
+
if (r["type"] === "user" && !r["isMeta"]) {
|
|
15383
|
+
const msg = r["message"];
|
|
15384
|
+
if (extractText(msg?.["content"]).trimStart().startsWith(CLEAR_COMMAND_ECHO)) return true;
|
|
15385
|
+
}
|
|
15386
|
+
if (r["type"] === "attachment") {
|
|
15387
|
+
const att = r["attachment"];
|
|
15388
|
+
if (typeof att?.["hookName"] === "string" && att["hookName"].startsWith("SessionStart:clear")) {
|
|
15389
|
+
return true;
|
|
15390
|
+
}
|
|
15391
|
+
}
|
|
15392
|
+
}
|
|
15393
|
+
return false;
|
|
15394
|
+
}
|
|
15395
|
+
function appendedTailHasResumeMarker(filePath, fromOffset) {
|
|
15396
|
+
let fd;
|
|
15397
|
+
try {
|
|
15398
|
+
fd = fs17.openSync(filePath, "r");
|
|
15399
|
+
} catch {
|
|
15400
|
+
return false;
|
|
15401
|
+
}
|
|
15402
|
+
try {
|
|
15403
|
+
const size = fs17.fstatSync(fd).size;
|
|
15404
|
+
const len = Math.min(Math.max(size - fromOffset, 0), SWITCH_PROBE_BYTES);
|
|
15405
|
+
if (len <= 0) return false;
|
|
15406
|
+
const buf = Buffer.alloc(len);
|
|
15407
|
+
const n = fs17.readSync(fd, buf, 0, len, fromOffset);
|
|
15408
|
+
const raw = buf.toString("utf8", 0, n);
|
|
15409
|
+
if (!raw.includes('"last-prompt"')) return false;
|
|
15410
|
+
for (const line of raw.split("\n")) {
|
|
15411
|
+
if (!line.trim()) continue;
|
|
15412
|
+
try {
|
|
15413
|
+
if (JSON.parse(line)["type"] === "last-prompt") return true;
|
|
15414
|
+
} catch {
|
|
15415
|
+
}
|
|
15416
|
+
}
|
|
15417
|
+
return false;
|
|
15418
|
+
} catch {
|
|
15419
|
+
return false;
|
|
15420
|
+
} finally {
|
|
15421
|
+
fs17.closeSync(fd);
|
|
15422
|
+
}
|
|
15423
|
+
}
|
|
15424
|
+
function watchConversationSwitch(cwd, opts, onSwitch, projectsRoot) {
|
|
15425
|
+
const root = projectsRoot ?? path21.join(os15.homedir(), ".claude", "projects");
|
|
15426
|
+
let currentId = opts.currentId;
|
|
15427
|
+
const baseline = /* @__PURE__ */ new Map();
|
|
15428
|
+
const unclassified = /* @__PURE__ */ new Map();
|
|
15429
|
+
let dirWatcher = null;
|
|
15430
|
+
let rootWatcher = null;
|
|
15431
|
+
let closed = false;
|
|
15432
|
+
const fileSize = (file) => {
|
|
15433
|
+
try {
|
|
15434
|
+
return fs17.statSync(file).size;
|
|
15435
|
+
} catch {
|
|
15436
|
+
return null;
|
|
15437
|
+
}
|
|
15438
|
+
};
|
|
15439
|
+
const switchTo = (dir, id, kind) => {
|
|
15440
|
+
const leaving = fileSize(path21.join(dir, `${currentId}.jsonl`));
|
|
15441
|
+
if (leaving !== null) baseline.set(currentId, leaving);
|
|
15442
|
+
baseline.delete(id);
|
|
15443
|
+
unclassified.delete(id);
|
|
15444
|
+
currentId = id;
|
|
15445
|
+
onSwitch(id, { kind });
|
|
15446
|
+
};
|
|
15447
|
+
const scan = (dir) => {
|
|
15448
|
+
if (closed) return;
|
|
15449
|
+
let entries;
|
|
15450
|
+
try {
|
|
15451
|
+
entries = fs17.readdirSync(dir);
|
|
15452
|
+
} catch {
|
|
15453
|
+
return;
|
|
15454
|
+
}
|
|
15455
|
+
for (const name of entries) {
|
|
15456
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
15457
|
+
const id = name.slice(0, -".jsonl".length);
|
|
15458
|
+
if (id === currentId) continue;
|
|
15459
|
+
const file = path21.join(dir, name);
|
|
15460
|
+
const size = fileSize(file);
|
|
15461
|
+
if (size === null) continue;
|
|
15462
|
+
const known = baseline.get(id);
|
|
15463
|
+
if (known !== void 0) {
|
|
15464
|
+
if (size > known && appendedTailHasResumeMarker(file, known)) {
|
|
15465
|
+
switchTo(dir, id, "resumed");
|
|
15466
|
+
return;
|
|
15467
|
+
}
|
|
15468
|
+
continue;
|
|
15469
|
+
}
|
|
15470
|
+
if (unclassified.get(id) === size) continue;
|
|
15471
|
+
unclassified.set(id, size);
|
|
15472
|
+
if (isClearedConversationFile(file)) {
|
|
15473
|
+
switchTo(dir, id, "new");
|
|
15474
|
+
return;
|
|
15475
|
+
}
|
|
15476
|
+
}
|
|
15477
|
+
};
|
|
15478
|
+
const attachDir = (initial) => {
|
|
15479
|
+
const dir = resolveHistoryDir(cwd, root);
|
|
15480
|
+
if (!dir) return false;
|
|
15481
|
+
rootWatcher?.close();
|
|
15482
|
+
rootWatcher = null;
|
|
15483
|
+
if (initial) {
|
|
15484
|
+
try {
|
|
15485
|
+
for (const name of fs17.readdirSync(dir)) {
|
|
15486
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
15487
|
+
const id = name.slice(0, -".jsonl".length);
|
|
15488
|
+
if (id === currentId) continue;
|
|
15489
|
+
const size = fileSize(path21.join(dir, name));
|
|
15490
|
+
if (size !== null) baseline.set(id, size);
|
|
15491
|
+
}
|
|
15492
|
+
} catch {
|
|
15493
|
+
}
|
|
15494
|
+
}
|
|
15495
|
+
try {
|
|
15496
|
+
dirWatcher = fs17.watch(dir, { persistent: false }, () => scan(dir));
|
|
15497
|
+
} catch {
|
|
15498
|
+
return false;
|
|
15499
|
+
}
|
|
15500
|
+
scan(dir);
|
|
15501
|
+
return true;
|
|
15502
|
+
};
|
|
15503
|
+
if (!attachDir(true)) {
|
|
15504
|
+
try {
|
|
15505
|
+
fs17.mkdirSync(root, { recursive: true });
|
|
15506
|
+
rootWatcher = fs17.watch(root, { persistent: false }, () => {
|
|
15507
|
+
if (!closed && !dirWatcher) attachDir(false);
|
|
15508
|
+
});
|
|
15509
|
+
} catch {
|
|
15510
|
+
}
|
|
15511
|
+
}
|
|
15512
|
+
return () => {
|
|
15513
|
+
closed = true;
|
|
15514
|
+
dirWatcher?.close();
|
|
15515
|
+
rootWatcher?.close();
|
|
15516
|
+
dirWatcher = null;
|
|
15517
|
+
rootWatcher = null;
|
|
15518
|
+
};
|
|
15519
|
+
}
|
|
15350
15520
|
|
|
15351
15521
|
// src/agents/claude/runtime.ts
|
|
15352
15522
|
var ClaudeRuntimeStrategy = class {
|
|
@@ -15436,6 +15606,14 @@ var ClaudeRuntimeStrategy = class {
|
|
|
15436
15606
|
parseHistoryFile(filePath) {
|
|
15437
15607
|
return parseHistoryFile(filePath);
|
|
15438
15608
|
}
|
|
15609
|
+
/**
|
|
15610
|
+
* Baton: follow the native TUI through `/clear` (new conversation id, new
|
|
15611
|
+
* JSONL) and `/resume` (an existing one) — see
|
|
15612
|
+
* {@link history.watchConversationSwitch}.
|
|
15613
|
+
*/
|
|
15614
|
+
watchConversationSwitch(cwd, opts, onSwitch) {
|
|
15615
|
+
return watchConversationSwitch(cwd, opts, onSwitch);
|
|
15616
|
+
}
|
|
15439
15617
|
getCurrentUsage(historyDir) {
|
|
15440
15618
|
return getCurrentUsage(historyDir);
|
|
15441
15619
|
}
|
|
@@ -18137,25 +18315,27 @@ async function waitForCommandOnPath(cmd, opts = {}) {
|
|
|
18137
18315
|
}
|
|
18138
18316
|
return check();
|
|
18139
18317
|
}
|
|
18140
|
-
function augmentUserLocalBinPaths() {
|
|
18141
|
-
const
|
|
18318
|
+
function augmentUserLocalBinPaths(deps = {}) {
|
|
18319
|
+
const env = deps.env ?? process.env;
|
|
18320
|
+
const home = deps.homedir ?? import_os4.default.homedir();
|
|
18321
|
+
const p2 = deps.pathApi ?? import_path4.default;
|
|
18142
18322
|
const candidates = [
|
|
18143
18323
|
// XDG-style per-user bin — npm's default global-prefix bin dir on most
|
|
18144
18324
|
// Linux setups (`npm config set prefix ~/.local` or an nvm-less
|
|
18145
18325
|
// per-user npm), and where curl-based agent installers commonly land.
|
|
18146
|
-
|
|
18326
|
+
p2.join(home, ".local", "bin"),
|
|
18147
18327
|
// Common explicit npm global-prefix conventions seen in the wild
|
|
18148
18328
|
// (`npm config set prefix ~/.npm-global`, and Debian/Fedora's
|
|
18149
18329
|
// `~/.local/share/npm` layout for `npm config set prefix
|
|
18150
18330
|
// ~/.local/share/npm`).
|
|
18151
|
-
|
|
18152
|
-
|
|
18331
|
+
p2.join(home, ".npm-global", "bin"),
|
|
18332
|
+
p2.join(home, ".local", "share", "npm", "bin")
|
|
18153
18333
|
];
|
|
18154
|
-
const parts = (
|
|
18334
|
+
const parts = (env.PATH ?? "").split(p2.delimiter).filter((s) => s.length > 0);
|
|
18155
18335
|
const existing = new Set(parts);
|
|
18156
18336
|
const additions = candidates.filter((dir) => !existing.has(dir));
|
|
18157
18337
|
if (additions.length === 0) return;
|
|
18158
|
-
|
|
18338
|
+
env.PATH = [...additions, ...parts].join(p2.delimiter);
|
|
18159
18339
|
}
|
|
18160
18340
|
function resolveCursorAgentBinary(deps = {}) {
|
|
18161
18341
|
const existsSync29 = deps.existsSync ?? import_fs.default.existsSync;
|
|
@@ -21786,7 +21966,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
21786
21966
|
if (process.env.NODE_ENV === "test") return;
|
|
21787
21967
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
21788
21968
|
if (process.env.CI) return;
|
|
21789
|
-
const current2 = true ? "2.65.
|
|
21969
|
+
const current2 = true ? "2.65.12" : null;
|
|
21790
21970
|
if (!current2) return;
|
|
21791
21971
|
const cache = readCache();
|
|
21792
21972
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -21803,7 +21983,7 @@ function checkForUpdates() {
|
|
|
21803
21983
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
21804
21984
|
if (process.env.CI) return;
|
|
21805
21985
|
if (!process.stdout.isTTY) return;
|
|
21806
|
-
const current2 = true ? "2.65.
|
|
21986
|
+
const current2 = true ? "2.65.12" : null;
|
|
21807
21987
|
if (!current2) return;
|
|
21808
21988
|
const cache = readCache();
|
|
21809
21989
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -21823,7 +22003,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
|
21823
22003
|
var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
21824
22004
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
21825
22005
|
function currentCliVersion() {
|
|
21826
|
-
return true ? "2.65.
|
|
22006
|
+
return true ? "2.65.12" : null;
|
|
21827
22007
|
}
|
|
21828
22008
|
function runCmd(cmd, args2, timeoutMs) {
|
|
21829
22009
|
return new Promise((resolve10) => {
|
|
@@ -41268,6 +41448,21 @@ var BatonController = class _BatonController {
|
|
|
41268
41448
|
this._conversationId = conversationId;
|
|
41269
41449
|
this.setState("LOCAL_DRIVE");
|
|
41270
41450
|
}
|
|
41451
|
+
/**
|
|
41452
|
+
* The native TUI SWITCHED conversation while driving (Claude `/clear` →
|
|
41453
|
+
* a new id, `/resume` → an existing one): the process and the pairing are
|
|
41454
|
+
* unchanged, but the conversation the user is in has another id. Re-point
|
|
41455
|
+
* the baton at it so the read-only mirror follows that transcript and a
|
|
41456
|
+
* later Take Control resumes THAT conversation instead of the abandoned one. Guarded to LOCAL_DRIVE — only
|
|
41457
|
+
* the terminal can switch its own conversation, and during a hand-off /
|
|
41458
|
+
* MOBILE_DRIVE the native watcher is torn down anyway (`stop()`). Same id →
|
|
41459
|
+
* no-op. Re-publishes LOCAL_DRIVE so the mirror re-arms + mobile learns the id.
|
|
41460
|
+
*/
|
|
41461
|
+
switchConversation(conversationId) {
|
|
41462
|
+
if (this._state !== "LOCAL_DRIVE" || this._conversationId === conversationId) return;
|
|
41463
|
+
this._conversationId = conversationId;
|
|
41464
|
+
this.setState("LOCAL_DRIVE");
|
|
41465
|
+
}
|
|
41271
41466
|
async takeControl() {
|
|
41272
41467
|
await this.switchDriver(
|
|
41273
41468
|
"LOCAL_DRIVE",
|
|
@@ -41419,18 +41614,25 @@ var NativeTuiDriver = class _NativeTuiDriver {
|
|
|
41419
41614
|
static QUICK_DISCOVER_MS = 8e3;
|
|
41420
41615
|
outputSvc;
|
|
41421
41616
|
historySvc;
|
|
41617
|
+
/** Unsubscribe for the runtime's conversation-switch watcher (armed per
|
|
41618
|
+
* `start()`, torn down in `stop()`), or null when not armed. */
|
|
41619
|
+
unwatchSwitch = null;
|
|
41422
41620
|
setKeepAlive;
|
|
41423
41621
|
keepAliveCtx;
|
|
41424
41622
|
async start(resumeId) {
|
|
41425
41623
|
if (resumeId !== void 0) {
|
|
41426
41624
|
await this.deps.runtime.syncTranscriptForNativeResume?.(this.deps.opts.cwd, resumeId);
|
|
41427
41625
|
await this.agent.restart(resumeId, false);
|
|
41626
|
+
this.armSwitchWatch(resumeId);
|
|
41428
41627
|
return resumeId;
|
|
41429
41628
|
}
|
|
41430
41629
|
const spawnedAt = this.now();
|
|
41431
41630
|
await this.agent.spawn();
|
|
41432
41631
|
const preMinted = this.agent.spawnedSessionId;
|
|
41433
|
-
if (preMinted)
|
|
41632
|
+
if (preMinted) {
|
|
41633
|
+
this.armSwitchWatch(preMinted);
|
|
41634
|
+
return preMinted;
|
|
41635
|
+
}
|
|
41434
41636
|
const discover = this.deps.runtime.discoverSessionId;
|
|
41435
41637
|
if (!discover) {
|
|
41436
41638
|
throw new Error("NativeTuiDriver: agent did not expose a session id after spawn");
|
|
@@ -41446,7 +41648,26 @@ var NativeTuiDriver = class _NativeTuiDriver {
|
|
|
41446
41648
|
});
|
|
41447
41649
|
return null;
|
|
41448
41650
|
}
|
|
41651
|
+
/**
|
|
41652
|
+
* Follow the native TUI if it switches conversation mid-session (Claude
|
|
41653
|
+
* `/clear` / `/resume`): the runtime's event-driven watcher reports the id
|
|
41654
|
+
* now being driven, we point the PTY-side history at it and tell the
|
|
41655
|
+
* controller to rebind. Armed once per `start()` (fresh or resume) on the
|
|
41656
|
+
* id the TUI is driving; inert for agents without the hook.
|
|
41657
|
+
*/
|
|
41658
|
+
armSwitchWatch(currentId) {
|
|
41659
|
+
this.unwatchSwitch?.();
|
|
41660
|
+
this.unwatchSwitch = null;
|
|
41661
|
+
const watch3 = this.deps.runtime.watchConversationSwitch;
|
|
41662
|
+
if (!watch3) return;
|
|
41663
|
+
this.unwatchSwitch = watch3(this.deps.opts.cwd, { currentId }, (id) => {
|
|
41664
|
+
this.historySvc.setCurrentConversationId(id);
|
|
41665
|
+
this.deps.onConversationSwitch?.(id);
|
|
41666
|
+
});
|
|
41667
|
+
}
|
|
41449
41668
|
async stop() {
|
|
41669
|
+
this.unwatchSwitch?.();
|
|
41670
|
+
this.unwatchSwitch = null;
|
|
41450
41671
|
this.agent.kill();
|
|
41451
41672
|
parkTerminalForReadonly();
|
|
41452
41673
|
}
|
|
@@ -41693,8 +41914,8 @@ var TranscriptMirror = class {
|
|
|
41693
41914
|
if (!file) return false;
|
|
41694
41915
|
this.attached = true;
|
|
41695
41916
|
this.emit(file);
|
|
41696
|
-
const
|
|
41697
|
-
this.unwatch =
|
|
41917
|
+
const watch3 = this.deps.watch ?? defaultWatch;
|
|
41918
|
+
this.unwatch = watch3(file, () => this.emit(file));
|
|
41698
41919
|
return true;
|
|
41699
41920
|
}
|
|
41700
41921
|
clearPoll() {
|
|
@@ -41909,10 +42130,17 @@ async function runBatonSession(opts) {
|
|
|
41909
42130
|
// calls this once the user's first terminal turn creates the transcript.
|
|
41910
42131
|
// Safe forward-ref — `controller` is initialised long before `begin()` runs,
|
|
41911
42132
|
// and onLateBind only fires from inside `begin()`'s spawn.
|
|
41912
|
-
onLateBind: (id) => controller.rebindConversation(id)
|
|
42133
|
+
onLateBind: (id) => controller.rebindConversation(id),
|
|
42134
|
+
// The native TUI switched conversation mid-drive (Claude `/clear` mints a
|
|
42135
|
+
// new id + JSONL; `/resume` re-opens an existing one): rebind so the mirror
|
|
42136
|
+
// follows that transcript and a later Take Control resumes THAT
|
|
42137
|
+
// conversation. Owner report 2026-08-18: after `/clear` (+`/rename`) the
|
|
42138
|
+
// mobile went silent — the mirror kept tailing the abandoned file.
|
|
42139
|
+
onConversationSwitch: (id) => controller.switchConversation(id)
|
|
41913
42140
|
});
|
|
41914
42141
|
let mirror = null;
|
|
41915
42142
|
let firstLocalDrive = true;
|
|
42143
|
+
let lastPublished = null;
|
|
41916
42144
|
const startMirror = (conversationId, fresh) => {
|
|
41917
42145
|
mirror?.stop();
|
|
41918
42146
|
mirror = new TranscriptMirror({
|
|
@@ -41945,11 +42173,13 @@ async function runBatonSession(opts) {
|
|
|
41945
42173
|
publishState: (state, driver, conversationId) => {
|
|
41946
42174
|
publishBatonState(state, driver, conversationId);
|
|
41947
42175
|
if (state === "LOCAL_DRIVE" && conversationId) {
|
|
41948
|
-
|
|
42176
|
+
const switched = lastPublished === "LOCAL_DRIVE";
|
|
42177
|
+
startMirror(conversationId, firstLocalDrive || switched);
|
|
41949
42178
|
firstLocalDrive = false;
|
|
41950
42179
|
} else if (state !== "LOCAL_DRIVE") {
|
|
41951
42180
|
mirror?.stop();
|
|
41952
42181
|
}
|
|
42182
|
+
lastPublished = state;
|
|
41953
42183
|
}
|
|
41954
42184
|
});
|
|
41955
42185
|
const dispatchActive = (cmd) => controller.activeSessionDriver.dispatch(cmd);
|
|
@@ -45024,7 +45254,7 @@ function checkChokidar() {
|
|
|
45024
45254
|
}
|
|
45025
45255
|
async function doctor(args2 = []) {
|
|
45026
45256
|
const json = args2.includes("--json");
|
|
45027
|
-
const cliVersion = true ? "2.65.
|
|
45257
|
+
const cliVersion = true ? "2.65.12" : "0.0.0-dev";
|
|
45028
45258
|
const apiBase2 = resolveApiBaseUrl();
|
|
45029
45259
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
45030
45260
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -45415,7 +45645,7 @@ async function mcpRun(args2) {
|
|
|
45415
45645
|
// src/commands/version.ts
|
|
45416
45646
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
45417
45647
|
function version2() {
|
|
45418
|
-
const v = true ? "2.65.
|
|
45648
|
+
const v = true ? "2.65.12" : "unknown";
|
|
45419
45649
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
45420
45650
|
}
|
|
45421
45651
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.65.
|
|
3
|
+
"version": "2.65.12",
|
|
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",
|