codeam-cli 2.63.1 → 2.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/index.js +1379 -450
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2789,6 +2789,26 @@ function resolveApiBaseUrl() {
|
|
|
2789
2789
|
return DEFAULT_API_BASE_URL;
|
|
2790
2790
|
}
|
|
2791
2791
|
|
|
2792
|
+
// ../../packages/shared/src/types/agent-squad.ts
|
|
2793
|
+
var HANDOFF_FENCE_TAG = "codeam-handoff";
|
|
2794
|
+
var SQUAD_CONFIGURE_COMMAND = "squad_configure";
|
|
2795
|
+
var SQUAD_STATS_COMMAND = "squad_stats";
|
|
2796
|
+
var SQUAD_HOP_BUDGET_DEFAULT = 3;
|
|
2797
|
+
var SQUAD_HOP_BUDGET_MIN = 1;
|
|
2798
|
+
var SQUAD_HOP_BUDGET_MAX = 10;
|
|
2799
|
+
function clampHopBudget(value) {
|
|
2800
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return SQUAD_HOP_BUDGET_DEFAULT;
|
|
2801
|
+
return Math.min(SQUAD_HOP_BUDGET_MAX, Math.max(SQUAD_HOP_BUDGET_MIN, Math.round(value)));
|
|
2802
|
+
}
|
|
2803
|
+
var SQUAD_SPECIALTIES = {
|
|
2804
|
+
claude: "deep reasoning, refactors, and multi-step architecture work",
|
|
2805
|
+
codex: "fast, focused implementation and test fixing",
|
|
2806
|
+
cursor: "multi-file edits and codebase-wide changes",
|
|
2807
|
+
gemini: "large-context analysis across big files and logs",
|
|
2808
|
+
kimi: "long-context code reading and summarization",
|
|
2809
|
+
opencode: "general implementation tasks"
|
|
2810
|
+
};
|
|
2811
|
+
|
|
2792
2812
|
// ../../packages/shared/src/headroom/manifest.ts
|
|
2793
2813
|
var HEADROOM_PROXY_PORT = 8787;
|
|
2794
2814
|
var HEADROOM_PIP_COMPANIONS = [
|
|
@@ -2921,6 +2941,9 @@ var USER_EVENTS = {
|
|
|
2921
2941
|
// the backend re-publishes them on the per-user SSE bus (mirrored in repo A).
|
|
2922
2942
|
SWITCH_AGENT_PROGRESS: "switch_agent_progress",
|
|
2923
2943
|
SWITCH_AGENT_STATUS: "switch_agent_status",
|
|
2944
|
+
// Agent Squad — the CLI posts these to /api/agent-switch/events; PRO-gated.
|
|
2945
|
+
HANDOFF_PROPOSED: "handoff_proposed",
|
|
2946
|
+
HANDOFF_RESOLVED: "handoff_resolved",
|
|
2924
2947
|
// VCS / PR Command Center — the backend publishes this after an agent finishes
|
|
2925
2948
|
// reviewing a PR (verdict + comment count + findings), driving the mobile
|
|
2926
2949
|
// completion screen + push. Mirrored in repo A's app-shared events.ts.
|
|
@@ -3125,11 +3148,11 @@ function quiet(fn) {
|
|
|
3125
3148
|
log.debug(TAG, "ignored sync error", err);
|
|
3126
3149
|
}
|
|
3127
3150
|
}
|
|
3128
|
-
function rmIfExistsQuiet(
|
|
3151
|
+
function rmIfExistsQuiet(path91) {
|
|
3129
3152
|
try {
|
|
3130
|
-
fs2.rmSync(
|
|
3153
|
+
fs2.rmSync(path91, { force: true });
|
|
3131
3154
|
} catch (err) {
|
|
3132
|
-
log.debug(TAG, `rmIfExists failed for ${
|
|
3155
|
+
log.debug(TAG, `rmIfExists failed for ${path91}`, err);
|
|
3133
3156
|
}
|
|
3134
3157
|
}
|
|
3135
3158
|
function killQuiet(target, signal = "SIGTERM") {
|
|
@@ -3243,6 +3266,22 @@ function makeConfig(baseDir) {
|
|
|
3243
3266
|
s.agent = agent;
|
|
3244
3267
|
save(c2);
|
|
3245
3268
|
}
|
|
3269
|
+
function setSquadAuto2(pluginId, value) {
|
|
3270
|
+
const stored = {
|
|
3271
|
+
enabled: value.enabled === true,
|
|
3272
|
+
hopBudget: clampHopBudget(value.hopBudget)
|
|
3273
|
+
};
|
|
3274
|
+
const c2 = load();
|
|
3275
|
+
const s = c2.sessions.find((x) => x.pluginId === pluginId);
|
|
3276
|
+
if (!s) return null;
|
|
3277
|
+
s.squadAuto = stored;
|
|
3278
|
+
save(c2);
|
|
3279
|
+
return stored;
|
|
3280
|
+
}
|
|
3281
|
+
function getSquadAuto2(pluginId) {
|
|
3282
|
+
const s = load().sessions.find((x) => x.pluginId === pluginId);
|
|
3283
|
+
return s?.squadAuto ?? null;
|
|
3284
|
+
}
|
|
3246
3285
|
function clearAll2() {
|
|
3247
3286
|
try {
|
|
3248
3287
|
fs3.unlinkSync(file);
|
|
@@ -3255,7 +3294,7 @@ function makeConfig(baseDir) {
|
|
|
3255
3294
|
function loadCliConfig2() {
|
|
3256
3295
|
return load();
|
|
3257
3296
|
}
|
|
3258
|
-
return { getConfig: getConfig2, ensurePluginId: ensurePluginId2, addSession: addSession2, removeSession: removeSession2, setActiveSession: setActiveSession2, getActiveSession: getActiveSession2, getActiveSessionForAgent: getActiveSessionForAgent2, setDisable1mContext: setDisable1mContext2, setSessionAgent: setSessionAgent2, clearAll: clearAll2, saveCliConfig: saveCliConfig2, loadCliConfig: loadCliConfig2 };
|
|
3297
|
+
return { getConfig: getConfig2, ensurePluginId: ensurePluginId2, addSession: addSession2, removeSession: removeSession2, setActiveSession: setActiveSession2, getActiveSession: getActiveSession2, getActiveSessionForAgent: getActiveSessionForAgent2, setDisable1mContext: setDisable1mContext2, setSessionAgent: setSessionAgent2, setSquadAuto: setSquadAuto2, getSquadAuto: getSquadAuto2, clearAll: clearAll2, saveCliConfig: saveCliConfig2, loadCliConfig: loadCliConfig2 };
|
|
3259
3298
|
}
|
|
3260
3299
|
var CODESPACE_ENV_KEYS = [
|
|
3261
3300
|
"PREVIEW_TUNNEL_TOKEN",
|
|
@@ -3279,7 +3318,7 @@ function loadCodespaceEnv() {
|
|
|
3279
3318
|
}
|
|
3280
3319
|
}
|
|
3281
3320
|
var _default = makeConfig();
|
|
3282
|
-
var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, getActiveSessionForAgent, setDisable1mContext, setSessionAgent, clearAll, saveCliConfig, loadCliConfig } = _default;
|
|
3321
|
+
var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, getActiveSessionForAgent, setDisable1mContext, setSessionAgent, setSquadAuto, getSquadAuto, clearAll, saveCliConfig, loadCliConfig } = _default;
|
|
3283
3322
|
|
|
3284
3323
|
// src/commands/pair-auto.ts
|
|
3285
3324
|
var fs63 = __toESM(require("fs"));
|
|
@@ -3320,8 +3359,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
|
|
|
3320
3359
|
return decodedFile;
|
|
3321
3360
|
};
|
|
3322
3361
|
}
|
|
3323
|
-
function normalizeWindowsPath(
|
|
3324
|
-
return
|
|
3362
|
+
function normalizeWindowsPath(path91) {
|
|
3363
|
+
return path91.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
|
|
3325
3364
|
}
|
|
3326
3365
|
|
|
3327
3366
|
// ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
|
|
@@ -5801,9 +5840,9 @@ async function addSourceContext(frames) {
|
|
|
5801
5840
|
LRU_FILE_CONTENTS_CACHE.reduce();
|
|
5802
5841
|
return frames;
|
|
5803
5842
|
}
|
|
5804
|
-
function getContextLinesFromFile(
|
|
5843
|
+
function getContextLinesFromFile(path91, ranges, output) {
|
|
5805
5844
|
return new Promise((resolve9) => {
|
|
5806
|
-
const stream = (0, import_node_fs.createReadStream)(
|
|
5845
|
+
const stream = (0, import_node_fs.createReadStream)(path91);
|
|
5807
5846
|
const lineReaded = (0, import_node_readline.createInterface)({
|
|
5808
5847
|
input: stream
|
|
5809
5848
|
});
|
|
@@ -5818,7 +5857,7 @@ function getContextLinesFromFile(path90, ranges, output) {
|
|
|
5818
5857
|
let rangeStart = range[0];
|
|
5819
5858
|
let rangeEnd = range[1];
|
|
5820
5859
|
function onStreamError() {
|
|
5821
|
-
LRU_FILE_CONTENTS_FS_READ_FAILED.set(
|
|
5860
|
+
LRU_FILE_CONTENTS_FS_READ_FAILED.set(path91, 1);
|
|
5822
5861
|
lineReaded.close();
|
|
5823
5862
|
lineReaded.removeAllListeners();
|
|
5824
5863
|
destroyStreamAndResolve();
|
|
@@ -5879,8 +5918,8 @@ function clearLineContext(frame) {
|
|
|
5879
5918
|
delete frame.context_line;
|
|
5880
5919
|
delete frame.post_context;
|
|
5881
5920
|
}
|
|
5882
|
-
function shouldSkipContextLinesForFile(
|
|
5883
|
-
return
|
|
5921
|
+
function shouldSkipContextLinesForFile(path91) {
|
|
5922
|
+
return path91.startsWith("node:") || path91.endsWith(".min.js") || path91.endsWith(".min.cjs") || path91.endsWith(".min.mjs") || path91.startsWith("data:");
|
|
5884
5923
|
}
|
|
5885
5924
|
function shouldSkipContextLinesForFrame(frame) {
|
|
5886
5925
|
if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
|
|
@@ -8034,7 +8073,7 @@ function readAnonId() {
|
|
|
8034
8073
|
}
|
|
8035
8074
|
function superProperties() {
|
|
8036
8075
|
return {
|
|
8037
|
-
cliVersion: true ? "2.
|
|
8076
|
+
cliVersion: true ? "2.65.0" : "0.0.0-dev",
|
|
8038
8077
|
nodeVersion: process.version,
|
|
8039
8078
|
platform: process.platform,
|
|
8040
8079
|
arch: process.arch,
|
|
@@ -8215,7 +8254,7 @@ var os4 = __toESM(require("os"));
|
|
|
8215
8254
|
// package.json
|
|
8216
8255
|
var package_default = {
|
|
8217
8256
|
name: "codeam-cli",
|
|
8218
|
-
version: "2.
|
|
8257
|
+
version: "2.65.0",
|
|
8219
8258
|
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.",
|
|
8220
8259
|
type: "commonjs",
|
|
8221
8260
|
main: "dist/index.js",
|
|
@@ -8611,6 +8650,20 @@ async function fetchProvisionCredential(input) {
|
|
|
8611
8650
|
return null;
|
|
8612
8651
|
}
|
|
8613
8652
|
}
|
|
8653
|
+
async function fetchSquadRoster(input) {
|
|
8654
|
+
try {
|
|
8655
|
+
const res = await _transport.postJsonAuthed(
|
|
8656
|
+
`${API_BASE}/api/plugin/agents/roster`,
|
|
8657
|
+
{ sessionId: input.sessionId, pluginId: input.pluginId },
|
|
8658
|
+
input.pluginAuthToken
|
|
8659
|
+
);
|
|
8660
|
+
const data = res?.data;
|
|
8661
|
+
if (!data || !Array.isArray(data.agents)) return null;
|
|
8662
|
+
return { agents: data.agents, handoffsEnabled: data.handoffsEnabled === true };
|
|
8663
|
+
} catch {
|
|
8664
|
+
return null;
|
|
8665
|
+
}
|
|
8666
|
+
}
|
|
8614
8667
|
async function postAgentSwitchEvent(input) {
|
|
8615
8668
|
try {
|
|
8616
8669
|
await _transport.postJsonAuthed(
|
|
@@ -8773,9 +8826,7 @@ async function _postJsonAuthed(url2, body, pluginAuthToken) {
|
|
|
8773
8826
|
});
|
|
8774
8827
|
res.on("end", () => {
|
|
8775
8828
|
if (res.statusCode && res.statusCode >= 400) {
|
|
8776
|
-
reject(
|
|
8777
|
-
makeHttpError(res.statusCode, res.headers["retry-after"], responseBody)
|
|
8778
|
-
);
|
|
8829
|
+
reject(makeHttpError(res.statusCode, res.headers["retry-after"], responseBody));
|
|
8779
8830
|
return;
|
|
8780
8831
|
}
|
|
8781
8832
|
try {
|
|
@@ -9655,7 +9706,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9655
9706
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9656
9707
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9657
9708
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9658
|
-
..."2.
|
|
9709
|
+
..."2.65.0" ? { ideVersion: "2.65.0" } : {}
|
|
9659
9710
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9660
9711
|
}
|
|
9661
9712
|
/**
|
|
@@ -9680,7 +9731,8 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9680
9731
|
];
|
|
9681
9732
|
_postJson(`${API_BASE2}/api/plugin/agents`, {
|
|
9682
9733
|
pluginId: this.pluginId,
|
|
9683
|
-
agents
|
|
9734
|
+
agents,
|
|
9735
|
+
capabilities: { squad: true }
|
|
9684
9736
|
}).then(() => {
|
|
9685
9737
|
this.agentsRegistered = true;
|
|
9686
9738
|
}).catch(() => {
|
|
@@ -9899,10 +9951,10 @@ var WINDOWS_LEGACY_JUNCTIONS = [
|
|
|
9899
9951
|
/[\\/]Start Menu([\\/]|$)/i,
|
|
9900
9952
|
/[\\/]Templates([\\/]|$)/i
|
|
9901
9953
|
];
|
|
9902
|
-
function isUnsafeWindowsWatchRoot(dir,
|
|
9954
|
+
function isUnsafeWindowsWatchRoot(dir, homedir53) {
|
|
9903
9955
|
const norm = (p2) => p2.replace(/\//g, "\\").replace(/\\+$/, "").toLowerCase();
|
|
9904
9956
|
const cwd = norm(dir);
|
|
9905
|
-
const home = norm(
|
|
9957
|
+
const home = norm(homedir53);
|
|
9906
9958
|
if (cwd === home) return true;
|
|
9907
9959
|
if (/^[a-z]:$/.test(cwd)) return true;
|
|
9908
9960
|
const sysRoots = [
|
|
@@ -14558,10 +14610,10 @@ function buildForPlatform(platform3) {
|
|
|
14558
14610
|
var import_node_crypto4 = require("crypto");
|
|
14559
14611
|
|
|
14560
14612
|
// src/agents/claude/resolver.ts
|
|
14561
|
-
function buildClaudeLaunch(extraArgs = [],
|
|
14562
|
-
const found =
|
|
14613
|
+
function buildClaudeLaunch(extraArgs = [], os66 = createOsStrategy()) {
|
|
14614
|
+
const found = os66.findInPath("claude") ?? os66.findInPath("claude-code");
|
|
14563
14615
|
if (!found) return null;
|
|
14564
|
-
return
|
|
14616
|
+
return os66.buildLaunch(found, extraArgs);
|
|
14565
14617
|
}
|
|
14566
14618
|
|
|
14567
14619
|
// src/agents/claude/installer.ts
|
|
@@ -15151,8 +15203,8 @@ var ClaudeRuntimeStrategy = class {
|
|
|
15151
15203
|
meta = getAgent("claude");
|
|
15152
15204
|
mode = "interactive";
|
|
15153
15205
|
os;
|
|
15154
|
-
constructor(
|
|
15155
|
-
this.os =
|
|
15206
|
+
constructor(os66) {
|
|
15207
|
+
this.os = os66;
|
|
15156
15208
|
}
|
|
15157
15209
|
/**
|
|
15158
15210
|
* Claude Code's react-ink TUI enables bracketed-paste mode at
|
|
@@ -15932,8 +15984,8 @@ function codexCredentialLocator() {
|
|
|
15932
15984
|
function codexLoginLauncher() {
|
|
15933
15985
|
return {
|
|
15934
15986
|
async ensureInstalled() {
|
|
15935
|
-
const
|
|
15936
|
-
return
|
|
15987
|
+
const os66 = createOsStrategy();
|
|
15988
|
+
return os66.findInPath("codex") !== null;
|
|
15937
15989
|
},
|
|
15938
15990
|
launch() {
|
|
15939
15991
|
return (0, import_node_child_process4.spawn)("codex", ["login"], { stdio: "inherit" });
|
|
@@ -15956,8 +16008,8 @@ var CodexRuntimeStrategy = class {
|
|
|
15956
16008
|
meta = getAgent("codex");
|
|
15957
16009
|
mode = "interactive";
|
|
15958
16010
|
os;
|
|
15959
|
-
constructor(
|
|
15960
|
-
this.os =
|
|
16011
|
+
constructor(os66) {
|
|
16012
|
+
this.os = os66;
|
|
15961
16013
|
}
|
|
15962
16014
|
async prepareLaunch() {
|
|
15963
16015
|
let binary = this.os.findInPath("codex");
|
|
@@ -16066,12 +16118,12 @@ var CodexRuntimeStrategy = class {
|
|
|
16066
16118
|
});
|
|
16067
16119
|
}
|
|
16068
16120
|
};
|
|
16069
|
-
function resolveNpm(
|
|
16070
|
-
return
|
|
16121
|
+
function resolveNpm(os66) {
|
|
16122
|
+
return os66.id === "win32" ? "npm.cmd" : "npm";
|
|
16071
16123
|
}
|
|
16072
|
-
async function installCodexViaNpm(
|
|
16124
|
+
async function installCodexViaNpm(os66) {
|
|
16073
16125
|
return new Promise((resolve9, reject) => {
|
|
16074
|
-
const proc = (0, import_node_child_process5.spawn)(resolveNpm(
|
|
16126
|
+
const proc = (0, import_node_child_process5.spawn)(resolveNpm(os66), ["install", "-g", "@openai/codex"], {
|
|
16075
16127
|
stdio: "inherit"
|
|
16076
16128
|
});
|
|
16077
16129
|
proc.on("close", (code) => {
|
|
@@ -16088,16 +16140,16 @@ async function installCodexViaNpm(os65) {
|
|
|
16088
16140
|
});
|
|
16089
16141
|
});
|
|
16090
16142
|
}
|
|
16091
|
-
function augmentNpmGlobalBin(
|
|
16143
|
+
function augmentNpmGlobalBin(os66) {
|
|
16092
16144
|
try {
|
|
16093
|
-
const result = (0, import_node_child_process5.spawnSync)(resolveNpm(
|
|
16145
|
+
const result = (0, import_node_child_process5.spawnSync)(resolveNpm(os66), ["prefix", "-g"], {
|
|
16094
16146
|
stdio: ["ignore", "pipe", "ignore"]
|
|
16095
16147
|
});
|
|
16096
16148
|
if (result.status !== 0) return;
|
|
16097
16149
|
const prefix = result.stdout.toString().trim();
|
|
16098
16150
|
if (!prefix) return;
|
|
16099
|
-
const binDir =
|
|
16100
|
-
|
|
16151
|
+
const binDir = os66.id === "win32" ? prefix : path25.join(prefix, "bin");
|
|
16152
|
+
os66.augmentPath([binDir]);
|
|
16101
16153
|
} catch {
|
|
16102
16154
|
}
|
|
16103
16155
|
}
|
|
@@ -16496,10 +16548,10 @@ function manualInstallHint(runner, tools) {
|
|
|
16496
16548
|
const argv = osPackageInstallArgv(detectPackageManager(runner), [...tools]);
|
|
16497
16549
|
return argv ? `\`sudo ${argv.join(" ")}\`` : `your package manager (e.g. \`${pkgs}\`)`;
|
|
16498
16550
|
}
|
|
16499
|
-
function writeUnzipShim(
|
|
16551
|
+
function writeUnzipShim(os66, runner) {
|
|
16500
16552
|
const python = ["python3", "python"].find((p2) => runner.which(p2));
|
|
16501
16553
|
if (!python) return null;
|
|
16502
|
-
const dir =
|
|
16554
|
+
const dir = os66.scratchPath("codeam-cr-prereq");
|
|
16503
16555
|
const shim = path27.join(dir, "unzip");
|
|
16504
16556
|
const script = `#!/bin/sh
|
|
16505
16557
|
set -e
|
|
@@ -16521,9 +16573,9 @@ exec ${python} -m zipfile -e "$archive" "$dest"
|
|
|
16521
16573
|
(0, import_node_fs5.chmodSync)(shim, 448);
|
|
16522
16574
|
return dir;
|
|
16523
16575
|
}
|
|
16524
|
-
async function ensureInstallPrerequisites(
|
|
16576
|
+
async function ensureInstallPrerequisites(os66, deps = {}) {
|
|
16525
16577
|
const runner = deps.runner ?? defaultHeadroomRunner;
|
|
16526
|
-
const missing = REQUIRED_TOOLS.filter((t2) =>
|
|
16578
|
+
const missing = REQUIRED_TOOLS.filter((t2) => os66.findInPath(t2) === null);
|
|
16527
16579
|
if (missing.length === 0) return { ok: true, extraPath: [] };
|
|
16528
16580
|
log.info(
|
|
16529
16581
|
"coderabbit",
|
|
@@ -16542,10 +16594,10 @@ async function ensureInstallPrerequisites(os65, deps = {}) {
|
|
|
16542
16594
|
);
|
|
16543
16595
|
}
|
|
16544
16596
|
}
|
|
16545
|
-
const stillMissing = REQUIRED_TOOLS.filter((t2) =>
|
|
16597
|
+
const stillMissing = REQUIRED_TOOLS.filter((t2) => os66.findInPath(t2) === null);
|
|
16546
16598
|
if (stillMissing.length === 0) return { ok: true, extraPath: [] };
|
|
16547
16599
|
if (stillMissing.length === 1 && stillMissing[0] === "unzip") {
|
|
16548
|
-
const shimDir = writeUnzipShim(
|
|
16600
|
+
const shimDir = writeUnzipShim(os66, runner);
|
|
16549
16601
|
if (shimDir) {
|
|
16550
16602
|
log.info("coderabbit", "unzip is unavailable \u2014 using a scoped python zipfile shim");
|
|
16551
16603
|
return { ok: true, extraPath: [shimDir] };
|
|
@@ -16608,15 +16660,15 @@ function summarizeInstallFailure(output) {
|
|
|
16608
16660
|
}
|
|
16609
16661
|
return lines.length > 0 ? lines[lines.length - 1] : null;
|
|
16610
16662
|
}
|
|
16611
|
-
async function ensureCoderabbitInstalled(
|
|
16612
|
-
if (
|
|
16613
|
-
if (
|
|
16663
|
+
async function ensureCoderabbitInstalled(os66, deps = {}) {
|
|
16664
|
+
if (os66.findInPath("coderabbit")) return { ok: true };
|
|
16665
|
+
if (os66.id === "win32") {
|
|
16614
16666
|
return {
|
|
16615
16667
|
ok: false,
|
|
16616
16668
|
error: "CodeRabbit on Windows requires WSL. Install the CLI inside your WSL distribution (curl -fsSL https://cli.coderabbit.ai/install.sh | sh), then try again."
|
|
16617
16669
|
};
|
|
16618
16670
|
}
|
|
16619
|
-
const prereq = await ensureInstallPrerequisites(
|
|
16671
|
+
const prereq = await ensureInstallPrerequisites(os66, deps);
|
|
16620
16672
|
if (!prereq.ok) return { ok: false, error: prereq.error };
|
|
16621
16673
|
const env = { ...process.env };
|
|
16622
16674
|
if (prereq.extraPath.length > 0) {
|
|
@@ -16632,8 +16684,8 @@ async function ensureCoderabbitInstalled(os65, deps = {}) {
|
|
|
16632
16684
|
error: detail ? `CodeRabbit CLI install failed: ${detail}` : "CodeRabbit CLI install failed \u2014 check this machine's network egress and try again."
|
|
16633
16685
|
};
|
|
16634
16686
|
}
|
|
16635
|
-
|
|
16636
|
-
if (
|
|
16687
|
+
os66.augmentPath([`${os66.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
|
|
16688
|
+
if (os66.findInPath("coderabbit") === null) {
|
|
16637
16689
|
const detail = summarizeInstallFailure(output);
|
|
16638
16690
|
return {
|
|
16639
16691
|
ok: false,
|
|
@@ -16675,10 +16727,10 @@ function coderabbitCredentialLocator() {
|
|
|
16675
16727
|
validate: validateNonEmptyCredential
|
|
16676
16728
|
};
|
|
16677
16729
|
}
|
|
16678
|
-
function coderabbitLoginLauncher(
|
|
16730
|
+
function coderabbitLoginLauncher(os66) {
|
|
16679
16731
|
return {
|
|
16680
16732
|
async ensureInstalled() {
|
|
16681
|
-
const result = await ensureCoderabbitInstalled(
|
|
16733
|
+
const result = await ensureCoderabbitInstalled(os66);
|
|
16682
16734
|
if (!result.ok && result.error) console.error(`
|
|
16683
16735
|
\u2717 ${result.error}
|
|
16684
16736
|
`);
|
|
@@ -16738,8 +16790,8 @@ function pickLine(obj) {
|
|
|
16738
16790
|
function toHunk(raw, groupSeverity) {
|
|
16739
16791
|
if (!raw || typeof raw !== "object") return null;
|
|
16740
16792
|
const o = raw;
|
|
16741
|
-
const
|
|
16742
|
-
if (!
|
|
16793
|
+
const path91 = asString(pick(o, ["file_path", "filePath", "file", "path", "filename", "fileName"])) ?? asString(pick(o, ["location"])?.path);
|
|
16794
|
+
if (!path91) return null;
|
|
16743
16795
|
const message = asString(
|
|
16744
16796
|
pick(o, [
|
|
16745
16797
|
"comment",
|
|
@@ -16756,7 +16808,7 @@ function toHunk(raw, groupSeverity) {
|
|
|
16756
16808
|
const severity = normSeverity(pick(o, ["severity", "level", "priority", "impact"])) ?? normSeverity(groupSeverity);
|
|
16757
16809
|
const locObj = pick(o, ["location"]) ?? o;
|
|
16758
16810
|
return {
|
|
16759
|
-
path:
|
|
16811
|
+
path: path91.trim(),
|
|
16760
16812
|
line: pickLine(o) ?? pickLine(locObj),
|
|
16761
16813
|
severity,
|
|
16762
16814
|
message: (title && message ? `${title}: ${message}` : title || message).trim() || "(no message)"
|
|
@@ -16839,10 +16891,10 @@ function parsePlain(stdout) {
|
|
|
16839
16891
|
for (const line of stdout.split(/\r?\n/)) {
|
|
16840
16892
|
const m = line.match(HUNK_LINE_RE);
|
|
16841
16893
|
if (!m) continue;
|
|
16842
|
-
const [,
|
|
16843
|
-
if (!
|
|
16894
|
+
const [, path91, lineNo, sevToken, message] = m;
|
|
16895
|
+
if (!path91 || !lineNo || !message) continue;
|
|
16844
16896
|
hunks.push({
|
|
16845
|
-
path:
|
|
16897
|
+
path: path91.trim(),
|
|
16846
16898
|
line: Number(lineNo),
|
|
16847
16899
|
severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
|
|
16848
16900
|
message: message.trim().replace(/^[*-]\s+/, "")
|
|
@@ -16902,8 +16954,8 @@ var CoderabbitRuntimeStrategy = class {
|
|
|
16902
16954
|
meta = getAgent("coderabbit");
|
|
16903
16955
|
mode = "batch";
|
|
16904
16956
|
os;
|
|
16905
|
-
constructor(
|
|
16906
|
-
this.os =
|
|
16957
|
+
constructor(os66) {
|
|
16958
|
+
this.os = os66;
|
|
16907
16959
|
}
|
|
16908
16960
|
getDefaultArgs() {
|
|
16909
16961
|
return ["review", "--agent"];
|
|
@@ -17174,10 +17226,10 @@ function cursorCredentialLocator() {
|
|
|
17174
17226
|
validate: validateNonEmptyCredential
|
|
17175
17227
|
};
|
|
17176
17228
|
}
|
|
17177
|
-
function cursorLoginLauncher(
|
|
17229
|
+
function cursorLoginLauncher(os66) {
|
|
17178
17230
|
return {
|
|
17179
17231
|
async ensureInstalled() {
|
|
17180
|
-
if (
|
|
17232
|
+
if (os66.findInPath("cursor-agent")) return true;
|
|
17181
17233
|
console.error(
|
|
17182
17234
|
"\n \u2717 cursor-agent binary not on PATH.\n Install Cursor (https://cursor.com/) and ensure the CLI\n plugin is enabled, then re-run `codeam link cursor`.\n"
|
|
17183
17235
|
);
|
|
@@ -17241,8 +17293,8 @@ var CursorRuntimeStrategy = class {
|
|
|
17241
17293
|
meta = getAgent("cursor");
|
|
17242
17294
|
mode = "interactive";
|
|
17243
17295
|
os;
|
|
17244
|
-
constructor(
|
|
17245
|
-
this.os =
|
|
17296
|
+
constructor(os66) {
|
|
17297
|
+
this.os = os66;
|
|
17246
17298
|
}
|
|
17247
17299
|
async prepareLaunch() {
|
|
17248
17300
|
const binary = this.os.findInPath("cursor-agent");
|
|
@@ -17458,10 +17510,10 @@ function aiderCredentialLocator() {
|
|
|
17458
17510
|
validate: validateNonEmptyCredential
|
|
17459
17511
|
};
|
|
17460
17512
|
}
|
|
17461
|
-
function aiderLoginLauncher(
|
|
17513
|
+
function aiderLoginLauncher(os66) {
|
|
17462
17514
|
return {
|
|
17463
17515
|
async ensureInstalled() {
|
|
17464
|
-
if (
|
|
17516
|
+
if (os66.findInPath("aider")) return true;
|
|
17465
17517
|
console.error(
|
|
17466
17518
|
"\n \u2717 aider binary not on PATH.\n Install Aider:\n pip install aider-chat\n then re-run `codeam link aider`.\n"
|
|
17467
17519
|
);
|
|
@@ -17471,7 +17523,7 @@ function aiderLoginLauncher(os65) {
|
|
|
17471
17523
|
console.error(
|
|
17472
17524
|
"\n Aider has no interactive login flow.\n Set ANTHROPIC_API_KEY or OPENAI_API_KEY in your shell,\n or re-run `codeam link aider --api-key=<your-key>`.\n"
|
|
17473
17525
|
);
|
|
17474
|
-
return (0, import_node_child_process12.spawn)(
|
|
17526
|
+
return (0, import_node_child_process12.spawn)(os66.id === "win32" ? "cmd.exe" : "sh", os66.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
|
|
17475
17527
|
stdio: "ignore"
|
|
17476
17528
|
});
|
|
17477
17529
|
}
|
|
@@ -17543,8 +17595,8 @@ var AiderRuntimeStrategy = class {
|
|
|
17543
17595
|
meta = getAgent("aider");
|
|
17544
17596
|
mode = "interactive";
|
|
17545
17597
|
os;
|
|
17546
|
-
constructor(
|
|
17547
|
-
this.os =
|
|
17598
|
+
constructor(os66) {
|
|
17599
|
+
this.os = os66;
|
|
17548
17600
|
}
|
|
17549
17601
|
async prepareLaunch() {
|
|
17550
17602
|
const binary = this.os.findInPath("aider");
|
|
@@ -17676,8 +17728,8 @@ function geminiCredentialLocator() {
|
|
|
17676
17728
|
function geminiLoginLauncher() {
|
|
17677
17729
|
return {
|
|
17678
17730
|
async ensureInstalled() {
|
|
17679
|
-
const
|
|
17680
|
-
return
|
|
17731
|
+
const os66 = createOsStrategy();
|
|
17732
|
+
return os66.findInPath("gemini") !== null;
|
|
17681
17733
|
},
|
|
17682
17734
|
launch() {
|
|
17683
17735
|
return (0, import_node_child_process13.spawn)("gemini", ["auth", "login"], { stdio: "inherit" });
|
|
@@ -17867,8 +17919,8 @@ var GeminiRuntimeStrategy = class {
|
|
|
17867
17919
|
meta = getAgent("gemini");
|
|
17868
17920
|
mode = "interactive";
|
|
17869
17921
|
os;
|
|
17870
|
-
constructor(
|
|
17871
|
-
this.os =
|
|
17922
|
+
constructor(os66) {
|
|
17923
|
+
this.os = os66;
|
|
17872
17924
|
}
|
|
17873
17925
|
async prepareLaunch() {
|
|
17874
17926
|
const binary = this.os.findInPath("gemini");
|
|
@@ -18159,8 +18211,8 @@ var KimiRuntimeStrategy = class {
|
|
|
18159
18211
|
meta = getAgent("kimi");
|
|
18160
18212
|
mode = "interactive";
|
|
18161
18213
|
os;
|
|
18162
|
-
constructor(
|
|
18163
|
-
this.os =
|
|
18214
|
+
constructor(os66) {
|
|
18215
|
+
this.os = os66;
|
|
18164
18216
|
}
|
|
18165
18217
|
async prepareLaunch() {
|
|
18166
18218
|
const binary = this.os.findInPath("kimi");
|
|
@@ -18302,8 +18354,8 @@ var OpencodeRuntimeStrategy = class {
|
|
|
18302
18354
|
meta = getAgent("opencode");
|
|
18303
18355
|
mode = "interactive";
|
|
18304
18356
|
os;
|
|
18305
|
-
constructor(
|
|
18306
|
-
this.os =
|
|
18357
|
+
constructor(os66) {
|
|
18358
|
+
this.os = os66;
|
|
18307
18359
|
}
|
|
18308
18360
|
async prepareLaunch() {
|
|
18309
18361
|
const binary = this.os.findInPath("opencode");
|
|
@@ -18387,20 +18439,20 @@ var OpencodeRuntimeStrategy = class {
|
|
|
18387
18439
|
|
|
18388
18440
|
// src/agents/registry.ts
|
|
18389
18441
|
var runtimeBuilders = {
|
|
18390
|
-
claude: (
|
|
18391
|
-
codex: (
|
|
18392
|
-
coderabbit: (
|
|
18393
|
-
cursor: (
|
|
18394
|
-
aider: (
|
|
18395
|
-
gemini: (
|
|
18396
|
-
kimi: (
|
|
18397
|
-
opencode: (
|
|
18442
|
+
claude: (os66) => new ClaudeRuntimeStrategy(os66),
|
|
18443
|
+
codex: (os66) => new CodexRuntimeStrategy(os66),
|
|
18444
|
+
coderabbit: (os66) => new CoderabbitRuntimeStrategy(os66),
|
|
18445
|
+
cursor: (os66) => new CursorRuntimeStrategy(os66),
|
|
18446
|
+
aider: (os66) => new AiderRuntimeStrategy(os66),
|
|
18447
|
+
gemini: (os66) => new GeminiRuntimeStrategy(os66),
|
|
18448
|
+
kimi: (os66) => new KimiRuntimeStrategy(os66),
|
|
18449
|
+
opencode: (os66) => new OpencodeRuntimeStrategy(os66)
|
|
18398
18450
|
};
|
|
18399
18451
|
var deployBuilders = {
|
|
18400
18452
|
claude: () => new ClaudeDeployStrategy(),
|
|
18401
18453
|
codex: () => new CodexDeployStrategy()
|
|
18402
18454
|
};
|
|
18403
|
-
function createAgentStrategy(agent,
|
|
18455
|
+
function createAgentStrategy(agent, os66 = createOsStrategy()) {
|
|
18404
18456
|
if (!AGENT_REGISTRY[agent]?.enabled) {
|
|
18405
18457
|
throw new Error(
|
|
18406
18458
|
`Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
|
|
@@ -18410,10 +18462,10 @@ function createAgentStrategy(agent, os65 = createOsStrategy()) {
|
|
|
18410
18462
|
if (!build) {
|
|
18411
18463
|
throw new Error(`No runtime strategy registered for agent "${agent}"`);
|
|
18412
18464
|
}
|
|
18413
|
-
return build(
|
|
18465
|
+
return build(os66);
|
|
18414
18466
|
}
|
|
18415
|
-
function createInteractiveAgentStrategy(agent,
|
|
18416
|
-
const s = createAgentStrategy(agent,
|
|
18467
|
+
function createInteractiveAgentStrategy(agent, os66 = createOsStrategy()) {
|
|
18468
|
+
const s = createAgentStrategy(agent, os66);
|
|
18417
18469
|
if (s.mode !== "interactive") {
|
|
18418
18470
|
throw new Error(
|
|
18419
18471
|
`Agent "${agent}" is a batch agent; use createAgentStrategy + .runOneShot for one-shot reviews.`
|
|
@@ -19091,8 +19143,8 @@ function collectChangedFiles(cwd) {
|
|
|
19091
19143
|
}
|
|
19092
19144
|
return [...byPath.values()];
|
|
19093
19145
|
}
|
|
19094
|
-
function restoreCoderabbitOauthBlob(
|
|
19095
|
-
const dir = path38.join(
|
|
19146
|
+
function restoreCoderabbitOauthBlob(os66, value) {
|
|
19147
|
+
const dir = path38.join(os66.homeDir(), ".coderabbit");
|
|
19096
19148
|
(0, import_node_fs6.mkdirSync)(dir, { recursive: true });
|
|
19097
19149
|
let file = "auth.json";
|
|
19098
19150
|
let contents = value.trim();
|
|
@@ -19112,26 +19164,26 @@ function installFailureMessage(result) {
|
|
|
19112
19164
|
return result.error ?? "CodeRabbit CLI could not be installed";
|
|
19113
19165
|
}
|
|
19114
19166
|
async function configureCoderabbit(input, deps = {}) {
|
|
19115
|
-
const
|
|
19167
|
+
const os66 = deps.os ?? createOsStrategy();
|
|
19116
19168
|
const ensureInstalled = deps.ensureInstalled ?? ensureCoderabbitInstalled;
|
|
19117
19169
|
const isLoggedIn = deps.isLoggedIn ?? defaultIsLoggedIn;
|
|
19118
19170
|
const runOAuth = deps.runOAuthLogin ?? runCoderabbitOAuthLogin;
|
|
19119
19171
|
const snapshot = deps.snapshotDir ?? (() => snapshotCredentialDir());
|
|
19120
19172
|
const capture2 = deps.captureCredential ?? ((b) => diffCapturedCredential(b));
|
|
19121
19173
|
const loginWithApiKey = deps.loginWithApiKey ?? defaultLoginWithApiKey;
|
|
19122
|
-
const home =
|
|
19123
|
-
|
|
19124
|
-
|
|
19174
|
+
const home = os66.homeDir();
|
|
19175
|
+
os66.augmentPath(
|
|
19176
|
+
os66.id === "win32" ? [
|
|
19125
19177
|
path38.join(home, ".local", "bin"),
|
|
19126
19178
|
path38.join(process.env.APPDATA ?? path38.join(home, "AppData", "Roaming"), "npm"),
|
|
19127
19179
|
path38.join(home, "scoop", "shims")
|
|
19128
19180
|
] : [path38.join(home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin"]
|
|
19129
19181
|
);
|
|
19130
|
-
const installed2 =
|
|
19182
|
+
const installed2 = os66.findInPath("coderabbit") !== null;
|
|
19131
19183
|
const base = () => ({
|
|
19132
19184
|
action: input.action,
|
|
19133
19185
|
supported: true,
|
|
19134
|
-
installed:
|
|
19186
|
+
installed: os66.findInPath("coderabbit") !== null,
|
|
19135
19187
|
loggedIn: false
|
|
19136
19188
|
});
|
|
19137
19189
|
if (input.action === "status") {
|
|
@@ -19145,7 +19197,7 @@ async function configureCoderabbit(input, deps = {}) {
|
|
|
19145
19197
|
const key = (input.apiKey ?? "").trim();
|
|
19146
19198
|
if (!key) return { ...res2, error: "No API key provided" };
|
|
19147
19199
|
if (!res2.installed) {
|
|
19148
|
-
const inst = await ensureInstalled(
|
|
19200
|
+
const inst = await ensureInstalled(os66);
|
|
19149
19201
|
res2.installed = inst.ok;
|
|
19150
19202
|
if (!inst.ok) return { ...res2, error: installFailureMessage(inst) };
|
|
19151
19203
|
}
|
|
@@ -19169,7 +19221,7 @@ async function configureCoderabbit(input, deps = {}) {
|
|
|
19169
19221
|
}
|
|
19170
19222
|
if (!res2.installed) {
|
|
19171
19223
|
deps.onEvent?.({ kind: "installing" });
|
|
19172
|
-
const inst = await ensureInstalled(
|
|
19224
|
+
const inst = await ensureInstalled(os66);
|
|
19173
19225
|
res2.installed = inst.ok;
|
|
19174
19226
|
if (!inst.ok) return { ...res2, error: installFailureMessage(inst) };
|
|
19175
19227
|
}
|
|
@@ -19186,7 +19238,7 @@ async function configureCoderabbit(input, deps = {}) {
|
|
|
19186
19238
|
return { ...res2, loggedIn: true, linked: true };
|
|
19187
19239
|
}
|
|
19188
19240
|
try {
|
|
19189
|
-
restoreCoderabbitOauthBlob(
|
|
19241
|
+
restoreCoderabbitOauthBlob(os66, cred.credential);
|
|
19190
19242
|
} catch (err) {
|
|
19191
19243
|
return {
|
|
19192
19244
|
...res2,
|
|
@@ -19210,7 +19262,7 @@ async function configureCoderabbit(input, deps = {}) {
|
|
|
19210
19262
|
const res2 = base();
|
|
19211
19263
|
if (!installed2) {
|
|
19212
19264
|
deps.onEvent?.({ kind: "installing" });
|
|
19213
|
-
const inst = await ensureInstalled(
|
|
19265
|
+
const inst = await ensureInstalled(os66);
|
|
19214
19266
|
res2.installed = inst.ok;
|
|
19215
19267
|
if (!inst.ok) return { ...res2, error: installFailureMessage(inst) };
|
|
19216
19268
|
}
|
|
@@ -19250,7 +19302,7 @@ async function configureCoderabbit(input, deps = {}) {
|
|
|
19250
19302
|
}
|
|
19251
19303
|
const res = base();
|
|
19252
19304
|
if (!res.installed) {
|
|
19253
|
-
const inst = await ensureInstalled(
|
|
19305
|
+
const inst = await ensureInstalled(os66);
|
|
19254
19306
|
res.installed = inst.ok;
|
|
19255
19307
|
if (!inst.ok) return { ...res, error: installFailureMessage(inst) };
|
|
19256
19308
|
}
|
|
@@ -21073,7 +21125,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
21073
21125
|
if (process.env.NODE_ENV === "test") return;
|
|
21074
21126
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
21075
21127
|
if (process.env.CI) return;
|
|
21076
|
-
const current2 = true ? "2.
|
|
21128
|
+
const current2 = true ? "2.65.0" : null;
|
|
21077
21129
|
if (!current2) return;
|
|
21078
21130
|
const cache = readCache();
|
|
21079
21131
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -21090,7 +21142,7 @@ function checkForUpdates() {
|
|
|
21090
21142
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
21091
21143
|
if (process.env.CI) return;
|
|
21092
21144
|
if (!process.stdout.isTTY) return;
|
|
21093
|
-
const current2 = true ? "2.
|
|
21145
|
+
const current2 = true ? "2.65.0" : null;
|
|
21094
21146
|
if (!current2) return;
|
|
21095
21147
|
const cache = readCache();
|
|
21096
21148
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -21110,7 +21162,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
|
21110
21162
|
var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
21111
21163
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
21112
21164
|
function currentCliVersion() {
|
|
21113
|
-
return true ? "2.
|
|
21165
|
+
return true ? "2.65.0" : null;
|
|
21114
21166
|
}
|
|
21115
21167
|
function runCmd(cmd, args2, timeoutMs) {
|
|
21116
21168
|
return new Promise((resolve9) => {
|
|
@@ -22779,7 +22831,7 @@ async function defaultFetchJson(url2) {
|
|
|
22779
22831
|
async function readUsageReport(deps = {}) {
|
|
22780
22832
|
const port = deps.port ?? HEADROOM_PROXY_PORT;
|
|
22781
22833
|
const base = `http://127.0.0.1:${port}`;
|
|
22782
|
-
const get2 = deps.fetchJson ?? ((
|
|
22834
|
+
const get2 = deps.fetchJson ?? ((path91) => defaultFetchJson(`${base}${path91}`));
|
|
22783
22835
|
let history;
|
|
22784
22836
|
try {
|
|
22785
22837
|
history = await get2("/stats-history");
|
|
@@ -24696,11 +24748,11 @@ function resolveDoltInstallStrategy(platform3) {
|
|
|
24696
24748
|
}
|
|
24697
24749
|
var DOLT_RELEASE_BASE = "https://github.com/dolthub/dolt/releases/latest/download";
|
|
24698
24750
|
function doltPlatformTuple(platform3, arch2) {
|
|
24699
|
-
const
|
|
24751
|
+
const os66 = platform3 === "win32" ? "windows" : platform3 === "darwin" ? "darwin" : "linux";
|
|
24700
24752
|
const a = arch2 === "x64" ? "amd64" : arch2 === "arm64" ? "arm64" : null;
|
|
24701
24753
|
if (!a) return null;
|
|
24702
|
-
if (
|
|
24703
|
-
return `${
|
|
24754
|
+
if (os66 === "windows" && a !== "amd64") return null;
|
|
24755
|
+
return `${os66}-${a}`;
|
|
24704
24756
|
}
|
|
24705
24757
|
function resolveDoltTarballStrategy(targetDir, platform3, arch2) {
|
|
24706
24758
|
const tuple = doltPlatformTuple(platform3, arch2);
|
|
@@ -25761,8 +25813,14 @@ function dispatchPrompt(ctx, prompt) {
|
|
|
25761
25813
|
ctx.outputSvc.newTurn();
|
|
25762
25814
|
ctx.agent.sendCommand(prompt);
|
|
25763
25815
|
}
|
|
25764
|
-
var startTask = (ctx,
|
|
25816
|
+
var startTask = async (ctx, cmd, parsed) => {
|
|
25765
25817
|
const { prompt, files } = parsed;
|
|
25818
|
+
if (parsed.agentId && parsed.agentId !== ctx.agentId) {
|
|
25819
|
+
await ctx.relay.sendResult(cmd.id, "failed", {
|
|
25820
|
+
error: "Switching agents isn't supported on this session."
|
|
25821
|
+
});
|
|
25822
|
+
return;
|
|
25823
|
+
}
|
|
25766
25824
|
const effectivePrompt = prompt ?? "";
|
|
25767
25825
|
if (files && files.length > 0) {
|
|
25768
25826
|
const paths = saveFilesTemp(files);
|
|
@@ -26346,7 +26404,7 @@ var vcsAgentReviewH = async (ctx, cmd, parsed) => {
|
|
|
26346
26404
|
});
|
|
26347
26405
|
const token = ctx.pluginAuthToken;
|
|
26348
26406
|
void (async () => {
|
|
26349
|
-
const
|
|
26407
|
+
const os66 = createOsStrategy();
|
|
26350
26408
|
try {
|
|
26351
26409
|
const report = await reviewPullRequest(
|
|
26352
26410
|
{
|
|
@@ -26355,7 +26413,7 @@ var vcsAgentReviewH = async (ctx, cmd, parsed) => {
|
|
|
26355
26413
|
baseBranch: parsed.baseBranch
|
|
26356
26414
|
},
|
|
26357
26415
|
{
|
|
26358
|
-
runReview: (input) => new CoderabbitRuntimeStrategy(
|
|
26416
|
+
runReview: (input) => new CoderabbitRuntimeStrategy(os66).runOneShot(input),
|
|
26359
26417
|
runGh: (args2) => defaultRunGh(args2),
|
|
26360
26418
|
postReport: async (r) => {
|
|
26361
26419
|
if (!token) return;
|
|
@@ -27458,11 +27516,11 @@ function resolveTokenValue(args2) {
|
|
|
27458
27516
|
}
|
|
27459
27517
|
const fileFlag = args2.find((a) => a.startsWith("--token-file="));
|
|
27460
27518
|
if (fileFlag) {
|
|
27461
|
-
const
|
|
27519
|
+
const path91 = fileFlag.slice("--token-file=".length);
|
|
27462
27520
|
try {
|
|
27463
|
-
const content = fs63.readFileSync(
|
|
27464
|
-
if (content.length === 0) fail(`--token-file ${
|
|
27465
|
-
rmIfExistsQuiet(
|
|
27521
|
+
const content = fs63.readFileSync(path91, "utf8").trim();
|
|
27522
|
+
if (content.length === 0) fail(`--token-file ${path91} is empty`);
|
|
27523
|
+
rmIfExistsQuiet(path91);
|
|
27466
27524
|
return content;
|
|
27467
27525
|
} catch (err) {
|
|
27468
27526
|
fail(`Could not read --token-file: ${err.message}`);
|
|
@@ -28525,12 +28583,292 @@ async function resolveAcpAdapterWithRetry(agent, opts = {}) {
|
|
|
28525
28583
|
var import_node_crypto11 = require("crypto");
|
|
28526
28584
|
|
|
28527
28585
|
// src/services/history.service.ts
|
|
28528
|
-
var
|
|
28529
|
-
var
|
|
28530
|
-
var
|
|
28586
|
+
var fs66 = __toESM(require("fs"));
|
|
28587
|
+
var path72 = __toESM(require("path"));
|
|
28588
|
+
var os56 = __toESM(require("os"));
|
|
28531
28589
|
var https7 = __toESM(require("https"));
|
|
28532
28590
|
var http6 = __toESM(require("http"));
|
|
28533
28591
|
var import_zod2 = require("zod");
|
|
28592
|
+
|
|
28593
|
+
// src/agents/acp/squad-roster.ts
|
|
28594
|
+
var fs65 = __toESM(require("fs"));
|
|
28595
|
+
var os55 = __toESM(require("os"));
|
|
28596
|
+
var path71 = __toESM(require("path"));
|
|
28597
|
+
var PROMPT_MAX = 500;
|
|
28598
|
+
var REPLY_SUMMARY_MAX = 1e3;
|
|
28599
|
+
var PREAMBLE_MAX = 2e3;
|
|
28600
|
+
var BRIEFING_DEFAULT_MAX = 8e3;
|
|
28601
|
+
var GENERIC_SPECIALTY = "general implementation tasks";
|
|
28602
|
+
function clip(s, max) {
|
|
28603
|
+
return s.length > max ? s.slice(0, max) : s;
|
|
28604
|
+
}
|
|
28605
|
+
function defaultMember() {
|
|
28606
|
+
return {
|
|
28607
|
+
acpSessionId: null,
|
|
28608
|
+
provisioned: false,
|
|
28609
|
+
binaryVerified: false,
|
|
28610
|
+
lastTurnIndex: 0,
|
|
28611
|
+
contextTextFallback: false
|
|
28612
|
+
};
|
|
28613
|
+
}
|
|
28614
|
+
function journalPathFor(homeDir2, sessionId) {
|
|
28615
|
+
return path71.join(homeDir2, ".codeam", `squad-journal-${sessionId}.json`);
|
|
28616
|
+
}
|
|
28617
|
+
function loadJournal(journalPath) {
|
|
28618
|
+
try {
|
|
28619
|
+
const raw = JSON.parse(fs65.readFileSync(journalPath, "utf-8"));
|
|
28620
|
+
return Array.isArray(raw.turns) ? raw.turns : [];
|
|
28621
|
+
} catch {
|
|
28622
|
+
return [];
|
|
28623
|
+
}
|
|
28624
|
+
}
|
|
28625
|
+
var SquadState = class {
|
|
28626
|
+
/** Set by the caller after fetchSquadRoster; null until then. */
|
|
28627
|
+
roster = null;
|
|
28628
|
+
/**
|
|
28629
|
+
* Autonomous chained handoffs (P2-2, PRO). Seeded from the persisted
|
|
28630
|
+
* `SavedSession.squadAuto` at session start and rewritten by
|
|
28631
|
+
* `squad_configure`. Read-only to callers — mutate via {@link setAuto}, which
|
|
28632
|
+
* clamps the budget and re-arms the chain.
|
|
28633
|
+
*/
|
|
28634
|
+
autoConfig = { enabled: false, hopBudget: SQUAD_HOP_BUDGET_DEFAULT };
|
|
28635
|
+
/**
|
|
28636
|
+
* Hops left in the CURRENT chain. Reset to the budget on every USER-initiated
|
|
28637
|
+
* prompt (so a user turn always interrupts and re-arms) and decremented by
|
|
28638
|
+
* each self-accepted handoff.
|
|
28639
|
+
*/
|
|
28640
|
+
hopsLeft = 0;
|
|
28641
|
+
/** Lifetime handoff counters for `squad_stats` (in-memory, this process). */
|
|
28642
|
+
handoffCounters = { proposed: 0, accepted: 0, auto: 0 };
|
|
28643
|
+
journalPath;
|
|
28644
|
+
turns;
|
|
28645
|
+
members = /* @__PURE__ */ new Map();
|
|
28646
|
+
constructor(opts) {
|
|
28647
|
+
this.journalPath = journalPathFor(opts.homeDir ?? os55.homedir(), opts.sessionId);
|
|
28648
|
+
this.turns = loadJournal(this.journalPath);
|
|
28649
|
+
if (opts.auto) this.setAuto(opts.auto);
|
|
28650
|
+
}
|
|
28651
|
+
get auto() {
|
|
28652
|
+
return this.autoConfig;
|
|
28653
|
+
}
|
|
28654
|
+
/** Apply a new mode (clamping the budget) and re-arm the chain. */
|
|
28655
|
+
setAuto(value) {
|
|
28656
|
+
this.autoConfig = {
|
|
28657
|
+
enabled: value.enabled === true,
|
|
28658
|
+
hopBudget: clampHopBudget(value.hopBudget)
|
|
28659
|
+
};
|
|
28660
|
+
this.resetHops();
|
|
28661
|
+
return this.autoConfig;
|
|
28662
|
+
}
|
|
28663
|
+
hopsRemaining() {
|
|
28664
|
+
return this.hopsLeft;
|
|
28665
|
+
}
|
|
28666
|
+
/** Re-arm the chain — called at the start of every USER-initiated turn. */
|
|
28667
|
+
resetHops() {
|
|
28668
|
+
this.hopsLeft = this.autoConfig.enabled ? this.autoConfig.hopBudget : 0;
|
|
28669
|
+
}
|
|
28670
|
+
/** Spend one hop on a self-accepted handoff. */
|
|
28671
|
+
consumeHop() {
|
|
28672
|
+
if (this.hopsLeft > 0) this.hopsLeft -= 1;
|
|
28673
|
+
}
|
|
28674
|
+
countProposal(opts) {
|
|
28675
|
+
this.handoffCounters.proposed += 1;
|
|
28676
|
+
if (opts.auto) this.handoffCounters.auto += 1;
|
|
28677
|
+
}
|
|
28678
|
+
countAccepted() {
|
|
28679
|
+
this.handoffCounters.accepted += 1;
|
|
28680
|
+
}
|
|
28681
|
+
/**
|
|
28682
|
+
* Per-member activity for the `squad_stats` relay command, derived from the
|
|
28683
|
+
* journal (the same shared history the delta briefing reads) plus the
|
|
28684
|
+
* in-memory handoff counters. `filesTouched` counts DISTINCT paths — a member
|
|
28685
|
+
* that edited the same file across three turns touched ONE file.
|
|
28686
|
+
*/
|
|
28687
|
+
stats() {
|
|
28688
|
+
const byAgent = /* @__PURE__ */ new Map();
|
|
28689
|
+
for (const t2 of this.turns) {
|
|
28690
|
+
let row = byAgent.get(t2.agentId);
|
|
28691
|
+
if (!row) {
|
|
28692
|
+
row = { turns: 0, files: /* @__PURE__ */ new Set() };
|
|
28693
|
+
byAgent.set(t2.agentId, row);
|
|
28694
|
+
}
|
|
28695
|
+
row.turns += 1;
|
|
28696
|
+
for (const f of t2.filesTouched) row.files.add(f);
|
|
28697
|
+
}
|
|
28698
|
+
const members = [...byAgent.entries()].map(([agentId, row]) => ({
|
|
28699
|
+
agentId,
|
|
28700
|
+
turns: row.turns,
|
|
28701
|
+
filesTouched: row.files.size
|
|
28702
|
+
}));
|
|
28703
|
+
return { members, handoffs: { ...this.handoffCounters }, sinceTurn: 1 };
|
|
28704
|
+
}
|
|
28705
|
+
/** Returns (creating on first access) the mutable per-agent state. */
|
|
28706
|
+
member(agentId) {
|
|
28707
|
+
let m = this.members.get(agentId);
|
|
28708
|
+
if (!m) {
|
|
28709
|
+
m = defaultMember();
|
|
28710
|
+
this.members.set(agentId, m);
|
|
28711
|
+
}
|
|
28712
|
+
return m;
|
|
28713
|
+
}
|
|
28714
|
+
/** Appends a journal entry and persists (fire-and-forget durability). */
|
|
28715
|
+
recordTurn(entry) {
|
|
28716
|
+
const turn = {
|
|
28717
|
+
turn: this.turns.length + 1,
|
|
28718
|
+
agentId: entry.agentId,
|
|
28719
|
+
prompt: clip(entry.prompt, PROMPT_MAX),
|
|
28720
|
+
replySummary: clip(entry.replySummary, REPLY_SUMMARY_MAX),
|
|
28721
|
+
filesTouched: entry.filesTouched
|
|
28722
|
+
};
|
|
28723
|
+
this.turns.push(turn);
|
|
28724
|
+
this.persist();
|
|
28725
|
+
}
|
|
28726
|
+
turnCount() {
|
|
28727
|
+
return this.turns.length;
|
|
28728
|
+
}
|
|
28729
|
+
entriesSince(turnIndex) {
|
|
28730
|
+
return this.turns.filter((t2) => t2.turn > turnIndex);
|
|
28731
|
+
}
|
|
28732
|
+
persist() {
|
|
28733
|
+
try {
|
|
28734
|
+
fs65.mkdirSync(path71.dirname(this.journalPath), { recursive: true, mode: 448 });
|
|
28735
|
+
fs65.writeFileSync(this.journalPath, JSON.stringify({ turns: this.turns }), { mode: 384 });
|
|
28736
|
+
} catch {
|
|
28737
|
+
}
|
|
28738
|
+
}
|
|
28739
|
+
};
|
|
28740
|
+
function specialtyFor(agentId) {
|
|
28741
|
+
return SQUAD_SPECIALTIES[agentId] ?? GENERIC_SPECIALTY;
|
|
28742
|
+
}
|
|
28743
|
+
var TEAM_PREAMBLE_MARKER = "[Team context]";
|
|
28744
|
+
var TEAM_PREAMBLE_LINES = [
|
|
28745
|
+
"[Team context] You are the active agent in a CodeAgent Mobile session where the user",
|
|
28746
|
+
"has a squad of agents and can pass work between them. Your available teammates:",
|
|
28747
|
+
"If a task clearly fits a teammate better than you, you MAY propose a handoff by ending",
|
|
28748
|
+
`your reply with a fenced code block tagged ${HANDOFF_FENCE_TAG} containing ONE JSON object:`,
|
|
28749
|
+
'{"to":"<teammate id>","reason":"<one sentence>","prompt":"<the prompt they should run>"}',
|
|
28750
|
+
"Propose at most one handoff per reply, only when genuinely better, and never announce",
|
|
28751
|
+
"the block in prose \u2014 the app renders it as a card the user can accept."
|
|
28752
|
+
];
|
|
28753
|
+
var TEAM_PREAMBLE_BULLET_RE = /^- .+ — best at: /;
|
|
28754
|
+
var BRIEFING_MARKER = "[Team update]";
|
|
28755
|
+
var BRIEFING_HEADER = "[Team update] While you were away, other agents worked on this session:";
|
|
28756
|
+
var BRIEFING_FOOTER = "Continue from the CURRENT state of the working tree.";
|
|
28757
|
+
function buildTeamPreamble(roster, currentAgent, opts) {
|
|
28758
|
+
const others = roster.agents.filter((a) => a.agentId !== currentAgent);
|
|
28759
|
+
if (others.length === 0) return null;
|
|
28760
|
+
const lines = [
|
|
28761
|
+
TEAM_PREAMBLE_LINES[0],
|
|
28762
|
+
TEAM_PREAMBLE_LINES[1],
|
|
28763
|
+
...others.map((a) => `- ${a.displayName} \u2014 best at: ${specialtyFor(a.agentId)}`)
|
|
28764
|
+
];
|
|
28765
|
+
if (opts.handoffInstructions) {
|
|
28766
|
+
lines.push(...TEAM_PREAMBLE_LINES.slice(2));
|
|
28767
|
+
}
|
|
28768
|
+
return clip(lines.join("\n"), PREAMBLE_MAX);
|
|
28769
|
+
}
|
|
28770
|
+
function renderJournalEntry(e) {
|
|
28771
|
+
const filesClause = e.filesTouched.length > 0 ? ` (files: ${e.filesTouched.join(", ")})` : "";
|
|
28772
|
+
return `- turn ${e.turn} (${e.agentId}): ${e.prompt} \u2192 ${e.replySummary}${filesClause}`;
|
|
28773
|
+
}
|
|
28774
|
+
function buildDeltaBriefing(entries, maxChars = BRIEFING_DEFAULT_MAX) {
|
|
28775
|
+
if (entries.length === 0) return null;
|
|
28776
|
+
const header = BRIEFING_HEADER;
|
|
28777
|
+
const footer = BRIEFING_FOOTER;
|
|
28778
|
+
const envelope = header.length + 1 + footer.length + 1;
|
|
28779
|
+
const sorted = [...entries].sort((a, b) => a.turn - b.turn);
|
|
28780
|
+
const lines = [];
|
|
28781
|
+
let bodyLen = 0;
|
|
28782
|
+
for (let i = sorted.length - 1; i >= 0; i--) {
|
|
28783
|
+
const line = renderJournalEntry(sorted[i]);
|
|
28784
|
+
const addedLen = line.length + (lines.length > 0 ? 1 : 0);
|
|
28785
|
+
if (lines.length > 0 && envelope + bodyLen + addedLen > maxChars) break;
|
|
28786
|
+
lines.unshift(line);
|
|
28787
|
+
bodyLen += addedLen;
|
|
28788
|
+
}
|
|
28789
|
+
return [header, lines.join("\n"), footer].join("\n");
|
|
28790
|
+
}
|
|
28791
|
+
|
|
28792
|
+
// src/agents/acp/squad-context.ts
|
|
28793
|
+
var SQUAD_CONTEXT_URI = "codeam://squad-context";
|
|
28794
|
+
var HANDOFF_MARKER = "[Session handoff]";
|
|
28795
|
+
var HANDOFF_TERMINATOR = "--- End of handoff context ---";
|
|
28796
|
+
function buildSquadContextBlock(text) {
|
|
28797
|
+
return {
|
|
28798
|
+
type: "resource",
|
|
28799
|
+
resource: { uri: SQUAD_CONTEXT_URI, mimeType: "text/plain", text }
|
|
28800
|
+
};
|
|
28801
|
+
}
|
|
28802
|
+
function isSquadContextBlock(block) {
|
|
28803
|
+
return block.type === "resource" && block.resource.uri === SQUAD_CONTEXT_URI;
|
|
28804
|
+
}
|
|
28805
|
+
function looksLikeUnsupportedPromptShape(err) {
|
|
28806
|
+
const code = err?.code;
|
|
28807
|
+
if (code === -32602) return true;
|
|
28808
|
+
const message = err instanceof Error ? err.message : String(err ?? "");
|
|
28809
|
+
if (/invalid[ _]params|-32602/i.test(message)) return true;
|
|
28810
|
+
return /\bresource\b/i.test(message) && /unsupported|not supported/i.test(message);
|
|
28811
|
+
}
|
|
28812
|
+
function skipWhile(lines, start2, keepGoing) {
|
|
28813
|
+
let i = start2;
|
|
28814
|
+
while (i < lines.length && keepGoing(lines[i])) i++;
|
|
28815
|
+
return i;
|
|
28816
|
+
}
|
|
28817
|
+
function endOfTeamPreamble(lines, start2) {
|
|
28818
|
+
if (lines[start2] !== TEAM_PREAMBLE_LINES[0]) return -1;
|
|
28819
|
+
if (lines[start2 + 1] !== TEAM_PREAMBLE_LINES[1]) return -1;
|
|
28820
|
+
return skipWhile(
|
|
28821
|
+
lines,
|
|
28822
|
+
start2 + 2,
|
|
28823
|
+
(line) => TEAM_PREAMBLE_BULLET_RE.test(line) || TEAM_PREAMBLE_LINES.includes(line)
|
|
28824
|
+
);
|
|
28825
|
+
}
|
|
28826
|
+
function endOfTerminatedBlock(lines, start2, terminator) {
|
|
28827
|
+
for (let i = start2; i < lines.length; i++) {
|
|
28828
|
+
if (lines[i].trimEnd() === terminator) return i + 1;
|
|
28829
|
+
}
|
|
28830
|
+
return -1;
|
|
28831
|
+
}
|
|
28832
|
+
function stripSquadContext(text) {
|
|
28833
|
+
if (!text.includes(HANDOFF_MARKER) && !text.includes(BRIEFING_MARKER) && !text.includes(TEAM_PREAMBLE_MARKER)) {
|
|
28834
|
+
return text;
|
|
28835
|
+
}
|
|
28836
|
+
const lines = text.split("\n");
|
|
28837
|
+
const kept = [];
|
|
28838
|
+
let i = 0;
|
|
28839
|
+
let stripped = false;
|
|
28840
|
+
while (i < lines.length) {
|
|
28841
|
+
const line = lines[i];
|
|
28842
|
+
if (line.startsWith(HANDOFF_MARKER)) {
|
|
28843
|
+
const end = endOfTerminatedBlock(lines, i, HANDOFF_TERMINATOR);
|
|
28844
|
+
if (end !== -1) {
|
|
28845
|
+
i = end;
|
|
28846
|
+
stripped = true;
|
|
28847
|
+
continue;
|
|
28848
|
+
}
|
|
28849
|
+
} else if (line.startsWith(BRIEFING_MARKER)) {
|
|
28850
|
+
const end = endOfTerminatedBlock(lines, i, BRIEFING_FOOTER);
|
|
28851
|
+
if (end !== -1) {
|
|
28852
|
+
i = end;
|
|
28853
|
+
stripped = true;
|
|
28854
|
+
continue;
|
|
28855
|
+
}
|
|
28856
|
+
} else if (line.startsWith(TEAM_PREAMBLE_MARKER)) {
|
|
28857
|
+
const end = endOfTeamPreamble(lines, i);
|
|
28858
|
+
if (end !== -1) {
|
|
28859
|
+
i = end;
|
|
28860
|
+
stripped = true;
|
|
28861
|
+
continue;
|
|
28862
|
+
}
|
|
28863
|
+
}
|
|
28864
|
+
kept.push(line);
|
|
28865
|
+
i++;
|
|
28866
|
+
}
|
|
28867
|
+
if (!stripped) return text;
|
|
28868
|
+
return kept.join("\n").trim();
|
|
28869
|
+
}
|
|
28870
|
+
|
|
28871
|
+
// src/services/history.service.ts
|
|
28534
28872
|
var historyRecordSchema = import_zod2.z.object({
|
|
28535
28873
|
type: import_zod2.z.string().optional(),
|
|
28536
28874
|
timestamp: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
|
|
@@ -28550,11 +28888,24 @@ function extractText3(content) {
|
|
|
28550
28888
|
return "";
|
|
28551
28889
|
}
|
|
28552
28890
|
var CONVERSATION_BATCH_SIZE = 30;
|
|
28891
|
+
function scrubSquadContext(messages) {
|
|
28892
|
+
const out2 = [];
|
|
28893
|
+
for (const m of messages) {
|
|
28894
|
+
const text = stripSquadContext(m.text);
|
|
28895
|
+
if (text === m.text) {
|
|
28896
|
+
out2.push(m);
|
|
28897
|
+
continue;
|
|
28898
|
+
}
|
|
28899
|
+
if (text.length === 0) continue;
|
|
28900
|
+
out2.push({ ...m, text });
|
|
28901
|
+
}
|
|
28902
|
+
return out2;
|
|
28903
|
+
}
|
|
28553
28904
|
function parseJsonl(filePath) {
|
|
28554
28905
|
const messages = [];
|
|
28555
28906
|
let raw;
|
|
28556
28907
|
try {
|
|
28557
|
-
raw =
|
|
28908
|
+
raw = fs66.readFileSync(filePath, "utf8");
|
|
28558
28909
|
} catch (err) {
|
|
28559
28910
|
if (err.code !== "ENOENT") {
|
|
28560
28911
|
log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
|
|
@@ -28695,7 +29046,7 @@ var HistoryService = class _HistoryService {
|
|
|
28695
29046
|
return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
|
|
28696
29047
|
}
|
|
28697
29048
|
get projectDir() {
|
|
28698
|
-
return this.runtime.resolveHistoryDir(this.cwd) ??
|
|
29049
|
+
return this.runtime.resolveHistoryDir(this.cwd) ?? path72.join(os56.homedir(), ".claude", "projects", encodeCwd(this.cwd));
|
|
28699
29050
|
}
|
|
28700
29051
|
/** Set the current Claude conversation ID (extracted from /cost command or session start) */
|
|
28701
29052
|
setCurrentConversationId(id) {
|
|
@@ -28707,7 +29058,7 @@ var HistoryService = class _HistoryService {
|
|
|
28707
29058
|
/** Return the current message count in the active conversation. */
|
|
28708
29059
|
getCurrentMessageCount() {
|
|
28709
29060
|
if (!this.currentConversationId) return 0;
|
|
28710
|
-
const filePath =
|
|
29061
|
+
const filePath = path72.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
28711
29062
|
return parseJsonl(filePath).length;
|
|
28712
29063
|
}
|
|
28713
29064
|
/**
|
|
@@ -28718,7 +29069,7 @@ var HistoryService = class _HistoryService {
|
|
|
28718
29069
|
const deadline = Date.now() + timeoutMs;
|
|
28719
29070
|
while (Date.now() < deadline) {
|
|
28720
29071
|
if (!this.currentConversationId) return null;
|
|
28721
|
-
const filePath =
|
|
29072
|
+
const filePath = path72.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
28722
29073
|
const messages = parseJsonl(filePath);
|
|
28723
29074
|
if (messages.length > previousCount) {
|
|
28724
29075
|
for (let i = messages.length - 1; i >= previousCount; i--) {
|
|
@@ -28744,16 +29095,16 @@ var HistoryService = class _HistoryService {
|
|
|
28744
29095
|
const dir = this.projectDir;
|
|
28745
29096
|
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
28746
29097
|
try {
|
|
28747
|
-
const files =
|
|
29098
|
+
const files = fs66.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
28748
29099
|
try {
|
|
28749
|
-
const stat3 =
|
|
29100
|
+
const stat3 = fs66.statSync(path72.join(dir, e.name));
|
|
28750
29101
|
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
28751
29102
|
} catch {
|
|
28752
29103
|
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
28753
29104
|
}
|
|
28754
29105
|
}).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
|
|
28755
29106
|
if (files.length > 0) {
|
|
28756
|
-
this.currentConversationId =
|
|
29107
|
+
this.currentConversationId = path72.basename(files[0].name, ".jsonl");
|
|
28757
29108
|
}
|
|
28758
29109
|
} catch {
|
|
28759
29110
|
}
|
|
@@ -28787,13 +29138,13 @@ var HistoryService = class _HistoryService {
|
|
|
28787
29138
|
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
28788
29139
|
let entries;
|
|
28789
29140
|
try {
|
|
28790
|
-
entries =
|
|
29141
|
+
entries = fs66.readdirSync(dir, { withFileTypes: true });
|
|
28791
29142
|
} catch {
|
|
28792
29143
|
return null;
|
|
28793
29144
|
}
|
|
28794
29145
|
const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
28795
29146
|
try {
|
|
28796
|
-
const stat3 =
|
|
29147
|
+
const stat3 = fs66.statSync(path72.join(dir, e.name));
|
|
28797
29148
|
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
28798
29149
|
} catch {
|
|
28799
29150
|
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
@@ -28802,12 +29153,12 @@ var HistoryService = class _HistoryService {
|
|
|
28802
29153
|
if (files.length === 0) return null;
|
|
28803
29154
|
const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
|
|
28804
29155
|
if (!files.some((f) => f.name === targetFile)) return null;
|
|
28805
|
-
return this.extractUsageFromFile(
|
|
29156
|
+
return this.extractUsageFromFile(path72.join(dir, targetFile));
|
|
28806
29157
|
}
|
|
28807
29158
|
extractUsageFromFile(filePath) {
|
|
28808
29159
|
let raw;
|
|
28809
29160
|
try {
|
|
28810
|
-
raw =
|
|
29161
|
+
raw = fs66.readFileSync(filePath, "utf8");
|
|
28811
29162
|
} catch {
|
|
28812
29163
|
return null;
|
|
28813
29164
|
}
|
|
@@ -28852,9 +29203,9 @@ var HistoryService = class _HistoryService {
|
|
|
28852
29203
|
let totalCost = 0;
|
|
28853
29204
|
let files;
|
|
28854
29205
|
try {
|
|
28855
|
-
files =
|
|
29206
|
+
files = fs66.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
|
|
28856
29207
|
try {
|
|
28857
|
-
return
|
|
29208
|
+
return fs66.statSync(path72.join(projectDir, f)).mtimeMs >= monthStartMs;
|
|
28858
29209
|
} catch {
|
|
28859
29210
|
return false;
|
|
28860
29211
|
}
|
|
@@ -28865,7 +29216,7 @@ var HistoryService = class _HistoryService {
|
|
|
28865
29216
|
for (const file of files) {
|
|
28866
29217
|
let raw;
|
|
28867
29218
|
try {
|
|
28868
|
-
raw =
|
|
29219
|
+
raw = fs66.readFileSync(path72.join(projectDir, file), "utf8");
|
|
28869
29220
|
} catch {
|
|
28870
29221
|
continue;
|
|
28871
29222
|
}
|
|
@@ -28944,7 +29295,7 @@ var HistoryService = class _HistoryService {
|
|
|
28944
29295
|
if (this.runtime.resolveHistoryFile) {
|
|
28945
29296
|
return this.runtime.resolveHistoryFile(this.cwd, sessionId);
|
|
28946
29297
|
}
|
|
28947
|
-
return
|
|
29298
|
+
return path72.join(this.projectDir, `${sessionId}.jsonl`);
|
|
28948
29299
|
}
|
|
28949
29300
|
/**
|
|
28950
29301
|
* Parse a conversation's messages from disk, agent-aware. Claude uses the
|
|
@@ -28956,6 +29307,10 @@ var HistoryService = class _HistoryService {
|
|
|
28956
29307
|
* convention as parseJsonl.
|
|
28957
29308
|
*/
|
|
28958
29309
|
readConversation(sessionId) {
|
|
29310
|
+
const agentId = this.runtime.id;
|
|
29311
|
+
return scrubSquadContext(this.readConversationRaw(sessionId)).map((m) => ({ ...m, agentId }));
|
|
29312
|
+
}
|
|
29313
|
+
readConversationRaw(sessionId) {
|
|
28959
29314
|
if (this.runtime.resolveHistoryFile) {
|
|
28960
29315
|
const filePath = this.runtime.resolveHistoryFile(this.cwd, sessionId);
|
|
28961
29316
|
if (!filePath) return [];
|
|
@@ -28978,7 +29333,7 @@ var HistoryService = class _HistoryService {
|
|
|
28978
29333
|
};
|
|
28979
29334
|
});
|
|
28980
29335
|
}
|
|
28981
|
-
return parseJsonl(
|
|
29336
|
+
return parseJsonl(path72.join(this.projectDir, `${sessionId}.jsonl`));
|
|
28982
29337
|
}
|
|
28983
29338
|
async loadConversation(sessionId) {
|
|
28984
29339
|
const messages = this.readConversation(sessionId);
|
|
@@ -29046,7 +29401,7 @@ var HistoryService = class _HistoryService {
|
|
|
29046
29401
|
if (!filePath) return false;
|
|
29047
29402
|
let mtimeMs;
|
|
29048
29403
|
try {
|
|
29049
|
-
mtimeMs =
|
|
29404
|
+
mtimeMs = fs66.statSync(filePath).mtimeMs;
|
|
29050
29405
|
} catch {
|
|
29051
29406
|
return false;
|
|
29052
29407
|
}
|
|
@@ -29121,10 +29476,10 @@ var HistoryService = class _HistoryService {
|
|
|
29121
29476
|
|
|
29122
29477
|
// src/agents/acp/client.ts
|
|
29123
29478
|
var import_node_child_process29 = require("child_process");
|
|
29124
|
-
var
|
|
29479
|
+
var fs67 = __toESM(require("fs/promises"));
|
|
29125
29480
|
var fsSync = __toESM(require("fs"));
|
|
29126
|
-
var
|
|
29127
|
-
var
|
|
29481
|
+
var os58 = __toESM(require("os"));
|
|
29482
|
+
var path74 = __toESM(require("path"));
|
|
29128
29483
|
var import_node_stream = require("stream");
|
|
29129
29484
|
|
|
29130
29485
|
// ../../node_modules/@agentclientprotocol/sdk/dist/schema/index.js
|
|
@@ -33150,8 +33505,8 @@ function createIdleTimeout(idleMs, makeError, activeIdleMs = idleMs) {
|
|
|
33150
33505
|
}
|
|
33151
33506
|
|
|
33152
33507
|
// src/agents/acp/internal-paths.ts
|
|
33153
|
-
var
|
|
33154
|
-
var
|
|
33508
|
+
var path73 = __toESM(require("path"));
|
|
33509
|
+
var os57 = __toESM(require("os"));
|
|
33155
33510
|
var INTERNAL_TOKENS = [".codeam", "house-claude"];
|
|
33156
33511
|
var SELF_HOSTED_WORKSPACE_RE = /\.codeam[/\\]self-hosted/gi;
|
|
33157
33512
|
function textReferencesInternal(text) {
|
|
@@ -33159,13 +33514,13 @@ function textReferencesInternal(text) {
|
|
|
33159
33514
|
const scrubbed = text.replace(SELF_HOSTED_WORKSPACE_RE, " ");
|
|
33160
33515
|
return INTERNAL_TOKENS.some((t2) => scrubbed.includes(t2));
|
|
33161
33516
|
}
|
|
33162
|
-
function pathIsInternal(p2, homeDir2 =
|
|
33517
|
+
function pathIsInternal(p2, homeDir2 = os57.homedir()) {
|
|
33163
33518
|
if (!p2) return false;
|
|
33164
|
-
const abs =
|
|
33165
|
-
const home =
|
|
33166
|
-
const within = (root) => abs === root || abs.startsWith(root +
|
|
33167
|
-
if (within(
|
|
33168
|
-
return within(
|
|
33519
|
+
const abs = path73.resolve(p2);
|
|
33520
|
+
const home = path73.resolve(homeDir2);
|
|
33521
|
+
const within = (root) => abs === root || abs.startsWith(root + path73.sep);
|
|
33522
|
+
if (within(path73.join(home, ".codeam", "self-hosted"))) return false;
|
|
33523
|
+
return within(path73.join(home, ".codeam")) || within(path73.join(home, ".beads")) || abs === path73.join(home, ".codeam-host.log") || abs.includes(`${path73.sep}house-claude${path73.sep}`) || abs.endsWith(`${path73.sep}house-claude`);
|
|
33169
33524
|
}
|
|
33170
33525
|
function toolCallReferencesInternal(call) {
|
|
33171
33526
|
if (textReferencesInternal(call.title)) return true;
|
|
@@ -34124,7 +34479,7 @@ var AcpClient = class {
|
|
|
34124
34479
|
throw new RequestError(-32002, GUARDRAIL_SECRET_READ_BLOCK_REASON, { uri: params.path });
|
|
34125
34480
|
}
|
|
34126
34481
|
try {
|
|
34127
|
-
const content = await
|
|
34482
|
+
const content = await fs67.readFile(params.path, "utf8");
|
|
34128
34483
|
return applyLineRange(content, params.line ?? null, params.limit ?? null);
|
|
34129
34484
|
} catch (err) {
|
|
34130
34485
|
const code = err.code;
|
|
@@ -34147,7 +34502,7 @@ var AcpClient = class {
|
|
|
34147
34502
|
throw new RequestError(-32002, INTERNAL_BLOCK_REASON, { uri: params.path });
|
|
34148
34503
|
}
|
|
34149
34504
|
try {
|
|
34150
|
-
await
|
|
34505
|
+
await fs67.writeFile(params.path, params.content, "utf8");
|
|
34151
34506
|
return {};
|
|
34152
34507
|
} catch (err) {
|
|
34153
34508
|
const code = err.code;
|
|
@@ -34207,29 +34562,29 @@ function applyLineRange(content, line, limit) {
|
|
|
34207
34562
|
return { content: lines.slice(start2, end).join("\n") };
|
|
34208
34563
|
}
|
|
34209
34564
|
function knownAgentBinaryDirs() {
|
|
34210
|
-
const home =
|
|
34565
|
+
const home = os58.homedir();
|
|
34211
34566
|
const out2 = [];
|
|
34212
34567
|
out2.push("/tmp/codeam-node20/bin");
|
|
34213
34568
|
for (const root of [
|
|
34214
34569
|
"/usr/local/share/nvm/versions/node",
|
|
34215
|
-
|
|
34570
|
+
path74.join(home, ".nvm/versions/node")
|
|
34216
34571
|
]) {
|
|
34217
34572
|
try {
|
|
34218
34573
|
for (const child of fsSync.readdirSync(root)) {
|
|
34219
|
-
out2.push(
|
|
34574
|
+
out2.push(path74.join(root, child, "bin"));
|
|
34220
34575
|
}
|
|
34221
34576
|
} catch {
|
|
34222
34577
|
}
|
|
34223
34578
|
}
|
|
34224
|
-
out2.push(
|
|
34579
|
+
out2.push(path74.join(home, ".volta/bin"));
|
|
34225
34580
|
out2.push("/usr/local/bin");
|
|
34226
34581
|
out2.push("/usr/bin");
|
|
34227
|
-
out2.push(
|
|
34228
|
-
out2.push(
|
|
34582
|
+
out2.push(path74.join(home, ".local/bin"));
|
|
34583
|
+
out2.push(path74.join(home, "bin"));
|
|
34229
34584
|
if (process.platform === "win32") {
|
|
34230
34585
|
const { LOCALAPPDATA, APPDATA } = process.env;
|
|
34231
|
-
if (LOCALAPPDATA) out2.push(
|
|
34232
|
-
if (APPDATA) out2.push(
|
|
34586
|
+
if (LOCALAPPDATA) out2.push(path74.join(LOCALAPPDATA, "cursor-agent"));
|
|
34587
|
+
if (APPDATA) out2.push(path74.join(APPDATA, "npm"));
|
|
34233
34588
|
}
|
|
34234
34589
|
return out2.filter((p2) => {
|
|
34235
34590
|
try {
|
|
@@ -34241,7 +34596,7 @@ function knownAgentBinaryDirs() {
|
|
|
34241
34596
|
}
|
|
34242
34597
|
function expandPathForAgentBinaries(existingPath) {
|
|
34243
34598
|
const existing = new Set(
|
|
34244
|
-
existingPath.split(
|
|
34599
|
+
existingPath.split(path74.delimiter).filter((p2) => p2.length > 0)
|
|
34245
34600
|
);
|
|
34246
34601
|
const additions = [];
|
|
34247
34602
|
for (const dir of knownAgentBinaryDirs()) {
|
|
@@ -34251,7 +34606,7 @@ function expandPathForAgentBinaries(existingPath) {
|
|
|
34251
34606
|
}
|
|
34252
34607
|
}
|
|
34253
34608
|
if (additions.length === 0) return existingPath;
|
|
34254
|
-
return [...additions, existingPath].filter((p2) => p2.length > 0).join(
|
|
34609
|
+
return [...additions, existingPath].filter((p2) => p2.length > 0).join(path74.delimiter);
|
|
34255
34610
|
}
|
|
34256
34611
|
|
|
34257
34612
|
// src/agents/acp/headroom-budget-proxy.ts
|
|
@@ -34370,7 +34725,7 @@ function makeSerializedSwitchEmitter(post2) {
|
|
|
34370
34725
|
return chain;
|
|
34371
34726
|
};
|
|
34372
34727
|
}
|
|
34373
|
-
async function performAgentSwitch(deps, rawAgentId) {
|
|
34728
|
+
async function performAgentSwitch(deps, rawAgentId, fastPath = {}) {
|
|
34374
34729
|
const from = deps.currentAgent();
|
|
34375
34730
|
const target = resolveSwitchTarget(rawAgentId, from);
|
|
34376
34731
|
if (!target.ok) {
|
|
@@ -34386,22 +34741,28 @@ async function performAgentSwitch(deps, rawAgentId) {
|
|
|
34386
34741
|
};
|
|
34387
34742
|
log.info("switchAgent", `switch requested ${from} \u2192 ${agentId}`);
|
|
34388
34743
|
void emitStatus({ state: "switching", agentId, fromAgentId: from });
|
|
34389
|
-
|
|
34390
|
-
|
|
34391
|
-
|
|
34392
|
-
|
|
34393
|
-
|
|
34394
|
-
|
|
34744
|
+
let installScript;
|
|
34745
|
+
if (!fastPath.skipProvision) {
|
|
34746
|
+
void emitStep("credential");
|
|
34747
|
+
const cred = await deps.fetchCredential(agentId);
|
|
34748
|
+
if (!cred) {
|
|
34749
|
+
return fail2(
|
|
34750
|
+
`No linked credential for ${displayName(agentId)}. Link it in Profile \u203A Agents first.`
|
|
34751
|
+
);
|
|
34752
|
+
}
|
|
34753
|
+
try {
|
|
34754
|
+
deps.provisionCredential(agentId, toAgentAuth(cred.method, cred.credential));
|
|
34755
|
+
} catch (err) {
|
|
34756
|
+
log.warn("switchAgent", `credential provisioning failed: ${err.message}`);
|
|
34757
|
+
return fail2(`Couldn't write the ${displayName(agentId)} credential on this machine.`);
|
|
34758
|
+
}
|
|
34759
|
+
installScript = cred.installScript;
|
|
34395
34760
|
}
|
|
34396
|
-
|
|
34397
|
-
|
|
34398
|
-
|
|
34399
|
-
|
|
34400
|
-
return fail2(`Couldn't write the ${displayName(agentId)} credential on this machine.`);
|
|
34761
|
+
if (!fastPath.skipInstall) {
|
|
34762
|
+
void emitStep("install");
|
|
34763
|
+
const bin = await deps.ensureBinary(agentId, installScript);
|
|
34764
|
+
if (!bin.ok) return fail2(bin.error);
|
|
34401
34765
|
}
|
|
34402
|
-
void emitStep("install");
|
|
34403
|
-
const bin = await deps.ensureBinary(agentId, cred.installScript);
|
|
34404
|
-
if (!bin.ok) return fail2(bin.error);
|
|
34405
34766
|
void emitStep("restart");
|
|
34406
34767
|
try {
|
|
34407
34768
|
await deps.swapRuntime(agentId);
|
|
@@ -34727,6 +35088,13 @@ var AcpPublisher = class {
|
|
|
34727
35088
|
* `mode: 'replace'` so a re-send overrides any stale persisted
|
|
34728
35089
|
* version — important for the ACP path because each turn we ship
|
|
34729
35090
|
* the cumulative messages, not a delta.
|
|
35091
|
+
*
|
|
35092
|
+
* Each message may carry its OWN `agentId` — the agent that PRODUCED that
|
|
35093
|
+
* turn. The top-level `agentId` only keys the backend's per-agent
|
|
35094
|
+
* conversation bucket, so without the per-message field a multi-agent
|
|
35095
|
+
* session's reloaded history collapsed to whichever agent happened to be
|
|
35096
|
+
* active (the v1 limitation, codeagent-egai). Additive: older backends
|
|
35097
|
+
* ignore it, older CLIs simply omit it.
|
|
34730
35098
|
*/
|
|
34731
35099
|
async pushConversation(args2) {
|
|
34732
35100
|
const url2 = `${this.apiBase}/api/sessions/conversation`;
|
|
@@ -34873,17 +35241,106 @@ function commonPrefixLength(a, b) {
|
|
|
34873
35241
|
return i;
|
|
34874
35242
|
}
|
|
34875
35243
|
|
|
35244
|
+
// src/agents/acp/handoff-protocol.ts
|
|
35245
|
+
var REASON_MAX = 1e3;
|
|
35246
|
+
var PROMPT_MAX2 = 8e3;
|
|
35247
|
+
var FENCE_OPEN = "```" + HANDOFF_FENCE_TAG;
|
|
35248
|
+
var FENCE_RE = new RegExp("```" + HANDOFF_FENCE_TAG + "\\s*\\n([\\s\\S]*?)\\n?```", "g");
|
|
35249
|
+
var OUTER_FENCE_RE = /(`{4,})[\s\S]*?\1/g;
|
|
35250
|
+
var outerFencePlaceholder = (i) => `@@HANDOFF_MASK_${i}@@`;
|
|
35251
|
+
function maskOuterFences(text) {
|
|
35252
|
+
const spans = [];
|
|
35253
|
+
const masked = text.replace(OUTER_FENCE_RE, (m) => {
|
|
35254
|
+
const token = outerFencePlaceholder(spans.length);
|
|
35255
|
+
spans.push(m);
|
|
35256
|
+
return token;
|
|
35257
|
+
});
|
|
35258
|
+
const restore = (s) => spans.reduce((acc, span, i) => acc.split(outerFencePlaceholder(i)).join(span), s);
|
|
35259
|
+
return { masked, restore };
|
|
35260
|
+
}
|
|
35261
|
+
function parseProposal(raw, currentAgent, validTargets) {
|
|
35262
|
+
let parsed;
|
|
35263
|
+
try {
|
|
35264
|
+
parsed = JSON.parse(raw);
|
|
35265
|
+
} catch {
|
|
35266
|
+
log.debug("handoffProtocol", "dropped proposal: malformed JSON");
|
|
35267
|
+
return null;
|
|
35268
|
+
}
|
|
35269
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
35270
|
+
log.debug("handoffProtocol", "dropped proposal: not a JSON object");
|
|
35271
|
+
return null;
|
|
35272
|
+
}
|
|
35273
|
+
const { to, reason, prompt } = parsed;
|
|
35274
|
+
if (typeof to !== "string" || to.length === 0) {
|
|
35275
|
+
log.debug("handoffProtocol", 'dropped proposal: missing/invalid "to"');
|
|
35276
|
+
return null;
|
|
35277
|
+
}
|
|
35278
|
+
if (!validTargets.has(to)) {
|
|
35279
|
+
log.debug("handoffProtocol", `dropped proposal: unknown target "${to}"`);
|
|
35280
|
+
return null;
|
|
35281
|
+
}
|
|
35282
|
+
if (to === currentAgent) {
|
|
35283
|
+
log.debug("handoffProtocol", "dropped proposal: target is the current agent");
|
|
35284
|
+
return null;
|
|
35285
|
+
}
|
|
35286
|
+
if (typeof reason !== "string" || reason.length === 0 || reason.length > REASON_MAX) {
|
|
35287
|
+
log.debug("handoffProtocol", 'dropped proposal: invalid "reason"');
|
|
35288
|
+
return null;
|
|
35289
|
+
}
|
|
35290
|
+
if (typeof prompt !== "string" || prompt.length === 0 || prompt.length > PROMPT_MAX2) {
|
|
35291
|
+
log.debug("handoffProtocol", 'dropped proposal: invalid "prompt"');
|
|
35292
|
+
return null;
|
|
35293
|
+
}
|
|
35294
|
+
return { to, reason, prompt };
|
|
35295
|
+
}
|
|
35296
|
+
function stripFences(masked) {
|
|
35297
|
+
const stripped = masked.replace(FENCE_RE, "");
|
|
35298
|
+
return stripped.replace(/(?:\r?\n){3,}/g, "\n\n").trim();
|
|
35299
|
+
}
|
|
35300
|
+
function extractHandoffProposal(text, currentAgent, validTargets) {
|
|
35301
|
+
const { masked, restore } = maskOuterFences(text);
|
|
35302
|
+
const matches = [...masked.matchAll(FENCE_RE)];
|
|
35303
|
+
if (matches.length === 0) {
|
|
35304
|
+
return { cleanText: text, proposal: null };
|
|
35305
|
+
}
|
|
35306
|
+
const last = matches[matches.length - 1];
|
|
35307
|
+
const proposal = parseProposal(last[1].trim(), currentAgent, validTargets);
|
|
35308
|
+
const cleanText = restore(stripFences(masked));
|
|
35309
|
+
return { cleanText, proposal };
|
|
35310
|
+
}
|
|
35311
|
+
function stripHandoffFences(text) {
|
|
35312
|
+
const { masked, restore } = maskOuterFences(text);
|
|
35313
|
+
if (masked.search(FENCE_RE) === -1) {
|
|
35314
|
+
return text;
|
|
35315
|
+
}
|
|
35316
|
+
return restore(stripFences(masked));
|
|
35317
|
+
}
|
|
35318
|
+
function handoffFenceStartMasked(text) {
|
|
35319
|
+
const { masked } = maskOuterFences(text);
|
|
35320
|
+
if (masked.indexOf(FENCE_OPEN) === -1) return -1;
|
|
35321
|
+
const spans = [];
|
|
35322
|
+
for (const m of text.matchAll(OUTER_FENCE_RE)) {
|
|
35323
|
+
const start2 = m.index ?? 0;
|
|
35324
|
+
spans.push({ start: start2, end: start2 + m[0].length });
|
|
35325
|
+
}
|
|
35326
|
+
const insideSpan = (idx) => spans.some((s) => idx >= s.start && idx < s.end);
|
|
35327
|
+
for (let i = text.indexOf(FENCE_OPEN); i !== -1; i = text.indexOf(FENCE_OPEN, i + 1)) {
|
|
35328
|
+
if (!insideSpan(i)) return i;
|
|
35329
|
+
}
|
|
35330
|
+
return -1;
|
|
35331
|
+
}
|
|
35332
|
+
|
|
34876
35333
|
// src/agents/acp/onboarding.ts
|
|
34877
35334
|
var import_child_process28 = require("child_process");
|
|
34878
|
-
var
|
|
34879
|
-
var
|
|
34880
|
-
var
|
|
35335
|
+
var fs68 = __toESM(require("fs"));
|
|
35336
|
+
var os59 = __toESM(require("os"));
|
|
35337
|
+
var path75 = __toESM(require("path"));
|
|
34881
35338
|
var _onboardingSeam = {
|
|
34882
|
-
markerPath: (sessionId) =>
|
|
34883
|
-
exists: (p2) =>
|
|
35339
|
+
markerPath: (sessionId) => path75.join(os59.homedir(), ".codeam", "welcomed", `${sessionId}.done`),
|
|
35340
|
+
exists: (p2) => fs68.existsSync(p2),
|
|
34884
35341
|
write: (p2) => {
|
|
34885
|
-
|
|
34886
|
-
|
|
35342
|
+
fs68.mkdirSync(path75.dirname(p2), { recursive: true });
|
|
35343
|
+
fs68.writeFileSync(p2, "");
|
|
34887
35344
|
},
|
|
34888
35345
|
disabled: () => {
|
|
34889
35346
|
const v = process.env.CODEAM_ONBOARDING_DISABLED;
|
|
@@ -34920,7 +35377,7 @@ function resolveRepoName(cwd) {
|
|
|
34920
35377
|
if (name) return name;
|
|
34921
35378
|
}
|
|
34922
35379
|
}
|
|
34923
|
-
const base =
|
|
35380
|
+
const base = path75.basename(cwd || "");
|
|
34924
35381
|
if (base && !isUuid(base)) return base;
|
|
34925
35382
|
return "this project";
|
|
34926
35383
|
}
|
|
@@ -35178,13 +35635,13 @@ var import_crypto5 = require("crypto");
|
|
|
35178
35635
|
|
|
35179
35636
|
// src/services/turn-files/git-changeset.ts
|
|
35180
35637
|
var import_child_process29 = require("child_process");
|
|
35181
|
-
var
|
|
35182
|
-
var
|
|
35638
|
+
var fs70 = __toESM(require("fs/promises"));
|
|
35639
|
+
var path77 = __toESM(require("path"));
|
|
35183
35640
|
|
|
35184
35641
|
// src/services/turn-files/review-ignore.ts
|
|
35185
35642
|
var import_ignore2 = __toESM(require("ignore"));
|
|
35186
|
-
var
|
|
35187
|
-
var
|
|
35643
|
+
var fs69 = __toESM(require("fs"));
|
|
35644
|
+
var path76 = __toESM(require("path"));
|
|
35188
35645
|
var CURATED_REVIEW_IGNORE = [
|
|
35189
35646
|
// Google Cloud SDK (the incident) — installs a huge python tree.
|
|
35190
35647
|
"google-cloud-sdk/",
|
|
@@ -35223,7 +35680,7 @@ var CURATED_REVIEW_IGNORE = [
|
|
|
35223
35680
|
function makeReviewIgnore(repoRoot) {
|
|
35224
35681
|
const ig = (0, import_ignore2.default)().add(CURATED_REVIEW_IGNORE);
|
|
35225
35682
|
try {
|
|
35226
|
-
const custom =
|
|
35683
|
+
const custom = fs69.readFileSync(path76.join(repoRoot, ".codeam", "reviewignore"), "utf8");
|
|
35227
35684
|
ig.add(custom);
|
|
35228
35685
|
} catch {
|
|
35229
35686
|
}
|
|
@@ -35268,7 +35725,7 @@ async function collectRepoChangeset(opts) {
|
|
|
35268
35725
|
let stats;
|
|
35269
35726
|
if (!truncated && row.fileStatus === "added" && numstatEntry === void 0) {
|
|
35270
35727
|
const lineCount = await readUntrackedLineCount(
|
|
35271
|
-
|
|
35728
|
+
path77.join(opts.repoRoot, row.filePath)
|
|
35272
35729
|
);
|
|
35273
35730
|
stats = { added: lineCount, removed: 0 };
|
|
35274
35731
|
} else {
|
|
@@ -35299,7 +35756,7 @@ function readUntrackedLineCount(absPath) {
|
|
|
35299
35756
|
}
|
|
35300
35757
|
async function defaultReadUntrackedLineCount(absPath) {
|
|
35301
35758
|
try {
|
|
35302
|
-
const content = await
|
|
35759
|
+
const content = await fs70.readFile(absPath, "utf8");
|
|
35303
35760
|
let count = 0;
|
|
35304
35761
|
let pos = -1;
|
|
35305
35762
|
while ((pos = content.indexOf("\n", pos + 1)) !== -1) {
|
|
@@ -35391,7 +35848,7 @@ function defaultRunGit(cwd, args2) {
|
|
|
35391
35848
|
});
|
|
35392
35849
|
}
|
|
35393
35850
|
async function discoverRepos(workingDir, maxDepth = 4) {
|
|
35394
|
-
const
|
|
35851
|
+
const fs80 = await import("fs/promises");
|
|
35395
35852
|
const out2 = [];
|
|
35396
35853
|
await walk(workingDir, 0);
|
|
35397
35854
|
return out2;
|
|
@@ -35399,7 +35856,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
|
|
|
35399
35856
|
if (depth > maxDepth) return;
|
|
35400
35857
|
let entries = [];
|
|
35401
35858
|
try {
|
|
35402
|
-
const dirents = await
|
|
35859
|
+
const dirents = await fs80.readdir(dir, { withFileTypes: true });
|
|
35403
35860
|
entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
|
|
35404
35861
|
} catch {
|
|
35405
35862
|
return;
|
|
@@ -35410,8 +35867,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
|
|
|
35410
35867
|
if (hasGit) {
|
|
35411
35868
|
out2.push({
|
|
35412
35869
|
repoRoot: dir,
|
|
35413
|
-
repoPath:
|
|
35414
|
-
repoName:
|
|
35870
|
+
repoPath: path77.relative(workingDir, dir),
|
|
35871
|
+
repoName: path77.basename(dir)
|
|
35415
35872
|
});
|
|
35416
35873
|
return;
|
|
35417
35874
|
}
|
|
@@ -35419,14 +35876,14 @@ async function discoverRepos(workingDir, maxDepth = 4) {
|
|
|
35419
35876
|
if (!entry.isDirectory) continue;
|
|
35420
35877
|
if (entry.name === "node_modules") continue;
|
|
35421
35878
|
if (entry.name === "dist" || entry.name === "build") continue;
|
|
35422
|
-
await walk(
|
|
35879
|
+
await walk(path77.join(dir, entry.name), depth + 1);
|
|
35423
35880
|
}
|
|
35424
35881
|
}
|
|
35425
35882
|
}
|
|
35426
35883
|
|
|
35427
35884
|
// src/services/turn-files/files-outbox.ts
|
|
35428
|
-
var
|
|
35429
|
-
var
|
|
35885
|
+
var fs71 = __toESM(require("fs/promises"));
|
|
35886
|
+
var path78 = __toESM(require("path"));
|
|
35430
35887
|
var import_os12 = require("os");
|
|
35431
35888
|
var HOME_OUTBOX_DIR = ".codeam/outbox";
|
|
35432
35889
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -35459,16 +35916,16 @@ var FilesOutbox = class {
|
|
|
35459
35916
|
backoffIndex = 0;
|
|
35460
35917
|
stopped = false;
|
|
35461
35918
|
constructor(opts) {
|
|
35462
|
-
const base = opts.baseDir ??
|
|
35463
|
-
this.filePath =
|
|
35919
|
+
const base = opts.baseDir ?? path78.join(homeDir(), HOME_OUTBOX_DIR);
|
|
35920
|
+
this.filePath = path78.join(base, `${opts.sessionId}.jsonl`);
|
|
35464
35921
|
this.post = opts.post;
|
|
35465
35922
|
this.autoSchedule = opts.autoSchedule !== false;
|
|
35466
35923
|
}
|
|
35467
35924
|
/** Persist the entry to disk and trigger a flush. Returns once the
|
|
35468
35925
|
* line is durable on disk (not once the POST succeeds). */
|
|
35469
35926
|
async enqueue(entry) {
|
|
35470
|
-
await
|
|
35471
|
-
await
|
|
35927
|
+
await fs71.mkdir(path78.dirname(this.filePath), { recursive: true });
|
|
35928
|
+
await fs71.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
|
|
35472
35929
|
this.backoffIndex = 0;
|
|
35473
35930
|
if (this.autoSchedule) this.scheduleFlush(0);
|
|
35474
35931
|
}
|
|
@@ -35559,7 +36016,7 @@ var FilesOutbox = class {
|
|
|
35559
36016
|
async readAll() {
|
|
35560
36017
|
let raw = "";
|
|
35561
36018
|
try {
|
|
35562
|
-
raw = await
|
|
36019
|
+
raw = await fs71.readFile(this.filePath, "utf8");
|
|
35563
36020
|
} catch {
|
|
35564
36021
|
return [];
|
|
35565
36022
|
}
|
|
@@ -35583,12 +36040,12 @@ var FilesOutbox = class {
|
|
|
35583
36040
|
async rewrite(entries) {
|
|
35584
36041
|
const tmpPath = `${this.filePath}.${process.pid}.tmp`;
|
|
35585
36042
|
if (entries.length === 0) {
|
|
35586
|
-
await
|
|
36043
|
+
await fs71.unlink(this.filePath).catch(() => void 0);
|
|
35587
36044
|
return;
|
|
35588
36045
|
}
|
|
35589
36046
|
const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
35590
|
-
await
|
|
35591
|
-
await
|
|
36047
|
+
await fs71.writeFile(tmpPath, payload, "utf8");
|
|
36048
|
+
await fs71.rename(tmpPath, this.filePath);
|
|
35592
36049
|
}
|
|
35593
36050
|
};
|
|
35594
36051
|
function applyJitter(ms) {
|
|
@@ -35650,11 +36107,34 @@ var TurnFileAggregator = class {
|
|
|
35650
36107
|
*/
|
|
35651
36108
|
baselineByKey = /* @__PURE__ */ new Map();
|
|
35652
36109
|
baselineCaptured = false;
|
|
36110
|
+
/**
|
|
36111
|
+
* The file paths (relative to their repo root) that the MOST RECENT
|
|
36112
|
+
* `flushTurn()` call found novel — i.e. what it just enqueued (or would
|
|
36113
|
+
* have enqueued had the batch not exactly matched the baseline). Updated
|
|
36114
|
+
* unconditionally on every flush, including a no-op one (empties back
|
|
36115
|
+
* out), so `peekTurnPaths()` never returns paths from more than one
|
|
36116
|
+
* flush cycle ago. Not touched by the baseline-capture flush (it returns
|
|
36117
|
+
* before `novel` is computed), which is correct — pre-pair files aren't
|
|
36118
|
+
* "this session's" changes.
|
|
36119
|
+
*/
|
|
36120
|
+
lastTurnPaths = [];
|
|
35653
36121
|
/** Stop the outbox scheduler. Idempotent. */
|
|
35654
36122
|
stop() {
|
|
35655
36123
|
this.stopped = true;
|
|
35656
36124
|
this.outbox.stop();
|
|
35657
36125
|
}
|
|
36126
|
+
/**
|
|
36127
|
+
* Non-destructive read of the paths the MOST RECENT `flushTurn()` call
|
|
36128
|
+
* found novel — does NOT trigger a scan or clear anything itself. The
|
|
36129
|
+
* squad journal (`recordSquadTurn` in `command-handlers.ts`) `await`s
|
|
36130
|
+
* `flushTurn()` for the turn it's about to record BEFORE calling this, so
|
|
36131
|
+
* the read reflects THAT turn's paths, not a stale one left over from
|
|
36132
|
+
* whatever flushed previously. Empty on a session's very first turn
|
|
36133
|
+
* (still capturing the pre-pair baseline) or a chat-only turn.
|
|
36134
|
+
*/
|
|
36135
|
+
peekTurnPaths() {
|
|
36136
|
+
return [...this.lastTurnPaths];
|
|
36137
|
+
}
|
|
35658
36138
|
/**
|
|
35659
36139
|
* Run the discovery + git collection + POST pipeline for one
|
|
35660
36140
|
* turn. Errors are swallowed (logged) so an agent never blocks on a
|
|
@@ -35695,6 +36175,7 @@ var TurnFileAggregator = class {
|
|
|
35695
36175
|
if (!base) return true;
|
|
35696
36176
|
return base.linesAdded !== f.linesAdded || base.linesRemoved !== f.linesRemoved || base.fileStatus !== f.fileStatus;
|
|
35697
36177
|
});
|
|
36178
|
+
this.lastTurnPaths = novel.map((f) => f.filePath);
|
|
35698
36179
|
if (novel.length === 0) {
|
|
35699
36180
|
log.trace(
|
|
35700
36181
|
"turnFiles",
|
|
@@ -35714,10 +36195,7 @@ var TurnFileAggregator = class {
|
|
|
35714
36195
|
await this.outbox.enqueue(entry);
|
|
35715
36196
|
}
|
|
35716
36197
|
} catch (err) {
|
|
35717
|
-
log.warn(
|
|
35718
|
-
"turnFiles",
|
|
35719
|
-
`flushTurn failed: ${err.message ?? String(err)}`
|
|
35720
|
-
);
|
|
36198
|
+
log.warn("turnFiles", `flushTurn failed: ${err.message ?? String(err)}`);
|
|
35721
36199
|
}
|
|
35722
36200
|
}
|
|
35723
36201
|
/**
|
|
@@ -36044,8 +36522,8 @@ async function postBudgetReached(opts, fetchImpl = fetch) {
|
|
|
36044
36522
|
|
|
36045
36523
|
// src/packs/gates.ts
|
|
36046
36524
|
var import_node_child_process30 = require("child_process");
|
|
36047
|
-
var
|
|
36048
|
-
var
|
|
36525
|
+
var fs72 = __toESM(require("fs"));
|
|
36526
|
+
var path79 = __toESM(require("path"));
|
|
36049
36527
|
var defaultCommandRunner = (file, args2, cwd, timeoutMs) => new Promise((resolve9) => {
|
|
36050
36528
|
(0, import_node_child_process30.execFile)(
|
|
36051
36529
|
file,
|
|
@@ -36082,14 +36560,14 @@ var NO_TEST_PLACEHOLDER = 'echo "Error: no test specified"';
|
|
|
36082
36560
|
var CHECKS_TIMEOUT_MS = 5 * 6e4;
|
|
36083
36561
|
function detectChecksCommand(cwd) {
|
|
36084
36562
|
try {
|
|
36085
|
-
const cfg = JSON.parse(
|
|
36563
|
+
const cfg = JSON.parse(fs72.readFileSync(path79.join(cwd, ".codeam", "pack.json"), "utf8"));
|
|
36086
36564
|
if (typeof cfg.checksCommand === "string" && cfg.checksCommand.trim().length > 0) {
|
|
36087
36565
|
return cfg.checksCommand.trim();
|
|
36088
36566
|
}
|
|
36089
36567
|
} catch {
|
|
36090
36568
|
}
|
|
36091
36569
|
try {
|
|
36092
|
-
const pkg = JSON.parse(
|
|
36570
|
+
const pkg = JSON.parse(fs72.readFileSync(path79.join(cwd, "package.json"), "utf8"));
|
|
36093
36571
|
const test = pkg.scripts?.test;
|
|
36094
36572
|
if (typeof test === "string" && test.trim().length > 0 && !test.includes(NO_TEST_PLACEHOLDER)) {
|
|
36095
36573
|
return "npm test";
|
|
@@ -36107,39 +36585,39 @@ ${res.stderr}`.trim();
|
|
|
36107
36585
|
}
|
|
36108
36586
|
|
|
36109
36587
|
// src/packs/run-store.ts
|
|
36110
|
-
var
|
|
36111
|
-
var
|
|
36588
|
+
var fs73 = __toESM(require("fs"));
|
|
36589
|
+
var path80 = __toESM(require("path"));
|
|
36112
36590
|
var crypto5 = __toESM(require("crypto"));
|
|
36113
36591
|
function packsDir(cwd) {
|
|
36114
|
-
return
|
|
36592
|
+
return path80.join(cwd, ".codeam", "packs");
|
|
36115
36593
|
}
|
|
36116
36594
|
function runDir(cwd, runId) {
|
|
36117
|
-
return
|
|
36595
|
+
return path80.join(packsDir(cwd), runId);
|
|
36118
36596
|
}
|
|
36119
36597
|
function newRunId() {
|
|
36120
36598
|
return `pk_${Date.now().toString(36)}_${crypto5.randomBytes(4).toString("hex")}`;
|
|
36121
36599
|
}
|
|
36122
36600
|
function writeJsonAtomic(file, value) {
|
|
36123
|
-
|
|
36601
|
+
fs73.mkdirSync(path80.dirname(file), { recursive: true });
|
|
36124
36602
|
const tmp = `${file}.tmp`;
|
|
36125
|
-
|
|
36126
|
-
|
|
36603
|
+
fs73.writeFileSync(tmp, JSON.stringify(value, null, 2));
|
|
36604
|
+
fs73.renameSync(tmp, file);
|
|
36127
36605
|
}
|
|
36128
36606
|
function saveRun(cwd, state) {
|
|
36129
|
-
writeJsonAtomic(
|
|
36607
|
+
writeJsonAtomic(path80.join(runDir(cwd, state.runId), "run.json"), state);
|
|
36130
36608
|
}
|
|
36131
36609
|
function saveStageHandoff(cwd, runId, stageIndex, role, handoff) {
|
|
36132
36610
|
const name = `${String(stageIndex + 1).padStart(2, "0")}-${role}.json`;
|
|
36133
|
-
writeJsonAtomic(
|
|
36611
|
+
writeJsonAtomic(path80.join(runDir(cwd, runId), name), handoff);
|
|
36134
36612
|
}
|
|
36135
36613
|
function loadLatestRun(cwd) {
|
|
36136
36614
|
try {
|
|
36137
36615
|
const dir = packsDir(cwd);
|
|
36138
|
-
const entries =
|
|
36616
|
+
const entries = fs73.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
36139
36617
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
36140
|
-
const file =
|
|
36618
|
+
const file = path80.join(dir, entries[i], "run.json");
|
|
36141
36619
|
try {
|
|
36142
|
-
const parsed = JSON.parse(
|
|
36620
|
+
const parsed = JSON.parse(fs73.readFileSync(file, "utf8"));
|
|
36143
36621
|
if (parsed && typeof parsed.runId === "string") return parsed;
|
|
36144
36622
|
} catch {
|
|
36145
36623
|
}
|
|
@@ -36150,17 +36628,17 @@ function loadLatestRun(cwd) {
|
|
|
36150
36628
|
}
|
|
36151
36629
|
function ensureLedgerIgnored(cwd) {
|
|
36152
36630
|
try {
|
|
36153
|
-
const gitDir =
|
|
36154
|
-
if (!
|
|
36155
|
-
const exclude =
|
|
36631
|
+
const gitDir = path80.join(cwd, ".git");
|
|
36632
|
+
if (!fs73.existsSync(gitDir)) return;
|
|
36633
|
+
const exclude = path80.join(gitDir, "info", "exclude");
|
|
36156
36634
|
let existing = "";
|
|
36157
36635
|
try {
|
|
36158
|
-
existing =
|
|
36636
|
+
existing = fs73.readFileSync(exclude, "utf8");
|
|
36159
36637
|
} catch {
|
|
36160
36638
|
}
|
|
36161
36639
|
if (existing.includes(".codeam/packs/")) return;
|
|
36162
|
-
|
|
36163
|
-
|
|
36640
|
+
fs73.mkdirSync(path80.dirname(exclude), { recursive: true });
|
|
36641
|
+
fs73.writeFileSync(exclude, `${existing.trimEnd()}
|
|
36164
36642
|
.codeam/packs/
|
|
36165
36643
|
`.trimStart());
|
|
36166
36644
|
} catch {
|
|
@@ -36713,6 +37191,56 @@ async function detectRepoStack(cwd, runtime) {
|
|
|
36713
37191
|
}
|
|
36714
37192
|
}
|
|
36715
37193
|
|
|
37194
|
+
// src/agents/acp/coderabbit-mention.ts
|
|
37195
|
+
var CODERABBIT_AGENT_ID = "coderabbit";
|
|
37196
|
+
var CODERABBIT_NOT_LINKED_MESSAGE = "Link CodeRabbit in Profile \u203A Your Squad first.";
|
|
37197
|
+
var CODERABBIT_NO_CUSTOM_INSTRUCTIONS_NOTICE = "CodeRabbit reviews your current changes \u2014 custom instructions aren't supported yet.";
|
|
37198
|
+
function hasCustomInstructions(prompt) {
|
|
37199
|
+
return prompt.replace(/@coderabbit\b/gi, "").trim().length > 0;
|
|
37200
|
+
}
|
|
37201
|
+
function composeReviewOutput(markdown, custom) {
|
|
37202
|
+
const body = markdown.trim().length > 0 ? markdown.trim() : "CodeRabbit found no issues to report.";
|
|
37203
|
+
return custom ? `${CODERABBIT_NO_CUSTOM_INSTRUCTIONS_NOTICE}
|
|
37204
|
+
|
|
37205
|
+
${body}` : body;
|
|
37206
|
+
}
|
|
37207
|
+
function defaultRunReview(input) {
|
|
37208
|
+
return new CoderabbitRuntimeStrategy(createOsStrategy()).runOneShot(input);
|
|
37209
|
+
}
|
|
37210
|
+
async function runCoderabbitMentionReview(deps = {}) {
|
|
37211
|
+
const configure = deps.configure ?? configureCoderabbit;
|
|
37212
|
+
const runReview = deps.runReview ?? defaultRunReview;
|
|
37213
|
+
try {
|
|
37214
|
+
const status2 = await configure({ action: "status" });
|
|
37215
|
+
if (!status2.loggedIn) {
|
|
37216
|
+
const cred = deps.fetchCredential ? await deps.fetchCredential() : null;
|
|
37217
|
+
if (!cred) return { ok: false, error: CODERABBIT_NOT_LINKED_MESSAGE };
|
|
37218
|
+
const provisioned = await configure({
|
|
37219
|
+
action: "provision",
|
|
37220
|
+
provisionCredential: cred
|
|
37221
|
+
});
|
|
37222
|
+
if (!provisioned.loggedIn) {
|
|
37223
|
+
return { ok: false, error: provisioned.error ?? CODERABBIT_NOT_LINKED_MESSAGE };
|
|
37224
|
+
}
|
|
37225
|
+
}
|
|
37226
|
+
const result = await configure({ action: "review" }, { runReview });
|
|
37227
|
+
if (result.error) return { ok: false, error: result.error };
|
|
37228
|
+
return { ok: true, markdown: result.review?.markdown ?? "" };
|
|
37229
|
+
} catch (err) {
|
|
37230
|
+
return {
|
|
37231
|
+
ok: false,
|
|
37232
|
+
error: err instanceof Error ? err.message : "CodeRabbit review failed"
|
|
37233
|
+
};
|
|
37234
|
+
}
|
|
37235
|
+
}
|
|
37236
|
+
function logMentionOutcome(result) {
|
|
37237
|
+
if (result.ok) {
|
|
37238
|
+
log.info("acpRunner", `squad: coderabbit review completed (${result.markdown.length} chars)`);
|
|
37239
|
+
} else {
|
|
37240
|
+
log.warn("acpRunner", `squad: coderabbit review failed: ${result.error}`);
|
|
37241
|
+
}
|
|
37242
|
+
}
|
|
37243
|
+
|
|
36716
37244
|
// src/agents/acp/command-handlers.ts
|
|
36717
37245
|
var import_node_child_process31 = require("child_process");
|
|
36718
37246
|
|
|
@@ -36751,35 +37279,35 @@ function buildAcpPromptBlocks(payload) {
|
|
|
36751
37279
|
}
|
|
36752
37280
|
|
|
36753
37281
|
// src/agents/agent-standard.ts
|
|
36754
|
-
var
|
|
36755
|
-
var
|
|
36756
|
-
var
|
|
36757
|
-
function ensureAgentStandard(homeDir2 =
|
|
37282
|
+
var fs75 = __toESM(require("fs"));
|
|
37283
|
+
var path82 = __toESM(require("path"));
|
|
37284
|
+
var os60 = __toESM(require("os"));
|
|
37285
|
+
function ensureAgentStandard(homeDir2 = os60.homedir()) {
|
|
36758
37286
|
try {
|
|
36759
|
-
const file =
|
|
37287
|
+
const file = path82.join(homeDir2, ".claude", "CLAUDE.md");
|
|
36760
37288
|
let existing = "";
|
|
36761
37289
|
try {
|
|
36762
|
-
existing =
|
|
37290
|
+
existing = fs75.readFileSync(file, "utf8");
|
|
36763
37291
|
} catch {
|
|
36764
37292
|
}
|
|
36765
37293
|
if (existing.includes(AGENT_STANDARD_MARKER)) return;
|
|
36766
|
-
|
|
37294
|
+
fs75.mkdirSync(path82.dirname(file), { recursive: true });
|
|
36767
37295
|
const next = existing.trim() ? `${existing.trimEnd()}
|
|
36768
37296
|
|
|
36769
37297
|
${AGENT_STANDARD_BLOCK}
|
|
36770
37298
|
` : `${AGENT_STANDARD_BLOCK}
|
|
36771
37299
|
`;
|
|
36772
|
-
|
|
37300
|
+
fs75.writeFileSync(file, next);
|
|
36773
37301
|
} catch {
|
|
36774
37302
|
}
|
|
36775
37303
|
}
|
|
36776
37304
|
var _agentStandardSeam = {
|
|
36777
37305
|
isLocalSession: () => isLocalSession(),
|
|
36778
|
-
markerPath: (sessionId) =>
|
|
36779
|
-
exists: (p2) =>
|
|
37306
|
+
markerPath: (sessionId) => path82.join(os60.homedir(), ".codeam", "agent-standard", `${sessionId}.done`),
|
|
37307
|
+
exists: (p2) => fs75.existsSync(p2),
|
|
36780
37308
|
write: (p2) => {
|
|
36781
|
-
|
|
36782
|
-
|
|
37309
|
+
fs75.mkdirSync(path82.dirname(p2), { recursive: true });
|
|
37310
|
+
fs75.writeFileSync(p2, "");
|
|
36783
37311
|
}
|
|
36784
37312
|
};
|
|
36785
37313
|
function isClaude(agent) {
|
|
@@ -36883,27 +37411,228 @@ async function beadsActionH(ctx) {
|
|
|
36883
37411
|
}
|
|
36884
37412
|
return;
|
|
36885
37413
|
}
|
|
37414
|
+
function rebindSessionHandles(ctx, outcome) {
|
|
37415
|
+
Object.assign(ctx, outcome.handles);
|
|
37416
|
+
return outcome;
|
|
37417
|
+
}
|
|
37418
|
+
async function routeSquadTask(ctx, target) {
|
|
37419
|
+
const { squad, routeToAgent } = ctx;
|
|
37420
|
+
if (!routeToAgent) {
|
|
37421
|
+
return { ok: false, error: "Routing to another agent is not supported on this session." };
|
|
37422
|
+
}
|
|
37423
|
+
const roster = squad?.roster;
|
|
37424
|
+
if (roster && !roster.agents.some((a) => a.agentId === target)) {
|
|
37425
|
+
return { ok: false, error: `Unknown agent '${target}' \u2014 not in your squad.` };
|
|
37426
|
+
}
|
|
37427
|
+
const member = squad?.member(target);
|
|
37428
|
+
const fastPathArmed = Boolean(member && (member.provisioned || member.binaryVerified));
|
|
37429
|
+
let { result } = rebindSessionHandles(ctx, await routeToAgent(target));
|
|
37430
|
+
if (!result.ok && fastPathArmed) {
|
|
37431
|
+
log.warn(
|
|
37432
|
+
"acpRunner",
|
|
37433
|
+
`squad: fast-path route to ${target} failed (${result.error}) \u2014 retrying full path`
|
|
37434
|
+
);
|
|
37435
|
+
({ result } = rebindSessionHandles(ctx, await routeToAgent(target, { skipFastPath: true })));
|
|
37436
|
+
}
|
|
37437
|
+
if (!result.ok) {
|
|
37438
|
+
return { ok: false, error: result.error ?? `Couldn't switch to ${target}.` };
|
|
37439
|
+
}
|
|
37440
|
+
return { ok: true };
|
|
37441
|
+
}
|
|
37442
|
+
function collectSquadContext(ctx) {
|
|
37443
|
+
const pieces = [];
|
|
37444
|
+
const { squad, opts } = ctx;
|
|
37445
|
+
if (squad) {
|
|
37446
|
+
const member = squad.member(opts.agent);
|
|
37447
|
+
const roster = squad.roster;
|
|
37448
|
+
if (roster && member.lastTurnIndex === 0) {
|
|
37449
|
+
const preamble = buildTeamPreamble(roster, opts.agent, {
|
|
37450
|
+
handoffInstructions: roster.handoffsEnabled === true
|
|
37451
|
+
});
|
|
37452
|
+
if (preamble) pieces.push(preamble);
|
|
37453
|
+
}
|
|
37454
|
+
if (squad.turnCount() > member.lastTurnIndex) {
|
|
37455
|
+
const otherEntries = squad.entriesSince(member.lastTurnIndex).filter((e) => e.agentId !== opts.agent);
|
|
37456
|
+
const briefing = buildDeltaBriefing(otherEntries);
|
|
37457
|
+
if (briefing) pieces.push(briefing);
|
|
37458
|
+
}
|
|
37459
|
+
}
|
|
37460
|
+
if (ctx.pendingHandoff?.current) {
|
|
37461
|
+
pieces.push(ctx.pendingHandoff.current);
|
|
37462
|
+
ctx.pendingHandoff.current = null;
|
|
37463
|
+
}
|
|
37464
|
+
return pieces;
|
|
37465
|
+
}
|
|
37466
|
+
function squadContextMode(ctx) {
|
|
37467
|
+
return ctx.squad?.member(ctx.opts.agent).contextTextFallback === true ? "text" : "resource";
|
|
37468
|
+
}
|
|
37469
|
+
function applySquadContext(blocks, pieces, mode) {
|
|
37470
|
+
if (pieces.length === 0) return;
|
|
37471
|
+
if (mode === "resource") {
|
|
37472
|
+
blocks.unshift(buildSquadContextBlock(pieces.join("\n\n")));
|
|
37473
|
+
return;
|
|
37474
|
+
}
|
|
37475
|
+
for (let i = pieces.length - 1; i >= 0; i--) blocks.unshift({ type: "text", text: pieces[i] });
|
|
37476
|
+
}
|
|
37477
|
+
async function promptWithContextFallback(ctx, client3, blocks, pieces) {
|
|
37478
|
+
try {
|
|
37479
|
+
return await client3.prompt(blocks);
|
|
37480
|
+
} catch (err) {
|
|
37481
|
+
if (pieces.length === 0 || blocks.length === 0 || !isSquadContextBlock(blocks[0]) || !looksLikeUnsupportedPromptShape(err)) {
|
|
37482
|
+
throw err;
|
|
37483
|
+
}
|
|
37484
|
+
log.warn(
|
|
37485
|
+
"acpRunner",
|
|
37486
|
+
`squad: ${ctx.opts.agent} rejected the native squad-context resource block (${describeError(err)}) \u2014 retrying once with legacy text blocks`
|
|
37487
|
+
);
|
|
37488
|
+
if (ctx.squad) ctx.squad.member(ctx.opts.agent).contextTextFallback = true;
|
|
37489
|
+
blocks.shift();
|
|
37490
|
+
applySquadContext(blocks, pieces, "text");
|
|
37491
|
+
return await client3.prompt(blocks);
|
|
37492
|
+
}
|
|
37493
|
+
}
|
|
37494
|
+
function recordSquadTurn(ctx, prompt, replySummary) {
|
|
37495
|
+
const { squad, opts, turnFiles } = ctx;
|
|
37496
|
+
if (!squad) return;
|
|
37497
|
+
squad.recordTurn({
|
|
37498
|
+
agentId: opts.agent,
|
|
37499
|
+
prompt,
|
|
37500
|
+
replySummary,
|
|
37501
|
+
// TurnFileAggregator owns per-turn file changesets end-to-end (git diff →
|
|
37502
|
+
// outbox POST). The caller (`startTaskH`) AWAITS `turnFiles.flushTurn()`
|
|
37503
|
+
// for THIS turn before calling recordSquadTurn precisely so
|
|
37504
|
+
// `peekTurnPaths()` reflects THIS turn's novel files, not a stale read
|
|
37505
|
+
// of whatever the aggregator's PREVIOUS flush happened to find. Capped
|
|
37506
|
+
// so a pathological turn (mass refactor) doesn't bloat the journal.
|
|
37507
|
+
filesTouched: turnFiles.peekTurnPaths().slice(0, 20)
|
|
37508
|
+
});
|
|
37509
|
+
squad.member(opts.agent).lastTurnIndex = squad.turnCount();
|
|
37510
|
+
}
|
|
37511
|
+
function handoffTargets(ctx) {
|
|
37512
|
+
const roster = ctx.squad?.roster;
|
|
37513
|
+
if (!roster) return /* @__PURE__ */ new Set();
|
|
37514
|
+
return new Set(roster.agents.map((a) => a.agentId).filter((id) => id !== ctx.opts.agent));
|
|
37515
|
+
}
|
|
37516
|
+
function resolvePendingProposal(ctx, requestedAgentId) {
|
|
37517
|
+
const slot = ctx.pendingProposal;
|
|
37518
|
+
const pending = slot?.current;
|
|
37519
|
+
if (!slot || !pending) return;
|
|
37520
|
+
slot.current = null;
|
|
37521
|
+
const accepted = requestedAgentId === pending.toAgentId;
|
|
37522
|
+
if (accepted) ctx.squad?.countAccepted();
|
|
37523
|
+
log.info(
|
|
37524
|
+
"acpRunner",
|
|
37525
|
+
`squad: handoff ${pending.proposalId} ${accepted ? "accepted" : "declined"}`
|
|
37526
|
+
);
|
|
37527
|
+
const resolution = { proposalId: pending.proposalId, accepted };
|
|
37528
|
+
void ctx.postSquadEvent?.("handoff_resolved", { ...resolution });
|
|
37529
|
+
}
|
|
37530
|
+
function proposalIdFor(commandId, hop) {
|
|
37531
|
+
const id = hop <= 1 ? `hp-${commandId}` : `hp-${commandId}-h${hop}`;
|
|
37532
|
+
return id.length > 128 ? id.slice(0, 128) : id;
|
|
37533
|
+
}
|
|
37534
|
+
function emitHandoffProposal(ctx, proposal, hop) {
|
|
37535
|
+
const { squad, pendingProposal, postSquadEvent, opts, cmd } = ctx;
|
|
37536
|
+
if (!proposal || !pendingProposal || !postSquadEvent) return null;
|
|
37537
|
+
if (squad?.roster?.handoffsEnabled !== true) return null;
|
|
37538
|
+
if (pendingProposal.current) {
|
|
37539
|
+
log.info("acpRunner", "squad: dropping handoff proposal \u2014 one is already pending");
|
|
37540
|
+
return null;
|
|
37541
|
+
}
|
|
37542
|
+
const auto = squad.auto.enabled && squad.hopsRemaining() > 0;
|
|
37543
|
+
if (auto) squad.consumeHop();
|
|
37544
|
+
const record2 = {
|
|
37545
|
+
proposalId: proposalIdFor(cmd.id, hop),
|
|
37546
|
+
fromAgentId: opts.agent,
|
|
37547
|
+
toAgentId: proposal.to,
|
|
37548
|
+
reason: proposal.reason,
|
|
37549
|
+
prompt: proposal.prompt,
|
|
37550
|
+
...auto ? { auto: true, hopsRemaining: squad.hopsRemaining() } : {}
|
|
37551
|
+
};
|
|
37552
|
+
squad.countProposal({ auto });
|
|
37553
|
+
log.info(
|
|
37554
|
+
"acpRunner",
|
|
37555
|
+
`squad: handoff proposed ${opts.agent} \u2192 ${record2.toAgentId}${auto ? " (auto)" : ""}`
|
|
37556
|
+
);
|
|
37557
|
+
void postSquadEvent("handoff_proposed", { ...record2 });
|
|
37558
|
+
if (!auto) {
|
|
37559
|
+
pendingProposal.current = record2;
|
|
37560
|
+
return null;
|
|
37561
|
+
}
|
|
37562
|
+
return record2;
|
|
37563
|
+
}
|
|
37564
|
+
function resolveAutoHandoff(ctx, record2) {
|
|
37565
|
+
ctx.squad?.countAccepted();
|
|
37566
|
+
const resolution = {
|
|
37567
|
+
proposalId: record2.proposalId,
|
|
37568
|
+
accepted: true,
|
|
37569
|
+
auto: true
|
|
37570
|
+
};
|
|
37571
|
+
void ctx.postSquadEvent?.("handoff_resolved", { ...resolution });
|
|
37572
|
+
}
|
|
37573
|
+
async function runCoderabbitMention(ctx, promptText) {
|
|
37574
|
+
const { cmd, relay, streaming, opts, turnFiles } = ctx;
|
|
37575
|
+
const userText = promptText.length > 0 ? promptText : `@${CODERABBIT_AGENT_ID}`;
|
|
37576
|
+
await streaming.beginTurn();
|
|
37577
|
+
ctx.history.appendUserPrompt(userText);
|
|
37578
|
+
log.info("acpRunner", `squad: coderabbit one-shot review id=${cmd.id.slice(0, 8)}`);
|
|
37579
|
+
const review = await runCoderabbitMentionReview({
|
|
37580
|
+
fetchCredential: () => fetchProvisionCredential({
|
|
37581
|
+
agentId: CODERABBIT_AGENT_ID,
|
|
37582
|
+
sessionId: opts.sessionId,
|
|
37583
|
+
pluginId: opts.pluginId,
|
|
37584
|
+
pluginAuthToken: opts.pluginAuthToken
|
|
37585
|
+
})
|
|
37586
|
+
});
|
|
37587
|
+
logMentionOutcome(review);
|
|
37588
|
+
if (!review.ok) {
|
|
37589
|
+
await streaming.closeWithBubble(review.error);
|
|
37590
|
+
ctx.history.appendAgentReply(review.error, CODERABBIT_AGENT_ID);
|
|
37591
|
+
void ctx.history.flush();
|
|
37592
|
+
await relay.sendResult(cmd.id, "failed", { error: review.error });
|
|
37593
|
+
return;
|
|
37594
|
+
}
|
|
37595
|
+
const output = composeReviewOutput(review.markdown, hasCustomInstructions(promptText));
|
|
37596
|
+
await streaming.closeWithBubble(output);
|
|
37597
|
+
ctx.history.appendAgentReply(output, CODERABBIT_AGENT_ID);
|
|
37598
|
+
void ctx.history.flush();
|
|
37599
|
+
await turnFiles.flushTurn().catch((err) => {
|
|
37600
|
+
log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
|
|
37601
|
+
});
|
|
37602
|
+
recordCoderabbitTurn(ctx, userText, output);
|
|
37603
|
+
await relay.sendResult(cmd.id, "completed", { agentId: CODERABBIT_AGENT_ID });
|
|
37604
|
+
}
|
|
37605
|
+
function recordCoderabbitTurn(ctx, prompt, replySummary) {
|
|
37606
|
+
ctx.squad?.recordTurn({
|
|
37607
|
+
agentId: CODERABBIT_AGENT_ID,
|
|
37608
|
+
prompt,
|
|
37609
|
+
replySummary,
|
|
37610
|
+
filesTouched: []
|
|
37611
|
+
});
|
|
37612
|
+
}
|
|
36886
37613
|
async function startTaskH(ctx) {
|
|
36887
|
-
const {
|
|
36888
|
-
cmd,
|
|
36889
|
-
client: client3,
|
|
36890
|
-
relay,
|
|
36891
|
-
streaming,
|
|
36892
|
-
opts,
|
|
36893
|
-
history,
|
|
36894
|
-
turnFiles,
|
|
36895
|
-
publisher,
|
|
36896
|
-
recentStderr,
|
|
36897
|
-
budgetRecovery,
|
|
36898
|
-
budgetReachedFlag
|
|
36899
|
-
} = ctx;
|
|
37614
|
+
const { cmd, relay, streaming, opts, turnFiles, publisher, recentStderr, budgetReachedFlag } = ctx;
|
|
36900
37615
|
const payload = cmd.payload;
|
|
37616
|
+
const requestedAgentId = typeof payload?.agentId === "string" ? payload.agentId.trim() : "";
|
|
37617
|
+
resolvePendingProposal(ctx, requestedAgentId);
|
|
37618
|
+
if (requestedAgentId === CODERABBIT_AGENT_ID) {
|
|
37619
|
+
await runCoderabbitMention(ctx, (payload?.prompt ?? "").trim());
|
|
37620
|
+
return;
|
|
37621
|
+
}
|
|
36901
37622
|
const blocks = buildAcpPromptBlocks(payload ?? {});
|
|
36902
37623
|
if (blocks.length === 0) {
|
|
36903
37624
|
log.warn("acpRunner", "start_task with empty prompt + no attachments; ignoring");
|
|
36904
37625
|
await relay.sendResult(cmd.id, "failed", { error: "empty prompt" });
|
|
36905
37626
|
return;
|
|
36906
37627
|
}
|
|
37628
|
+
if (requestedAgentId.length > 0 && requestedAgentId !== opts.agent) {
|
|
37629
|
+
const routed = await routeSquadTask(ctx, requestedAgentId);
|
|
37630
|
+
if (!routed.ok) {
|
|
37631
|
+
log.warn("acpRunner", `start_task routing to ${requestedAgentId} failed: ${routed.error}`);
|
|
37632
|
+
await relay.sendResult(cmd.id, "failed", { error: routed.error });
|
|
37633
|
+
return;
|
|
37634
|
+
}
|
|
37635
|
+
}
|
|
36907
37636
|
const promptText = blocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
36908
37637
|
const imageCount = blocks.filter((b) => b.type === "image").length;
|
|
36909
37638
|
log.info(
|
|
@@ -36915,129 +37644,214 @@ async function startTaskH(ctx) {
|
|
|
36915
37644
|
showInfo(echoLine);
|
|
36916
37645
|
}
|
|
36917
37646
|
await streaming.beginTurn();
|
|
36918
|
-
history.appendUserPrompt(promptText);
|
|
37647
|
+
ctx.history.appendUserPrompt(promptText);
|
|
36919
37648
|
maybePrefaceAgentStandard(blocks, opts.agent, opts.sessionId);
|
|
36920
|
-
|
|
36921
|
-
|
|
36922
|
-
|
|
36923
|
-
|
|
36924
|
-
let
|
|
36925
|
-
|
|
36926
|
-
|
|
36927
|
-
|
|
36928
|
-
|
|
36929
|
-
|
|
36930
|
-
|
|
36931
|
-
|
|
36932
|
-
|
|
36933
|
-
|
|
36934
|
-
|
|
36935
|
-
|
|
36936
|
-
|
|
36937
|
-
|
|
36938
|
-
|
|
36939
|
-
|
|
36940
|
-
|
|
36941
|
-
|
|
36942
|
-
|
|
36943
|
-
|
|
36944
|
-
|
|
36945
|
-
|
|
36946
|
-
|
|
36947
|
-
|
|
36948
|
-
|
|
36949
|
-
|
|
36950
|
-
|
|
36951
|
-
|
|
36952
|
-
|
|
36953
|
-
|
|
36954
|
-
|
|
36955
|
-
})
|
|
36956
|
-
|
|
36957
|
-
|
|
36958
|
-
|
|
36959
|
-
|
|
36960
|
-
|
|
36961
|
-
|
|
36962
|
-
|
|
36963
|
-
|
|
36964
|
-
|
|
36965
|
-
|
|
36966
|
-
|
|
36967
|
-
|
|
36968
|
-
|
|
36969
|
-
|
|
36970
|
-
|
|
36971
|
-
|
|
36972
|
-
|
|
36973
|
-
|
|
36974
|
-
|
|
36975
|
-
|
|
36976
|
-
|
|
36977
|
-
|
|
36978
|
-
|
|
36979
|
-
|
|
36980
|
-
|
|
36981
|
-
|
|
36982
|
-
|
|
36983
|
-
|
|
36984
|
-
|
|
36985
|
-
|
|
36986
|
-
|
|
36987
|
-
|
|
36988
|
-
|
|
36989
|
-
|
|
36990
|
-
|
|
36991
|
-
|
|
36992
|
-
|
|
36993
|
-
|
|
36994
|
-
|
|
36995
|
-
|
|
36996
|
-
|
|
36997
|
-
);
|
|
36998
|
-
return;
|
|
36999
|
-
}
|
|
37000
|
-
const hadText = streaming.hasVisibleProgress();
|
|
37001
|
-
const detail = describeError(err);
|
|
37002
|
-
log.warn("acpRunner", `prompt failed: ${detail}`);
|
|
37003
|
-
await cancelStuckTurn(client3);
|
|
37004
|
-
if (looksLikeBudgetExceeded(`${detail}
|
|
37005
|
-
${recentStderr.join("\n")}`)) {
|
|
37006
|
-
await streaming.closeAll();
|
|
37007
|
-
if (!budgetReachedFlag.get()) {
|
|
37008
|
-
budgetReachedFlag.set(true);
|
|
37009
|
-
void postBudgetReached({
|
|
37010
|
-
sessionId: opts.sessionId,
|
|
37011
|
-
pluginId: opts.pluginId,
|
|
37012
|
-
pluginAuthToken: opts.pluginAuthToken,
|
|
37013
|
-
agent: opts.agent,
|
|
37014
|
-
period: extractBudgetPeriod(`${detail}
|
|
37015
|
-
${recentStderr.join("\n")}`)
|
|
37649
|
+
const squadContext = collectSquadContext(ctx);
|
|
37650
|
+
applySquadContext(blocks, squadContext, squadContextMode(ctx));
|
|
37651
|
+
ctx.squad?.resetHops();
|
|
37652
|
+
let turnBlocks = blocks;
|
|
37653
|
+
let turnPieces = squadContext;
|
|
37654
|
+
let turnPrompt = promptText;
|
|
37655
|
+
let hop = 1;
|
|
37656
|
+
for (; ; ) {
|
|
37657
|
+
const { client: client3, history, budgetRecovery } = ctx;
|
|
37658
|
+
let turnClosed = false;
|
|
37659
|
+
try {
|
|
37660
|
+
const reply = await promptWithContextFallback(ctx, client3, turnBlocks, turnPieces);
|
|
37661
|
+
const finalText = streaming.getCurrentText();
|
|
37662
|
+
if (agentHooks(opts.agent)?.classifyCompletedReply?.(finalText) === "upgrade_required") {
|
|
37663
|
+
await streaming.closeWithBubble(CURSOR_UPGRADE_MESSAGE);
|
|
37664
|
+
turnClosed = true;
|
|
37665
|
+
history.appendAgentReply(CURSOR_UPGRADE_MESSAGE);
|
|
37666
|
+
void history.flush();
|
|
37667
|
+
log.info("acpRunner", `start_task \u2190 cursor-plan-upgrade-required id=${cmd.id.slice(0, 8)}`);
|
|
37668
|
+
await relay.sendResult(cmd.id, "failed", { error: "cursor plan upgrade required" });
|
|
37669
|
+
return;
|
|
37670
|
+
} else if (replyIsHouseAgentLimit(finalText)) {
|
|
37671
|
+
const houseBubble = houseAgentLimitMessage(finalText);
|
|
37672
|
+
await streaming.closeWithBubble(houseBubble);
|
|
37673
|
+
turnClosed = true;
|
|
37674
|
+
history.appendAgentReply(houseBubble);
|
|
37675
|
+
void history.flush();
|
|
37676
|
+
turnFiles.flushTurn().catch((err) => {
|
|
37677
|
+
log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
|
|
37678
|
+
});
|
|
37679
|
+
log.info("acpRunner", `start_task \u2190 house-agent-limit id=${cmd.id.slice(0, 8)}`);
|
|
37680
|
+
await relay.sendResult(cmd.id, "failed", {
|
|
37681
|
+
error: "house agent usage ceiling / temporarily unavailable"
|
|
37682
|
+
});
|
|
37683
|
+
return;
|
|
37684
|
+
} else if (replyIsAuthFailure(finalText)) {
|
|
37685
|
+
await streaming.closeWithBubble(AUTH_FAILURE_MESSAGE);
|
|
37686
|
+
turnClosed = true;
|
|
37687
|
+
history.appendAgentReply(AUTH_FAILURE_MESSAGE);
|
|
37688
|
+
void history.flush();
|
|
37689
|
+
turnFiles.flushTurn().catch((err) => {
|
|
37690
|
+
log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
|
|
37691
|
+
});
|
|
37692
|
+
void reportCredentialInvalid(opts);
|
|
37693
|
+
log.info("acpRunner", `start_task \u2190 auth-failure-in-reply id=${cmd.id.slice(0, 8)}`);
|
|
37694
|
+
await relay.sendResult(cmd.id, "failed", { error: "agent reply reported auth failure" });
|
|
37695
|
+
return;
|
|
37696
|
+
} else if (shouldOfferOneMRecovery({ detail: "", recentStderr: recentStderr.join("\n"), finalText })) {
|
|
37697
|
+
await streaming.closeWithBubble(ONE_M_CREDITS_MESSAGE);
|
|
37698
|
+
turnClosed = true;
|
|
37699
|
+
history.appendAgentReply(ONE_M_CREDITS_MESSAGE);
|
|
37700
|
+
void history.flush();
|
|
37701
|
+
turnFiles.flushTurn().catch((err) => {
|
|
37702
|
+
log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
|
|
37703
|
+
});
|
|
37704
|
+
void reportCredentialInvalid(opts);
|
|
37705
|
+
log.info("acpRunner", `start_task \u2190 1m-credits-reconnect id=${cmd.id.slice(0, 8)}`);
|
|
37706
|
+
await relay.sendResult(cmd.id, "failed", {
|
|
37707
|
+
error: "agent reply reported 1M-context usage-credits gate"
|
|
37708
|
+
});
|
|
37709
|
+
return;
|
|
37710
|
+
} else {
|
|
37711
|
+
await streaming.closeTurnWithInteractiveDetection();
|
|
37712
|
+
turnClosed = true;
|
|
37713
|
+
const { cleanText, proposal } = extractHandoffProposal(
|
|
37714
|
+
finalText,
|
|
37715
|
+
opts.agent,
|
|
37716
|
+
handoffTargets(ctx)
|
|
37717
|
+
);
|
|
37718
|
+
const replyLine = formatAgentReplyLine(cleanText);
|
|
37719
|
+
if (replyLine.length > 0) {
|
|
37720
|
+
showInfo(replyLine);
|
|
37721
|
+
}
|
|
37722
|
+
history.appendAgentReply(cleanText);
|
|
37723
|
+
void history.flush();
|
|
37724
|
+
const flush = turnFiles.flushTurn().catch((err) => {
|
|
37725
|
+
log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
|
|
37016
37726
|
});
|
|
37727
|
+
if (ctx.squad) await flush;
|
|
37728
|
+
recordSquadTurn(ctx, turnPrompt, cleanText);
|
|
37729
|
+
const autoHop = emitHandoffProposal(ctx, proposal, hop);
|
|
37730
|
+
if (autoHop) {
|
|
37731
|
+
const routed = await routeSquadTask(ctx, autoHop.toAgentId);
|
|
37732
|
+
if (routed.ok) {
|
|
37733
|
+
resolveAutoHandoff(ctx, autoHop);
|
|
37734
|
+
hop += 1;
|
|
37735
|
+
turnPrompt = autoHop.prompt;
|
|
37736
|
+
turnPieces = collectSquadContext(ctx);
|
|
37737
|
+
turnBlocks = [{ type: "text", text: turnPrompt }];
|
|
37738
|
+
maybePrefaceAgentStandard(turnBlocks, opts.agent, opts.sessionId);
|
|
37739
|
+
applySquadContext(turnBlocks, turnPieces, squadContextMode(ctx));
|
|
37740
|
+
await streaming.beginTurn();
|
|
37741
|
+
ctx.history.appendUserPrompt(turnPrompt);
|
|
37742
|
+
continue;
|
|
37743
|
+
}
|
|
37744
|
+
log.warn(
|
|
37745
|
+
"acpRunner",
|
|
37746
|
+
`squad: auto-handoff to ${autoHop.toAgentId} failed: ${routed.error}`
|
|
37747
|
+
);
|
|
37748
|
+
}
|
|
37749
|
+
void publisher.publishOutput({
|
|
37750
|
+
type: "input_suggestion",
|
|
37751
|
+
content: ACP_QUICK_REPLIES,
|
|
37752
|
+
done: true
|
|
37753
|
+
});
|
|
37754
|
+
log.info(
|
|
37755
|
+
"acpRunner",
|
|
37756
|
+
`start_task \u2190 done stopReason=${reply.stopReason ?? "?"} id=${cmd.id.slice(0, 8)}`
|
|
37757
|
+
);
|
|
37758
|
+
await relay.sendResult(cmd.id, "completed", { stopReason: reply.stopReason });
|
|
37759
|
+
return;
|
|
37760
|
+
}
|
|
37761
|
+
} catch (err) {
|
|
37762
|
+
if (turnClosed) {
|
|
37763
|
+
log.warn(
|
|
37764
|
+
"acpRunner",
|
|
37765
|
+
`post-close ack failed (turn already delivered) id=${cmd.id.slice(0, 8)}: ${describeError(err)}`
|
|
37766
|
+
);
|
|
37767
|
+
return;
|
|
37017
37768
|
}
|
|
37018
|
-
|
|
37769
|
+
const hadText = streaming.hasVisibleProgress();
|
|
37770
|
+
const detail = describeError(err);
|
|
37771
|
+
log.warn("acpRunner", `prompt failed: ${detail}`);
|
|
37772
|
+
await cancelStuckTurn(client3);
|
|
37773
|
+
if (looksLikeBudgetExceeded(`${detail}
|
|
37774
|
+
${recentStderr.join("\n")}`)) {
|
|
37775
|
+
await streaming.closeAll();
|
|
37776
|
+
if (!budgetReachedFlag.get()) {
|
|
37777
|
+
budgetReachedFlag.set(true);
|
|
37778
|
+
void postBudgetReached({
|
|
37779
|
+
sessionId: opts.sessionId,
|
|
37780
|
+
pluginId: opts.pluginId,
|
|
37781
|
+
pluginAuthToken: opts.pluginAuthToken,
|
|
37782
|
+
agent: opts.agent,
|
|
37783
|
+
period: extractBudgetPeriod(`${detail}
|
|
37784
|
+
${recentStderr.join("\n")}`)
|
|
37785
|
+
});
|
|
37786
|
+
}
|
|
37787
|
+
await budgetRecovery.offer(cmd.id, turnBlocks, `${detail}
|
|
37019
37788
|
${recentStderr.join("\n")}`);
|
|
37789
|
+
return;
|
|
37790
|
+
}
|
|
37791
|
+
const bubble = failureBubble({
|
|
37792
|
+
detail,
|
|
37793
|
+
recentStderr: recentStderr.join("\n"),
|
|
37794
|
+
hadText,
|
|
37795
|
+
agent: opts.agent
|
|
37796
|
+
});
|
|
37797
|
+
if (bubble) {
|
|
37798
|
+
await streaming.closeWithBubble(bubble);
|
|
37799
|
+
history.appendAgentReply(bubble);
|
|
37800
|
+
void history.flush();
|
|
37801
|
+
} else {
|
|
37802
|
+
await streaming.closeAll();
|
|
37803
|
+
}
|
|
37804
|
+
if (bubble === AUTH_FAILURE_MESSAGE || bubble === ONE_M_CREDITS_MESSAGE) {
|
|
37805
|
+
void reportCredentialInvalid(opts);
|
|
37806
|
+
}
|
|
37807
|
+
await relay.sendResult(cmd.id, "failed", { error: detail });
|
|
37020
37808
|
return;
|
|
37021
37809
|
}
|
|
37022
|
-
|
|
37023
|
-
|
|
37024
|
-
|
|
37025
|
-
|
|
37026
|
-
|
|
37810
|
+
}
|
|
37811
|
+
}
|
|
37812
|
+
async function squadConfigureH(ctx) {
|
|
37813
|
+
const { cmd, relay, opts, squad } = ctx;
|
|
37814
|
+
if (!squad) {
|
|
37815
|
+
await relay.sendResult(cmd.id, "failed", {
|
|
37816
|
+
error: "Agent Squad is not available on this session."
|
|
37027
37817
|
});
|
|
37028
|
-
|
|
37029
|
-
await streaming.closeWithBubble(bubble);
|
|
37030
|
-
history.appendAgentReply(bubble);
|
|
37031
|
-
void history.flush();
|
|
37032
|
-
} else {
|
|
37033
|
-
await streaming.closeAll();
|
|
37034
|
-
}
|
|
37035
|
-
if (bubble === AUTH_FAILURE_MESSAGE || bubble === ONE_M_CREDITS_MESSAGE) {
|
|
37036
|
-
void reportCredentialInvalid(opts);
|
|
37037
|
-
}
|
|
37038
|
-
await relay.sendResult(cmd.id, "failed", { error: detail });
|
|
37818
|
+
return;
|
|
37039
37819
|
}
|
|
37040
|
-
|
|
37820
|
+
const payload = cmd.payload;
|
|
37821
|
+
if (payload?.action === "set") {
|
|
37822
|
+
const applied = squad.setAuto({
|
|
37823
|
+
enabled: payload.autoHandoffs === true,
|
|
37824
|
+
hopBudget: clampHopBudget(payload.hopBudget)
|
|
37825
|
+
});
|
|
37826
|
+
setSquadAuto(opts.pluginId, applied);
|
|
37827
|
+
log.info(
|
|
37828
|
+
"acpRunner",
|
|
37829
|
+
`squad: auto handoffs ${applied.enabled ? "ON" : "OFF"} budget=${applied.hopBudget}`
|
|
37830
|
+
);
|
|
37831
|
+
const result = { ...applied, hopsRemaining: squad.hopsRemaining() };
|
|
37832
|
+
await relay.sendResult(cmd.id, "completed", { ...result });
|
|
37833
|
+
return;
|
|
37834
|
+
}
|
|
37835
|
+
if (payload?.action === "status") {
|
|
37836
|
+
const result = {
|
|
37837
|
+
...squad.auto,
|
|
37838
|
+
hopsRemaining: squad.hopsRemaining()
|
|
37839
|
+
};
|
|
37840
|
+
await relay.sendResult(cmd.id, "completed", { ...result });
|
|
37841
|
+
return;
|
|
37842
|
+
}
|
|
37843
|
+
await relay.sendResult(cmd.id, "failed", { error: "squad_configure: unknown action" });
|
|
37844
|
+
}
|
|
37845
|
+
async function squadStatsH(ctx) {
|
|
37846
|
+
const { cmd, relay, squad } = ctx;
|
|
37847
|
+
if (!squad) {
|
|
37848
|
+
await relay.sendResult(cmd.id, "failed", {
|
|
37849
|
+
error: "Agent Squad is not available on this session."
|
|
37850
|
+
});
|
|
37851
|
+
return;
|
|
37852
|
+
}
|
|
37853
|
+
const stats = squad.stats();
|
|
37854
|
+
await relay.sendResult(cmd.id, "completed", { ...stats });
|
|
37041
37855
|
}
|
|
37042
37856
|
async function groupMentionTaskH(ctx) {
|
|
37043
37857
|
const { cmd, client: client3, relay, streaming, opts, history } = ctx;
|
|
@@ -37050,6 +37864,7 @@ async function groupMentionTaskH(ctx) {
|
|
|
37050
37864
|
});
|
|
37051
37865
|
return;
|
|
37052
37866
|
}
|
|
37867
|
+
ctx.squad?.resetHops();
|
|
37053
37868
|
await streaming.beginTurn();
|
|
37054
37869
|
history.appendUserPrompt(promptText);
|
|
37055
37870
|
let response = "";
|
|
@@ -37444,7 +38259,10 @@ async function integrationsSyncH(ctx) {
|
|
|
37444
38259
|
attached: manifest.integrations.map((e) => e.id)
|
|
37445
38260
|
});
|
|
37446
38261
|
} catch (err) {
|
|
37447
|
-
log.warn(
|
|
38262
|
+
log.warn(
|
|
38263
|
+
"acpRunner",
|
|
38264
|
+
`integrations_sync failed (tools apply next restart): ${describeError(err)}`
|
|
38265
|
+
);
|
|
37448
38266
|
await relay.sendResult(cmd.id, "completed", { synced: false, error: describeError(err) });
|
|
37449
38267
|
}
|
|
37450
38268
|
}
|
|
@@ -37514,7 +38332,9 @@ var ACP_COMMAND_HANDLERS = {
|
|
|
37514
38332
|
skills_configure: skillsConfigureH2,
|
|
37515
38333
|
pack_start: packStartH,
|
|
37516
38334
|
pack_action: packActionH,
|
|
37517
|
-
pack_status: packStatusH
|
|
38335
|
+
pack_status: packStatusH,
|
|
38336
|
+
[SQUAD_CONFIGURE_COMMAND]: squadConfigureH,
|
|
38337
|
+
[SQUAD_STATS_COMMAND]: squadStatsH
|
|
37518
38338
|
};
|
|
37519
38339
|
async function dispatchAcpCommand(ctx) {
|
|
37520
38340
|
const handler = ACP_COMMAND_HANDLERS[ctx.cmd.type];
|
|
@@ -37776,14 +38596,18 @@ var StreamingState = class {
|
|
|
37776
38596
|
}
|
|
37777
38597
|
const cumulativeContent = reconcileCumulative(existing?.content ?? "", delta.delta);
|
|
37778
38598
|
this.streamingChunks.set(chunkId, { kind: delta.kind, content: cumulativeContent });
|
|
38599
|
+
let fenceCut = -1;
|
|
37779
38600
|
if (delta.kind === "text") {
|
|
37780
38601
|
this.recomputeText();
|
|
37781
|
-
|
|
38602
|
+
fenceCut = handoffFenceStartMasked(this.text);
|
|
38603
|
+
const visibleText = fenceCut === -1 ? this.text : this.text.slice(0, fenceCut).trimEnd();
|
|
38604
|
+
void this.publisher.publishOutput({ type: "text", content: visibleText, done: false });
|
|
37782
38605
|
}
|
|
38606
|
+
const visibleChunkContent = delta.kind === "text" && fenceCut !== -1 ? cumulativeContent.slice(0, fenceCut).trimEnd() : cumulativeContent;
|
|
37783
38607
|
void this.publisher.publishStreamingChunk({
|
|
37784
38608
|
chunkId,
|
|
37785
38609
|
kind: delta.kind,
|
|
37786
|
-
content:
|
|
38610
|
+
content: visibleChunkContent,
|
|
37787
38611
|
isFinal: false
|
|
37788
38612
|
});
|
|
37789
38613
|
}
|
|
@@ -37813,8 +38637,28 @@ var StreamingState = class {
|
|
|
37813
38637
|
* spuriously and strand the runner with a free-form pending state
|
|
37814
38638
|
* the user can't see / answer.
|
|
37815
38639
|
*/
|
|
38640
|
+
/**
|
|
38641
|
+
* Presentation-only view of a text buffer: everything BEFORE a
|
|
38642
|
+
* ```codeam-handoff fence. The fence is protocol litter the app renders as
|
|
38643
|
+
* a proposal card (see `handoff-protocol.ts`) — it must never reach the
|
|
38644
|
+
* user, and the TERMINAL frames (`done:true` / `isFinal:true`) are the ones
|
|
38645
|
+
* that persist, so suppressing it only while streaming would still leave it
|
|
38646
|
+
* pinned on the finished bubble. Internal state is untouched:
|
|
38647
|
+
* {@link getCurrentText} keeps returning the RAW text so the turn-close
|
|
38648
|
+
* extraction can parse the proposal out of it.
|
|
38649
|
+
*
|
|
38650
|
+
* Masked-aware (`stripHandoffFences`), same as the live-stream `append()`
|
|
38651
|
+
* cut (`handoffFenceStartMasked`): a TERMINAL frame is the one that
|
|
38652
|
+
* PERSISTS, so cutting on a fence quoted as an example inside a
|
|
38653
|
+
* 4+-backtick block here would permanently truncate the bubble — and an
|
|
38654
|
+
* unmasked live cut would truncate the LIVE view for the rest of the turn
|
|
38655
|
+
* the moment the quoted example's fence-open marker streams in.
|
|
38656
|
+
*/
|
|
38657
|
+
visible(text) {
|
|
38658
|
+
return stripHandoffFences(text);
|
|
38659
|
+
}
|
|
37816
38660
|
async closeAll() {
|
|
37817
|
-
const finalText = this.text;
|
|
38661
|
+
const finalText = this.visible(this.text);
|
|
37818
38662
|
this.text = "";
|
|
37819
38663
|
await Promise.all([
|
|
37820
38664
|
this.publisher.publishOutput({ type: "text", content: finalText, done: true }),
|
|
@@ -37858,7 +38702,9 @@ var StreamingState = class {
|
|
|
37858
38702
|
([chunkId, { kind, content }]) => this.publisher.publishStreamingChunk({
|
|
37859
38703
|
chunkId,
|
|
37860
38704
|
kind,
|
|
37861
|
-
|
|
38705
|
+
// Terminal frame — same fence suppression the live deltas apply, so
|
|
38706
|
+
// a proposal fence never survives on the finalised bubble.
|
|
38707
|
+
content: kind === "text" ? this.visible(content) : content,
|
|
37862
38708
|
isFinal: true
|
|
37863
38709
|
})
|
|
37864
38710
|
)
|
|
@@ -37885,7 +38731,7 @@ var StreamingState = class {
|
|
|
37885
38731
|
* text done:true chunk with the full cumulative).
|
|
37886
38732
|
*/
|
|
37887
38733
|
async closeTurnWithInteractiveDetection() {
|
|
37888
|
-
const finalText = this.text;
|
|
38734
|
+
const finalText = this.visible(this.text);
|
|
37889
38735
|
this.text = "";
|
|
37890
38736
|
const flushSc = this.flushStreamingChunks();
|
|
37891
38737
|
const extracted = extractSelectPrompt(finalText);
|
|
@@ -37982,16 +38828,24 @@ var AcpHistory = class {
|
|
|
37982
38828
|
id: (0, import_node_crypto11.randomUUID)(),
|
|
37983
38829
|
role: "user",
|
|
37984
38830
|
text,
|
|
37985
|
-
timestamp: Date.now()
|
|
38831
|
+
timestamp: Date.now(),
|
|
38832
|
+
agentId: this.opts.agent
|
|
37986
38833
|
});
|
|
37987
38834
|
}
|
|
37988
|
-
|
|
38835
|
+
/**
|
|
38836
|
+
* `agentId` overrides the producing agent for a turn this session's RESIDENT
|
|
38837
|
+
* agent did not author — today only the `@coderabbit` one-shot review, which
|
|
38838
|
+
* runs the batch reviewer inside the session without ever swapping. Defaults
|
|
38839
|
+
* to the resident agent.
|
|
38840
|
+
*/
|
|
38841
|
+
appendAgentReply(text, agentId = this.opts.agent) {
|
|
37989
38842
|
if (text.length === 0) return;
|
|
37990
38843
|
this.messages.push({
|
|
37991
38844
|
id: (0, import_node_crypto11.randomUUID)(),
|
|
37992
38845
|
role: "agent",
|
|
37993
38846
|
text,
|
|
37994
|
-
timestamp: Date.now()
|
|
38847
|
+
timestamp: Date.now(),
|
|
38848
|
+
agentId
|
|
37995
38849
|
});
|
|
37996
38850
|
}
|
|
37997
38851
|
/**
|
|
@@ -38331,6 +39185,8 @@ async function runAcpSession(opts) {
|
|
|
38331
39185
|
pluginAuthToken: opts.pluginAuthToken,
|
|
38332
39186
|
agentId: opts.agent
|
|
38333
39187
|
});
|
|
39188
|
+
void turnFiles.flushTurn().catch(() => {
|
|
39189
|
+
});
|
|
38334
39190
|
const REPO_DIRTY_FLUSH_DEBOUNCE_MS = 2e3;
|
|
38335
39191
|
let repoDirtyTimer = null;
|
|
38336
39192
|
const fileWatcher = new FileWatcherService({
|
|
@@ -38376,9 +39232,12 @@ async function runAcpSession(opts) {
|
|
|
38376
39232
|
publisher,
|
|
38377
39233
|
recentStderr,
|
|
38378
39234
|
budgetRecovery,
|
|
38379
|
-
{
|
|
38380
|
-
|
|
38381
|
-
|
|
39235
|
+
{
|
|
39236
|
+
get: () => _budgetReachedPosted,
|
|
39237
|
+
set: (v) => {
|
|
39238
|
+
_budgetReachedPosted = v;
|
|
39239
|
+
}
|
|
39240
|
+
},
|
|
38382
39241
|
// resume_session re-points the runner's active conversation: the
|
|
38383
39242
|
// relay callback reads `acpSessionId` per command, so every FUTURE
|
|
38384
39243
|
// get_conversation / upload / one-shot serves the RESUMED id — not
|
|
@@ -38388,13 +39247,35 @@ async function runAcpSession(opts) {
|
|
|
38388
39247
|
acpSessionId = id;
|
|
38389
39248
|
},
|
|
38390
39249
|
switchAgentForSession,
|
|
38391
|
-
pendingHandoff
|
|
39250
|
+
pendingHandoff,
|
|
39251
|
+
// Agent Squad: roster/journal state, the @-mention route, the
|
|
39252
|
+
// single-slot proposal, and the SAME serialized event chain the
|
|
39253
|
+
// switch uses (so proposal events can't overtake switch events).
|
|
39254
|
+
squad,
|
|
39255
|
+
routeToAgent,
|
|
39256
|
+
pendingProposal,
|
|
39257
|
+
emitSwitchEvent
|
|
38392
39258
|
);
|
|
38393
39259
|
},
|
|
38394
39260
|
{ id: opts.agent, name: opts.agent, displayName: opts.agent }
|
|
38395
39261
|
);
|
|
38396
39262
|
const HANDOFF_MAX_CHARS = 16e3;
|
|
38397
39263
|
const pendingHandoff = { current: null };
|
|
39264
|
+
const squad = new SquadState({
|
|
39265
|
+
sessionId: opts.sessionId,
|
|
39266
|
+
auto: getSquadAuto(opts.pluginId)
|
|
39267
|
+
});
|
|
39268
|
+
const refreshSquadRoster = () => {
|
|
39269
|
+
void fetchSquadRoster({
|
|
39270
|
+
sessionId: opts.sessionId,
|
|
39271
|
+
pluginId: opts.pluginId,
|
|
39272
|
+
pluginAuthToken: opts.pluginAuthToken
|
|
39273
|
+
}).then((roster) => {
|
|
39274
|
+
if (roster) squad.roster = roster;
|
|
39275
|
+
});
|
|
39276
|
+
};
|
|
39277
|
+
refreshSquadRoster();
|
|
39278
|
+
const pendingProposal = { current: null };
|
|
38398
39279
|
let switchCredentialEnv = {};
|
|
38399
39280
|
const relaunchWith = async (nextAgent) => {
|
|
38400
39281
|
try {
|
|
@@ -38404,6 +39285,7 @@ async function runAcpSession(opts) {
|
|
|
38404
39285
|
await streaming.closeAll();
|
|
38405
39286
|
const prevAgent = opts.agent;
|
|
38406
39287
|
const transcript = history.recentTranscript(HANDOFF_MAX_CHARS);
|
|
39288
|
+
squad.member(prevAgent).acpSessionId = acpSessionId;
|
|
38407
39289
|
await client3.stop();
|
|
38408
39290
|
const adapter = await resolveAcpAdapterWithRetry(nextAgent);
|
|
38409
39291
|
if (!adapter) throw new Error(`no ACP adapter available for ${nextAgent}`);
|
|
@@ -38453,31 +39335,74 @@ async function runAcpSession(opts) {
|
|
|
38453
39335
|
payload
|
|
38454
39336
|
})
|
|
38455
39337
|
);
|
|
38456
|
-
const
|
|
38457
|
-
|
|
38458
|
-
|
|
38459
|
-
|
|
38460
|
-
|
|
38461
|
-
|
|
38462
|
-
|
|
38463
|
-
|
|
38464
|
-
|
|
38465
|
-
|
|
38466
|
-
|
|
38467
|
-
|
|
38468
|
-
switchCredentialEnv = provisionAgentCredentials(agentId, auth);
|
|
38469
|
-
},
|
|
38470
|
-
ensureBinary: (agentId, installScript) => ensureAgentBinaryForSwitch(agentId, installScript),
|
|
38471
|
-
swapRuntime: relaunchWith,
|
|
38472
|
-
revertRuntime: relaunchWith,
|
|
38473
|
-
persistAgent: (agentId) => setSessionAgent(opts.pluginId, agentId),
|
|
38474
|
-
reannounce: (agentId) => {
|
|
38475
|
-
relay.setAgentMeta({ id: agentId, name: agentId, displayName: agentId });
|
|
38476
|
-
relay.reannounceAgents();
|
|
38477
|
-
}
|
|
39338
|
+
const switchDeps = {
|
|
39339
|
+
currentAgent: () => opts.agent,
|
|
39340
|
+
postEvent: emitSwitchEvent,
|
|
39341
|
+
fetchCredential: (agentId) => fetchProvisionCredential({
|
|
39342
|
+
agentId,
|
|
39343
|
+
sessionId: opts.sessionId,
|
|
39344
|
+
pluginId: opts.pluginId,
|
|
39345
|
+
pluginAuthToken: opts.pluginAuthToken,
|
|
39346
|
+
includeInstallScript: true
|
|
39347
|
+
}),
|
|
39348
|
+
provisionCredential: (agentId, auth) => {
|
|
39349
|
+
switchCredentialEnv = provisionAgentCredentials(agentId, auth);
|
|
38478
39350
|
},
|
|
38479
|
-
|
|
38480
|
-
|
|
39351
|
+
ensureBinary: (agentId, installScript) => ensureAgentBinaryForSwitch(agentId, installScript),
|
|
39352
|
+
swapRuntime: relaunchWith,
|
|
39353
|
+
revertRuntime: relaunchWith,
|
|
39354
|
+
persistAgent: (agentId) => setSessionAgent(opts.pluginId, agentId),
|
|
39355
|
+
reannounce: (agentId) => {
|
|
39356
|
+
relay.setAgentMeta({ id: agentId, name: agentId, displayName: agentId });
|
|
39357
|
+
relay.reannounceAgents();
|
|
39358
|
+
}
|
|
39359
|
+
};
|
|
39360
|
+
const switchAgentForSession = async (rawAgentId) => {
|
|
39361
|
+
const result = await performAgentSwitch(switchDeps, rawAgentId);
|
|
39362
|
+
if (result.ok) refreshSquadRoster();
|
|
39363
|
+
return result;
|
|
39364
|
+
};
|
|
39365
|
+
const sessionHandles = () => ({
|
|
39366
|
+
client: client3,
|
|
39367
|
+
acpSessionId,
|
|
39368
|
+
history,
|
|
39369
|
+
jsonlHistory,
|
|
39370
|
+
agentCaps,
|
|
39371
|
+
budgetRecovery
|
|
39372
|
+
});
|
|
39373
|
+
const routeToAgent = async (target, routeOpts = {}) => {
|
|
39374
|
+
const m = squad.member(target);
|
|
39375
|
+
const fast = routeOpts.skipFastPath !== true;
|
|
39376
|
+
const result = await performAgentSwitch(switchDeps, target, {
|
|
39377
|
+
skipProvision: fast && m.provisioned,
|
|
39378
|
+
skipInstall: fast && m.binaryVerified
|
|
39379
|
+
});
|
|
39380
|
+
if (!result.ok) {
|
|
39381
|
+
if (fast) {
|
|
39382
|
+
m.provisioned = false;
|
|
39383
|
+
m.binaryVerified = false;
|
|
39384
|
+
}
|
|
39385
|
+
return { result, handles: sessionHandles() };
|
|
39386
|
+
}
|
|
39387
|
+
m.provisioned = true;
|
|
39388
|
+
m.binaryVerified = true;
|
|
39389
|
+
if (m.acpSessionId && agentCaps?.loadSession) {
|
|
39390
|
+
try {
|
|
39391
|
+
await client3.loadSession(m.acpSessionId);
|
|
39392
|
+
acpSessionId = m.acpSessionId;
|
|
39393
|
+
history.switchActiveSession(m.acpSessionId);
|
|
39394
|
+
pendingHandoff.current = null;
|
|
39395
|
+
log.info(
|
|
39396
|
+
"acpRunner",
|
|
39397
|
+
`squad: resumed ${target}'s conversation ${m.acpSessionId.slice(0, 8)}`
|
|
39398
|
+
);
|
|
39399
|
+
} catch (err) {
|
|
39400
|
+
log.warn("acpRunner", `squad: resume for ${target} failed: ${describeError(err)}`);
|
|
39401
|
+
}
|
|
39402
|
+
}
|
|
39403
|
+
refreshSquadRoster();
|
|
39404
|
+
return { result, handles: sessionHandles() };
|
|
39405
|
+
};
|
|
38481
39406
|
await onboardingWelcomeDone;
|
|
38482
39407
|
relay.start();
|
|
38483
39408
|
void createWakeCredentialProbe({
|
|
@@ -38508,7 +39433,7 @@ async function runAcpSession(opts) {
|
|
|
38508
39433
|
await new Promise(() => {
|
|
38509
39434
|
});
|
|
38510
39435
|
}
|
|
38511
|
-
async function handleCommand(cmd, client3, relay, acpSessionId, streaming, opts, history, jsonlHistory, agentCaps, turnFiles, getBeads, publisher, recentStderr, budgetRecovery, budgetReachedFlag, onActiveSessionChanged, switchAgent, pendingHandoff) {
|
|
39436
|
+
async function handleCommand(cmd, client3, relay, acpSessionId, streaming, opts, history, jsonlHistory, agentCaps, turnFiles, getBeads, publisher, recentStderr, budgetRecovery, budgetReachedFlag, onActiveSessionChanged, switchAgent, pendingHandoff, squad, routeToAgent, pendingProposal, postSquadEvent) {
|
|
38512
39437
|
const session = {
|
|
38513
39438
|
client: client3,
|
|
38514
39439
|
relay,
|
|
@@ -38526,7 +39451,11 @@ async function handleCommand(cmd, client3, relay, acpSessionId, streaming, opts,
|
|
|
38526
39451
|
budgetReachedFlag,
|
|
38527
39452
|
onActiveSessionChanged,
|
|
38528
39453
|
switchAgent,
|
|
38529
|
-
pendingHandoff
|
|
39454
|
+
pendingHandoff,
|
|
39455
|
+
squad,
|
|
39456
|
+
routeToAgent,
|
|
39457
|
+
pendingProposal,
|
|
39458
|
+
postSquadEvent
|
|
38530
39459
|
};
|
|
38531
39460
|
await dispatchAcpCommand(assembleAcpCommandContext(session, cmd));
|
|
38532
39461
|
}
|
|
@@ -39586,9 +40515,9 @@ function startClaudeCredentialSync(opts) {
|
|
|
39586
40515
|
}
|
|
39587
40516
|
|
|
39588
40517
|
// src/beads/workflow-hint.ts
|
|
39589
|
-
var
|
|
39590
|
-
var
|
|
39591
|
-
var
|
|
40518
|
+
var fs76 = __toESM(require("fs"));
|
|
40519
|
+
var path83 = __toESM(require("path"));
|
|
40520
|
+
var os61 = __toESM(require("os"));
|
|
39592
40521
|
var BEADS_HINT_MARKER = "<!-- codeam:beads-workflow -->";
|
|
39593
40522
|
var BEADS_HINT = `${BEADS_HINT_MARKER}
|
|
39594
40523
|
# Beads (bd) \u2014 task tracking + persistent memory (ALWAYS use it)
|
|
@@ -39602,22 +40531,22 @@ This environment uses **bd (beads)** for issue/task tracking and persistent memo
|
|
|
39602
40531
|
- \`bd ready\` (available work) \xB7 \`bd show <id>\` \xB7 \`bd update <id> --claim\` \xB7 \`bd close <id>\`.
|
|
39603
40532
|
- Use \`bd remember "..."\` for persistent knowledge \u2014 do NOT use MEMORY.md files.
|
|
39604
40533
|
${BEADS_HINT_MARKER}`;
|
|
39605
|
-
function ensureBeadsWorkflowHint(homeDir2 =
|
|
40534
|
+
function ensureBeadsWorkflowHint(homeDir2 = os61.homedir()) {
|
|
39606
40535
|
try {
|
|
39607
|
-
const file =
|
|
40536
|
+
const file = path83.join(homeDir2, ".claude", "CLAUDE.md");
|
|
39608
40537
|
let existing = "";
|
|
39609
40538
|
try {
|
|
39610
|
-
existing =
|
|
40539
|
+
existing = fs76.readFileSync(file, "utf8");
|
|
39611
40540
|
} catch {
|
|
39612
40541
|
}
|
|
39613
40542
|
if (existing.includes(BEADS_HINT_MARKER)) return;
|
|
39614
|
-
|
|
40543
|
+
fs76.mkdirSync(path83.dirname(file), { recursive: true });
|
|
39615
40544
|
const next = existing.trim() ? `${existing.trimEnd()}
|
|
39616
40545
|
|
|
39617
40546
|
${BEADS_HINT}
|
|
39618
40547
|
` : `${BEADS_HINT}
|
|
39619
40548
|
`;
|
|
39620
|
-
|
|
40549
|
+
fs76.writeFileSync(file, next);
|
|
39621
40550
|
} catch {
|
|
39622
40551
|
}
|
|
39623
40552
|
}
|
|
@@ -39989,7 +40918,7 @@ var AcpDriver = class {
|
|
|
39989
40918
|
};
|
|
39990
40919
|
|
|
39991
40920
|
// src/baton/transcript-mirror.ts
|
|
39992
|
-
var
|
|
40921
|
+
var fs77 = __toESM(require("fs"));
|
|
39993
40922
|
var TranscriptMirror = class {
|
|
39994
40923
|
constructor(deps) {
|
|
39995
40924
|
this.deps = deps;
|
|
@@ -40056,7 +40985,7 @@ var TranscriptMirror = class {
|
|
|
40056
40985
|
}
|
|
40057
40986
|
};
|
|
40058
40987
|
function defaultWatch(file, onChange) {
|
|
40059
|
-
const w3 =
|
|
40988
|
+
const w3 = fs77.watch(file, { persistent: false }, () => onChange());
|
|
40060
40989
|
return () => w3.close();
|
|
40061
40990
|
}
|
|
40062
40991
|
|
|
@@ -40354,16 +41283,16 @@ function keepDeviceAwake(deps = {}) {
|
|
|
40354
41283
|
}
|
|
40355
41284
|
|
|
40356
41285
|
// src/agents/claude/onboarding.ts
|
|
40357
|
-
var
|
|
40358
|
-
var
|
|
40359
|
-
var
|
|
41286
|
+
var fs78 = __toESM(require("fs"));
|
|
41287
|
+
var os63 = __toESM(require("os"));
|
|
41288
|
+
var path84 = __toESM(require("path"));
|
|
40360
41289
|
var ONBOARDING_VERSION_SENTINEL = "9999.0.0";
|
|
40361
41290
|
function ensureClaudeOnboarded(cwd) {
|
|
40362
41291
|
try {
|
|
40363
|
-
const file =
|
|
41292
|
+
const file = path84.join(os63.homedir(), ".claude.json");
|
|
40364
41293
|
let config = {};
|
|
40365
41294
|
try {
|
|
40366
|
-
config = JSON.parse(
|
|
41295
|
+
config = JSON.parse(fs78.readFileSync(file, "utf8"));
|
|
40367
41296
|
} catch {
|
|
40368
41297
|
}
|
|
40369
41298
|
let changed = false;
|
|
@@ -40388,8 +41317,8 @@ function ensureClaudeOnboarded(cwd) {
|
|
|
40388
41317
|
}
|
|
40389
41318
|
}
|
|
40390
41319
|
if (!changed) return;
|
|
40391
|
-
|
|
40392
|
-
|
|
41320
|
+
fs78.mkdirSync(path84.dirname(file), { recursive: true });
|
|
41321
|
+
fs78.writeFileSync(file, JSON.stringify(config, null, 2));
|
|
40393
41322
|
log.info(
|
|
40394
41323
|
"claude",
|
|
40395
41324
|
`pre-completed Claude onboarding${cwd ? ` + trusted workspace ${cwd}` : ""}`
|
|
@@ -41121,7 +42050,7 @@ var import_picocolors11 = __toESM(require("picocolors"));
|
|
|
41121
42050
|
var import_child_process30 = require("child_process");
|
|
41122
42051
|
var import_util4 = require("util");
|
|
41123
42052
|
var import_picocolors9 = __toESM(require("picocolors"));
|
|
41124
|
-
var
|
|
42053
|
+
var path85 = __toESM(require("path"));
|
|
41125
42054
|
var execFileP6 = (0, import_util4.promisify)(import_child_process30.execFile);
|
|
41126
42055
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
41127
42056
|
function resetStdinForChild() {
|
|
@@ -41610,7 +42539,7 @@ var GitHubCodespacesProvider = class {
|
|
|
41610
42539
|
});
|
|
41611
42540
|
}
|
|
41612
42541
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
41613
|
-
const remoteDir =
|
|
42542
|
+
const remoteDir = path85.posix.dirname(remotePath);
|
|
41614
42543
|
const parts = [
|
|
41615
42544
|
`mkdir -p ${shellQuote(remoteDir)}`,
|
|
41616
42545
|
`cat > ${shellQuote(remotePath)}`
|
|
@@ -41680,7 +42609,7 @@ function shellQuote(s) {
|
|
|
41680
42609
|
// src/services/providers/gitpod.ts
|
|
41681
42610
|
var import_child_process31 = require("child_process");
|
|
41682
42611
|
var import_util5 = require("util");
|
|
41683
|
-
var
|
|
42612
|
+
var path86 = __toESM(require("path"));
|
|
41684
42613
|
var import_picocolors10 = __toESM(require("picocolors"));
|
|
41685
42614
|
var execFileP7 = (0, import_util5.promisify)(import_child_process31.execFile);
|
|
41686
42615
|
var MAX_BUFFER2 = 8 * 1024 * 1024;
|
|
@@ -41920,7 +42849,7 @@ var GitpodProvider = class {
|
|
|
41920
42849
|
});
|
|
41921
42850
|
}
|
|
41922
42851
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
41923
|
-
const remoteDir =
|
|
42852
|
+
const remoteDir = path86.posix.dirname(remotePath);
|
|
41924
42853
|
const parts = [
|
|
41925
42854
|
`mkdir -p ${shellQuote2(remoteDir)}`,
|
|
41926
42855
|
`cat > ${shellQuote2(remotePath)}`
|
|
@@ -41956,7 +42885,7 @@ function shellQuote2(s) {
|
|
|
41956
42885
|
// src/services/providers/gitlab-workspaces.ts
|
|
41957
42886
|
var import_child_process32 = require("child_process");
|
|
41958
42887
|
var import_util6 = require("util");
|
|
41959
|
-
var
|
|
42888
|
+
var path87 = __toESM(require("path"));
|
|
41960
42889
|
var execFileP8 = (0, import_util6.promisify)(import_child_process32.execFile);
|
|
41961
42890
|
var MAX_BUFFER3 = 8 * 1024 * 1024;
|
|
41962
42891
|
var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
|
|
@@ -42216,7 +43145,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
|
|
|
42216
43145
|
}
|
|
42217
43146
|
async uploadFile(workspaceId, remotePath, contents, options = {}) {
|
|
42218
43147
|
const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
|
|
42219
|
-
const remoteDir =
|
|
43148
|
+
const remoteDir = path87.posix.dirname(remotePath);
|
|
42220
43149
|
const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
|
|
42221
43150
|
if (options.mode != null) {
|
|
42222
43151
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
|
|
@@ -42284,7 +43213,7 @@ function shellQuote3(s) {
|
|
|
42284
43213
|
// src/services/providers/railway.ts
|
|
42285
43214
|
var import_child_process33 = require("child_process");
|
|
42286
43215
|
var import_util7 = require("util");
|
|
42287
|
-
var
|
|
43216
|
+
var path88 = __toESM(require("path"));
|
|
42288
43217
|
var execFileP9 = (0, import_util7.promisify)(import_child_process33.execFile);
|
|
42289
43218
|
var MAX_BUFFER4 = 8 * 1024 * 1024;
|
|
42290
43219
|
function resetStdinForChild4() {
|
|
@@ -42520,7 +43449,7 @@ var RailwayProvider = class {
|
|
|
42520
43449
|
if (!projectId || !serviceId) {
|
|
42521
43450
|
throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
|
|
42522
43451
|
}
|
|
42523
|
-
const remoteDir =
|
|
43452
|
+
const remoteDir = path88.posix.dirname(remotePath);
|
|
42524
43453
|
const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
|
|
42525
43454
|
if (options.mode != null) {
|
|
42526
43455
|
parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
|
|
@@ -43166,8 +44095,8 @@ async function invite() {
|
|
|
43166
44095
|
var import_node_dns = require("dns");
|
|
43167
44096
|
var import_node_util5 = require("util");
|
|
43168
44097
|
var import_node_crypto13 = require("crypto");
|
|
43169
|
-
var
|
|
43170
|
-
var
|
|
44098
|
+
var fs79 = __toESM(require("fs"));
|
|
44099
|
+
var path89 = __toESM(require("path"));
|
|
43171
44100
|
var import_picocolors14 = __toESM(require("picocolors"));
|
|
43172
44101
|
var dnsResolveP = (0, import_node_util5.promisify)(import_node_dns.resolve);
|
|
43173
44102
|
async function checkDns(apiBase2) {
|
|
@@ -43223,13 +44152,13 @@ async function checkHealth(apiBase2) {
|
|
|
43223
44152
|
}
|
|
43224
44153
|
}
|
|
43225
44154
|
function checkConfigDir() {
|
|
43226
|
-
const dir =
|
|
44155
|
+
const dir = path89.join(require("os").homedir(), ".codeam");
|
|
43227
44156
|
try {
|
|
43228
|
-
|
|
43229
|
-
const probe =
|
|
43230
|
-
|
|
43231
|
-
const read2 =
|
|
43232
|
-
|
|
44157
|
+
fs79.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
44158
|
+
const probe = path89.join(dir, ".doctor-probe");
|
|
44159
|
+
fs79.writeFileSync(probe, "ok", { mode: 384 });
|
|
44160
|
+
const read2 = fs79.readFileSync(probe, "utf8");
|
|
44161
|
+
fs79.unlinkSync(probe);
|
|
43233
44162
|
if (read2 !== "ok") throw new Error("write/read round-trip mismatch");
|
|
43234
44163
|
return {
|
|
43235
44164
|
id: "config-dir",
|
|
@@ -43269,9 +44198,9 @@ function checkSessions() {
|
|
|
43269
44198
|
}
|
|
43270
44199
|
}
|
|
43271
44200
|
function checkAgentBinaries() {
|
|
43272
|
-
const
|
|
44201
|
+
const os66 = createOsStrategy();
|
|
43273
44202
|
return getEnabledAgents().map((meta) => {
|
|
43274
|
-
const found =
|
|
44203
|
+
const found = os66.findInPath(meta.binaryName);
|
|
43275
44204
|
return {
|
|
43276
44205
|
id: `agent-${meta.id}`,
|
|
43277
44206
|
label: `Agent binary: ${meta.displayName} (${meta.binaryName})`,
|
|
@@ -43293,7 +44222,7 @@ function checkNodePty() {
|
|
|
43293
44222
|
detail: "not required on this platform"
|
|
43294
44223
|
};
|
|
43295
44224
|
}
|
|
43296
|
-
const vendoredPath =
|
|
44225
|
+
const vendoredPath = path89.join(__dirname, "vendor", "node-pty");
|
|
43297
44226
|
for (const target of [vendoredPath, "node-pty"]) {
|
|
43298
44227
|
try {
|
|
43299
44228
|
require(target);
|
|
@@ -43335,7 +44264,7 @@ function checkChokidar() {
|
|
|
43335
44264
|
}
|
|
43336
44265
|
async function doctor(args2 = []) {
|
|
43337
44266
|
const json = args2.includes("--json");
|
|
43338
|
-
const cliVersion = true ? "2.
|
|
44267
|
+
const cliVersion = true ? "2.65.0" : "0.0.0-dev";
|
|
43339
44268
|
const apiBase2 = resolveApiBaseUrl();
|
|
43340
44269
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
43341
44270
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -43726,7 +44655,7 @@ async function mcpRun(args2) {
|
|
|
43726
44655
|
// src/commands/version.ts
|
|
43727
44656
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
43728
44657
|
function version2() {
|
|
43729
|
-
const v = true ? "2.
|
|
44658
|
+
const v = true ? "2.65.0" : "unknown";
|
|
43730
44659
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
43731
44660
|
}
|
|
43732
44661
|
|
|
@@ -43875,10 +44804,10 @@ var EXIT_CODE_NAMES = {
|
|
|
43875
44804
|
};
|
|
43876
44805
|
|
|
43877
44806
|
// src/index.ts
|
|
43878
|
-
var
|
|
44807
|
+
var os65 = __toESM(require("os"));
|
|
43879
44808
|
if (!process.env.HOME) {
|
|
43880
44809
|
try {
|
|
43881
|
-
const home =
|
|
44810
|
+
const home = os65.homedir();
|
|
43882
44811
|
if (home) process.env.HOME = home;
|
|
43883
44812
|
} catch {
|
|
43884
44813
|
}
|