codeam-cli 2.42.0 → 2.43.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.
- package/CHANGELOG.md +30 -0
- package/dist/index.js +131 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,36 @@ 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.43.0] — 2026-06-24
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Classify the 1M-context usage-credits error
|
|
12
|
+
- **cli:** Persist per-session disable1mContext flag
|
|
13
|
+
- **cli:** AcpClient accepts extraEnv for the adapter spawn
|
|
14
|
+
- **cli:** OneMContextRecovery offer + decision helper
|
|
15
|
+
- **cli:** OneMContextRecovery offer + decision helper + DI recovery factory
|
|
16
|
+
- **cli:** On-demand 1M-context disable recovery — offer action, re-spawn ACP with the knob, auto-rerun
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **cli:** Move 1M-context classifier into oneMContextRecovery (break runner import cycle)
|
|
21
|
+
|
|
22
|
+
### Documentation
|
|
23
|
+
|
|
24
|
+
- **meta:** Record agent-failure-messaging + heartbeat + credential rules (AGENTS.md + CLAUDE.md)
|
|
25
|
+
|
|
26
|
+
## [2.42.0] — 2026-06-24
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **cli:** Confirm provider outage via status page on turn stall/failure (catch-all)
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **cli:** Make agent-failure messaging honest — outage only on real provider error, re-auth on auth-notice replies
|
|
35
|
+
- **cli:** Keep the heartbeat punctual by detecting the git branch off the hot path
|
|
36
|
+
|
|
7
37
|
## [2.41.0] — 2026-06-24
|
|
8
38
|
|
|
9
39
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -476,6 +476,13 @@ function makeConfig(baseDir) {
|
|
|
476
476
|
matches.sort((a, b) => b.pairedAt - a.pairedAt);
|
|
477
477
|
return matches[0];
|
|
478
478
|
}
|
|
479
|
+
function setDisable1mContext2(pluginId, value) {
|
|
480
|
+
const c2 = load();
|
|
481
|
+
const s = c2.sessions.find((x) => x.pluginId === pluginId);
|
|
482
|
+
if (!s) return;
|
|
483
|
+
s.disable1mContext = value;
|
|
484
|
+
save(c2);
|
|
485
|
+
}
|
|
479
486
|
function clearAll2() {
|
|
480
487
|
try {
|
|
481
488
|
fs.unlinkSync(file);
|
|
@@ -488,7 +495,7 @@ function makeConfig(baseDir) {
|
|
|
488
495
|
function loadCliConfig2() {
|
|
489
496
|
return load();
|
|
490
497
|
}
|
|
491
|
-
return { getConfig: getConfig2, ensurePluginId: ensurePluginId2, addSession: addSession2, removeSession: removeSession2, setActiveSession: setActiveSession2, getActiveSession: getActiveSession2, getActiveSessionForAgent: getActiveSessionForAgent2, clearAll: clearAll2, saveCliConfig: saveCliConfig2, loadCliConfig: loadCliConfig2 };
|
|
498
|
+
return { getConfig: getConfig2, ensurePluginId: ensurePluginId2, addSession: addSession2, removeSession: removeSession2, setActiveSession: setActiveSession2, getActiveSession: getActiveSession2, getActiveSessionForAgent: getActiveSessionForAgent2, setDisable1mContext: setDisable1mContext2, clearAll: clearAll2, saveCliConfig: saveCliConfig2, loadCliConfig: loadCliConfig2 };
|
|
492
499
|
}
|
|
493
500
|
var CODESPACE_ENV_KEYS = [
|
|
494
501
|
"PREVIEW_TUNNEL_TOKEN",
|
|
@@ -512,7 +519,7 @@ function loadCodespaceEnv() {
|
|
|
512
519
|
}
|
|
513
520
|
}
|
|
514
521
|
var _default = makeConfig();
|
|
515
|
-
var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, getActiveSessionForAgent, clearAll, saveCliConfig, loadCliConfig } = _default;
|
|
522
|
+
var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, getActiveSessionForAgent, setDisable1mContext, clearAll, saveCliConfig, loadCliConfig } = _default;
|
|
516
523
|
|
|
517
524
|
// src/commands/pair-auto.ts
|
|
518
525
|
var fs41 = __toESM(require("fs"));
|
|
@@ -5390,7 +5397,7 @@ function readAnonId() {
|
|
|
5390
5397
|
}
|
|
5391
5398
|
function superProperties() {
|
|
5392
5399
|
return {
|
|
5393
|
-
cliVersion: true ? "2.
|
|
5400
|
+
cliVersion: true ? "2.43.1" : "0.0.0-dev",
|
|
5394
5401
|
nodeVersion: process.version,
|
|
5395
5402
|
platform: process.platform,
|
|
5396
5403
|
arch: process.arch,
|
|
@@ -5571,7 +5578,7 @@ var os4 = __toESM(require("os"));
|
|
|
5571
5578
|
// package.json
|
|
5572
5579
|
var package_default = {
|
|
5573
5580
|
name: "codeam-cli",
|
|
5574
|
-
version: "2.
|
|
5581
|
+
version: "2.43.1",
|
|
5575
5582
|
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.",
|
|
5576
5583
|
type: "commonjs",
|
|
5577
5584
|
main: "dist/index.js",
|
|
@@ -17804,7 +17811,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
17804
17811
|
if (process.env.NODE_ENV === "test") return;
|
|
17805
17812
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
17806
17813
|
if (process.env.CI) return;
|
|
17807
|
-
const current = true ? "2.
|
|
17814
|
+
const current = true ? "2.43.1" : null;
|
|
17808
17815
|
if (!current) return;
|
|
17809
17816
|
const cache = readCache();
|
|
17810
17817
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -17821,7 +17828,7 @@ function checkForUpdates() {
|
|
|
17821
17828
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
17822
17829
|
if (process.env.CI) return;
|
|
17823
17830
|
if (!process.stdout.isTTY) return;
|
|
17824
|
-
const current = true ? "2.
|
|
17831
|
+
const current = true ? "2.43.1" : null;
|
|
17825
17832
|
if (!current) return;
|
|
17826
17833
|
const cache = readCache();
|
|
17827
17834
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -18259,7 +18266,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process14.s
|
|
|
18259
18266
|
detached: false
|
|
18260
18267
|
});
|
|
18261
18268
|
function currentCliVersion() {
|
|
18262
|
-
return true ? "2.
|
|
18269
|
+
return true ? "2.43.1" : null;
|
|
18263
18270
|
}
|
|
18264
18271
|
function runCmd(cmd, args2, timeoutMs) {
|
|
18265
18272
|
return new Promise((resolve7) => {
|
|
@@ -22737,7 +22744,10 @@ var AcpClient = class {
|
|
|
22737
22744
|
);
|
|
22738
22745
|
const child = (0, import_node_child_process16.spawn)(adapter.command, adapter.args, {
|
|
22739
22746
|
cwd,
|
|
22740
|
-
|
|
22747
|
+
// extraEnv (e.g. CLAUDE_CODE_DISABLE_1M_CONTEXT=1 on an on-demand
|
|
22748
|
+
// re-spawn) layers over process.env; PATH stays last so the augmented
|
|
22749
|
+
// PATH always wins.
|
|
22750
|
+
env: { ...process.env, ...this.opts.extraEnv ?? {}, PATH: augmentedPath },
|
|
22741
22751
|
stdio: ["pipe", "pipe", "pipe"]
|
|
22742
22752
|
});
|
|
22743
22753
|
this.child = child;
|
|
@@ -23431,6 +23441,67 @@ function buildAcpPromptBlocks(payload) {
|
|
|
23431
23441
|
return blocks;
|
|
23432
23442
|
}
|
|
23433
23443
|
|
|
23444
|
+
// src/agents/acp/oneMContextRecovery.ts
|
|
23445
|
+
var ONE_M_CONTEXT_CREDITS_RE = /usage credits required for 1m context/i;
|
|
23446
|
+
function looksLike1mContextCreditsError(text) {
|
|
23447
|
+
return ONE_M_CONTEXT_CREDITS_RE.test(text);
|
|
23448
|
+
}
|
|
23449
|
+
var ONE_M_DISABLE_OPTION = "Disable 1M context and continue";
|
|
23450
|
+
function shouldOfferOneMRecovery(opts) {
|
|
23451
|
+
return looksLike1mContextCreditsError(opts.detail) || looksLike1mContextCreditsError(opts.recentStderr) || looksLike1mContextCreditsError(opts.finalText);
|
|
23452
|
+
}
|
|
23453
|
+
function oneMRecoverySelectPrompt() {
|
|
23454
|
+
return {
|
|
23455
|
+
question: "\u26A0\uFE0F This agent's Anthropic account needs usage credits for 1M context. You can disable 1M context (uses standard 200K context) and continue.",
|
|
23456
|
+
options: [ONE_M_DISABLE_OPTION]
|
|
23457
|
+
};
|
|
23458
|
+
}
|
|
23459
|
+
function createOneMRecovery(deps) {
|
|
23460
|
+
let pending = null;
|
|
23461
|
+
const sp = oneMRecoverySelectPrompt();
|
|
23462
|
+
return {
|
|
23463
|
+
offer: async (commandId, blocks) => {
|
|
23464
|
+
pending = { blocks };
|
|
23465
|
+
await deps.publishText(sp.question);
|
|
23466
|
+
await deps.publishSelectPrompt(sp.question, sp.options);
|
|
23467
|
+
await deps.publishAwaitingAnswer(sp.question, sp.options);
|
|
23468
|
+
deps.appendAgentReply(sp.question);
|
|
23469
|
+
deps.flushHistory();
|
|
23470
|
+
deps.log?.(`1M-context credits gate \u2014 recovery offered id=${commandId.slice(0, 8)}`);
|
|
23471
|
+
await deps.sendResult(commandId, "failed", {
|
|
23472
|
+
error: "1M context requires usage credits \u2014 recovery offered"
|
|
23473
|
+
});
|
|
23474
|
+
},
|
|
23475
|
+
tryRecover: async (commandId) => {
|
|
23476
|
+
if (!pending) return false;
|
|
23477
|
+
const blocks = pending.blocks;
|
|
23478
|
+
pending = null;
|
|
23479
|
+
deps.log?.("1M-context recovery \u2192 disable + reconnect + rerun");
|
|
23480
|
+
await deps.reconnectWith1mDisabled();
|
|
23481
|
+
await deps.beginTurn();
|
|
23482
|
+
try {
|
|
23483
|
+
const reply = await deps.promptAgent(blocks);
|
|
23484
|
+
const finalText = deps.getCurrentText();
|
|
23485
|
+
await deps.closeTurn();
|
|
23486
|
+
deps.appendAgentReply(finalText);
|
|
23487
|
+
deps.flushHistory();
|
|
23488
|
+
await deps.sendResult(commandId, "completed", { stopReason: reply.stopReason });
|
|
23489
|
+
} catch (err) {
|
|
23490
|
+
await deps.recoverFromFailedTurn();
|
|
23491
|
+
const d3 = deps.describeError(err);
|
|
23492
|
+
const b = deps.failureBubbleFor(d3);
|
|
23493
|
+
if (b) {
|
|
23494
|
+
await deps.publishText(b);
|
|
23495
|
+
deps.appendAgentReply(b);
|
|
23496
|
+
deps.flushHistory();
|
|
23497
|
+
}
|
|
23498
|
+
await deps.sendResult(commandId, "failed", { error: d3 });
|
|
23499
|
+
}
|
|
23500
|
+
return true;
|
|
23501
|
+
}
|
|
23502
|
+
};
|
|
23503
|
+
}
|
|
23504
|
+
|
|
23434
23505
|
// src/agents/acp/reconcileDelta.ts
|
|
23435
23506
|
function reconcileCumulative(existing, incoming) {
|
|
23436
23507
|
if (incoming.length === 0) return existing;
|
|
@@ -24793,9 +24864,11 @@ async function runAcpSession(opts) {
|
|
|
24793
24864
|
}
|
|
24794
24865
|
});
|
|
24795
24866
|
let updateCount = 0;
|
|
24796
|
-
const
|
|
24867
|
+
const disable1mContext = loadCliConfig().sessions.find((s) => s.pluginId === opts.pluginId)?.disable1mContext === true;
|
|
24868
|
+
const clientOptions = {
|
|
24797
24869
|
adapter: opts.adapter,
|
|
24798
24870
|
cwd: opts.cwd,
|
|
24871
|
+
extraEnv: disable1mContext ? { CLAUDE_CODE_DISABLE_1M_CONTEXT: "1" } : {},
|
|
24799
24872
|
onSessionUpdate: (notification) => {
|
|
24800
24873
|
updateCount += 1;
|
|
24801
24874
|
const variant = notification.update?.sessionUpdate ?? "unknown";
|
|
@@ -24860,6 +24933,43 @@ async function runAcpSession(opts) {
|
|
|
24860
24933
|
process.exit(1);
|
|
24861
24934
|
})();
|
|
24862
24935
|
}
|
|
24936
|
+
};
|
|
24937
|
+
let client2 = new AcpClient(clientOptions);
|
|
24938
|
+
const reconnectWith1mDisabled = async () => {
|
|
24939
|
+
setDisable1mContext(opts.pluginId, true);
|
|
24940
|
+
try {
|
|
24941
|
+
await client2.stop();
|
|
24942
|
+
} catch {
|
|
24943
|
+
}
|
|
24944
|
+
client2 = new AcpClient({ ...clientOptions, extraEnv: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "1" } });
|
|
24945
|
+
await client2.start();
|
|
24946
|
+
};
|
|
24947
|
+
const oneMRecovery = createOneMRecovery({
|
|
24948
|
+
publishText: (text) => publisher.publishOutput({ type: "text", content: text, done: true }),
|
|
24949
|
+
publishSelectPrompt: (question, options) => publisher.publishOutput({
|
|
24950
|
+
type: "select_prompt",
|
|
24951
|
+
content: question,
|
|
24952
|
+
options,
|
|
24953
|
+
optionDescriptions: options.map(() => ""),
|
|
24954
|
+
currentIndex: 0,
|
|
24955
|
+
done: true
|
|
24956
|
+
}),
|
|
24957
|
+
// The button driver — mobile renders the tappable option from this
|
|
24958
|
+
// awaiting-answer event; the user's tap returns as a `select_option`
|
|
24959
|
+
// command, caught at the top of the select_option case by tryRecover.
|
|
24960
|
+
publishAwaitingAnswer: (prompt, options) => publisher.publishAwaitingAnswer({ questionId: (0, import_node_crypto7.randomUUID)(), prompt, options }),
|
|
24961
|
+
sendResult: (commandId, status2, result) => relay.sendResult(commandId, status2, result),
|
|
24962
|
+
appendAgentReply: (text) => history.appendAgentReply(text),
|
|
24963
|
+
flushHistory: () => void history.flush(),
|
|
24964
|
+
beginTurn: () => streaming.beginTurn(),
|
|
24965
|
+
getCurrentText: () => streaming.getCurrentText(),
|
|
24966
|
+
closeTurn: () => streaming.closeTurnWithInteractiveDetection(),
|
|
24967
|
+
recoverFromFailedTurn: () => recoverFromFailedTurn(client2, streaming),
|
|
24968
|
+
reconnectWith1mDisabled,
|
|
24969
|
+
promptAgent: (blocks) => client2.prompt(blocks),
|
|
24970
|
+
failureBubbleFor: (detail) => failureBubble({ detail, recentStderr: recentStderr.join("\n"), hadText: false, agent: opts.agent }),
|
|
24971
|
+
describeError,
|
|
24972
|
+
log: (msg) => log.info("acpRunner", msg)
|
|
24863
24973
|
});
|
|
24864
24974
|
showInfo(`Starting ${opts.agent} via ACP adapter (${opts.adapter.requiresAgentBinary})\u2026`);
|
|
24865
24975
|
const {
|
|
@@ -24954,7 +25064,8 @@ async function runAcpSession(opts) {
|
|
|
24954
25064
|
turnFiles,
|
|
24955
25065
|
getBeads,
|
|
24956
25066
|
publisher,
|
|
24957
|
-
recentStderr
|
|
25067
|
+
recentStderr,
|
|
25068
|
+
oneMRecovery
|
|
24958
25069
|
);
|
|
24959
25070
|
},
|
|
24960
25071
|
{ id: opts.agent, name: opts.agent, displayName: opts.agent }
|
|
@@ -24978,7 +25089,7 @@ async function runAcpSession(opts) {
|
|
|
24978
25089
|
await new Promise(() => {
|
|
24979
25090
|
});
|
|
24980
25091
|
}
|
|
24981
|
-
async function handleCommand(cmd, client2, relay, acpSessionId, models, streaming, opts, history, jsonlHistory, agentCaps, turnFiles, getBeads, publisher, recentStderr) {
|
|
25092
|
+
async function handleCommand(cmd, client2, relay, acpSessionId, models, streaming, opts, history, jsonlHistory, agentCaps, turnFiles, getBeads, publisher, recentStderr, oneMRecovery) {
|
|
24982
25093
|
switch (cmd.type) {
|
|
24983
25094
|
case "beads_action": {
|
|
24984
25095
|
const beads = getBeads();
|
|
@@ -25029,6 +25140,8 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
|
|
|
25029
25140
|
void reportCredentialInvalid(opts);
|
|
25030
25141
|
log.info("acpRunner", `start_task \u2190 auth-failure-in-reply id=${cmd.id.slice(0, 8)}`);
|
|
25031
25142
|
await relay.sendResult(cmd.id, "failed", { error: "agent reply reported auth failure" });
|
|
25143
|
+
} else if (shouldOfferOneMRecovery({ detail: "", recentStderr: recentStderr.join("\n"), finalText })) {
|
|
25144
|
+
await oneMRecovery.offer(cmd.id, blocks);
|
|
25032
25145
|
} else {
|
|
25033
25146
|
await streaming.closeTurnWithInteractiveDetection();
|
|
25034
25147
|
const replyLine = formatAgentReplyLine(finalText);
|
|
@@ -25048,6 +25161,10 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
|
|
|
25048
25161
|
await recoverFromFailedTurn(client2, streaming);
|
|
25049
25162
|
const detail = describeError(err);
|
|
25050
25163
|
log.warn("acpRunner", `prompt failed: ${detail}`);
|
|
25164
|
+
if (shouldOfferOneMRecovery({ detail, recentStderr: recentStderr.join("\n"), finalText: "" })) {
|
|
25165
|
+
await oneMRecovery.offer(cmd.id, blocks);
|
|
25166
|
+
return;
|
|
25167
|
+
}
|
|
25051
25168
|
const bubble = failureBubble({
|
|
25052
25169
|
detail,
|
|
25053
25170
|
recentStderr: recentStderr.join("\n"),
|
|
@@ -25142,6 +25259,7 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
|
|
|
25142
25259
|
return;
|
|
25143
25260
|
}
|
|
25144
25261
|
case "select_option": {
|
|
25262
|
+
if (await oneMRecovery.tryRecover(cmd.id)) return;
|
|
25145
25263
|
const payload = cmd.payload;
|
|
25146
25264
|
const index = typeof payload?.index === "number" ? payload.index : 0;
|
|
25147
25265
|
const offset = typeof payload?.from === "number" ? payload.from : 0;
|
|
@@ -29211,7 +29329,7 @@ function checkChokidar() {
|
|
|
29211
29329
|
}
|
|
29212
29330
|
async function doctor(args2 = []) {
|
|
29213
29331
|
const json = args2.includes("--json");
|
|
29214
|
-
const cliVersion = true ? "2.
|
|
29332
|
+
const cliVersion = true ? "2.43.1" : "0.0.0-dev";
|
|
29215
29333
|
const apiBase2 = resolveApiBaseUrl();
|
|
29216
29334
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
29217
29335
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -29410,7 +29528,7 @@ async function completion(args2) {
|
|
|
29410
29528
|
// src/commands/version.ts
|
|
29411
29529
|
var import_picocolors14 = __toESM(require("picocolors"));
|
|
29412
29530
|
function version2() {
|
|
29413
|
-
const v = true ? "2.
|
|
29531
|
+
const v = true ? "2.43.1" : "unknown";
|
|
29414
29532
|
console.log(`${import_picocolors14.default.bold("codeam-cli")} ${import_picocolors14.default.cyan(v)}`);
|
|
29415
29533
|
}
|
|
29416
29534
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.43.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",
|