codeam-cli 2.64.0 → 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 +21 -0
- package/dist/index.js +841 -342
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2791,6 +2791,15 @@ function resolveApiBaseUrl() {
|
|
|
2791
2791
|
|
|
2792
2792
|
// ../../packages/shared/src/types/agent-squad.ts
|
|
2793
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
|
+
}
|
|
2794
2803
|
var SQUAD_SPECIALTIES = {
|
|
2795
2804
|
claude: "deep reasoning, refactors, and multi-step architecture work",
|
|
2796
2805
|
codex: "fast, focused implementation and test fixing",
|
|
@@ -3257,6 +3266,22 @@ function makeConfig(baseDir) {
|
|
|
3257
3266
|
s.agent = agent;
|
|
3258
3267
|
save(c2);
|
|
3259
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
|
+
}
|
|
3260
3285
|
function clearAll2() {
|
|
3261
3286
|
try {
|
|
3262
3287
|
fs3.unlinkSync(file);
|
|
@@ -3269,7 +3294,7 @@ function makeConfig(baseDir) {
|
|
|
3269
3294
|
function loadCliConfig2() {
|
|
3270
3295
|
return load();
|
|
3271
3296
|
}
|
|
3272
|
-
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 };
|
|
3273
3298
|
}
|
|
3274
3299
|
var CODESPACE_ENV_KEYS = [
|
|
3275
3300
|
"PREVIEW_TUNNEL_TOKEN",
|
|
@@ -3293,7 +3318,7 @@ function loadCodespaceEnv() {
|
|
|
3293
3318
|
}
|
|
3294
3319
|
}
|
|
3295
3320
|
var _default = makeConfig();
|
|
3296
|
-
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;
|
|
3297
3322
|
|
|
3298
3323
|
// src/commands/pair-auto.ts
|
|
3299
3324
|
var fs63 = __toESM(require("fs"));
|
|
@@ -8048,7 +8073,7 @@ function readAnonId() {
|
|
|
8048
8073
|
}
|
|
8049
8074
|
function superProperties() {
|
|
8050
8075
|
return {
|
|
8051
|
-
cliVersion: true ? "2.
|
|
8076
|
+
cliVersion: true ? "2.65.0" : "0.0.0-dev",
|
|
8052
8077
|
nodeVersion: process.version,
|
|
8053
8078
|
platform: process.platform,
|
|
8054
8079
|
arch: process.arch,
|
|
@@ -8229,7 +8254,7 @@ var os4 = __toESM(require("os"));
|
|
|
8229
8254
|
// package.json
|
|
8230
8255
|
var package_default = {
|
|
8231
8256
|
name: "codeam-cli",
|
|
8232
|
-
version: "2.
|
|
8257
|
+
version: "2.65.0",
|
|
8233
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.",
|
|
8234
8259
|
type: "commonjs",
|
|
8235
8260
|
main: "dist/index.js",
|
|
@@ -9681,7 +9706,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9681
9706
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9682
9707
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9683
9708
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9684
|
-
..."2.
|
|
9709
|
+
..."2.65.0" ? { ideVersion: "2.65.0" } : {}
|
|
9685
9710
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9686
9711
|
}
|
|
9687
9712
|
/**
|
|
@@ -21100,7 +21125,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
21100
21125
|
if (process.env.NODE_ENV === "test") return;
|
|
21101
21126
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
21102
21127
|
if (process.env.CI) return;
|
|
21103
|
-
const current2 = true ? "2.
|
|
21128
|
+
const current2 = true ? "2.65.0" : null;
|
|
21104
21129
|
if (!current2) return;
|
|
21105
21130
|
const cache = readCache();
|
|
21106
21131
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -21117,7 +21142,7 @@ function checkForUpdates() {
|
|
|
21117
21142
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
21118
21143
|
if (process.env.CI) return;
|
|
21119
21144
|
if (!process.stdout.isTTY) return;
|
|
21120
|
-
const current2 = true ? "2.
|
|
21145
|
+
const current2 = true ? "2.65.0" : null;
|
|
21121
21146
|
if (!current2) return;
|
|
21122
21147
|
const cache = readCache();
|
|
21123
21148
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -21137,7 +21162,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
|
21137
21162
|
var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
21138
21163
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
21139
21164
|
function currentCliVersion() {
|
|
21140
|
-
return true ? "2.
|
|
21165
|
+
return true ? "2.65.0" : null;
|
|
21141
21166
|
}
|
|
21142
21167
|
function runCmd(cmd, args2, timeoutMs) {
|
|
21143
21168
|
return new Promise((resolve9) => {
|
|
@@ -28558,12 +28583,292 @@ async function resolveAcpAdapterWithRetry(agent, opts = {}) {
|
|
|
28558
28583
|
var import_node_crypto11 = require("crypto");
|
|
28559
28584
|
|
|
28560
28585
|
// src/services/history.service.ts
|
|
28561
|
-
var
|
|
28562
|
-
var
|
|
28563
|
-
var
|
|
28586
|
+
var fs66 = __toESM(require("fs"));
|
|
28587
|
+
var path72 = __toESM(require("path"));
|
|
28588
|
+
var os56 = __toESM(require("os"));
|
|
28564
28589
|
var https7 = __toESM(require("https"));
|
|
28565
28590
|
var http6 = __toESM(require("http"));
|
|
28566
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
|
|
28567
28872
|
var historyRecordSchema = import_zod2.z.object({
|
|
28568
28873
|
type: import_zod2.z.string().optional(),
|
|
28569
28874
|
timestamp: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
|
|
@@ -28583,11 +28888,24 @@ function extractText3(content) {
|
|
|
28583
28888
|
return "";
|
|
28584
28889
|
}
|
|
28585
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
|
+
}
|
|
28586
28904
|
function parseJsonl(filePath) {
|
|
28587
28905
|
const messages = [];
|
|
28588
28906
|
let raw;
|
|
28589
28907
|
try {
|
|
28590
|
-
raw =
|
|
28908
|
+
raw = fs66.readFileSync(filePath, "utf8");
|
|
28591
28909
|
} catch (err) {
|
|
28592
28910
|
if (err.code !== "ENOENT") {
|
|
28593
28911
|
log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
|
|
@@ -28728,7 +29046,7 @@ var HistoryService = class _HistoryService {
|
|
|
28728
29046
|
return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
|
|
28729
29047
|
}
|
|
28730
29048
|
get projectDir() {
|
|
28731
|
-
return this.runtime.resolveHistoryDir(this.cwd) ??
|
|
29049
|
+
return this.runtime.resolveHistoryDir(this.cwd) ?? path72.join(os56.homedir(), ".claude", "projects", encodeCwd(this.cwd));
|
|
28732
29050
|
}
|
|
28733
29051
|
/** Set the current Claude conversation ID (extracted from /cost command or session start) */
|
|
28734
29052
|
setCurrentConversationId(id) {
|
|
@@ -28740,7 +29058,7 @@ var HistoryService = class _HistoryService {
|
|
|
28740
29058
|
/** Return the current message count in the active conversation. */
|
|
28741
29059
|
getCurrentMessageCount() {
|
|
28742
29060
|
if (!this.currentConversationId) return 0;
|
|
28743
|
-
const filePath =
|
|
29061
|
+
const filePath = path72.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
28744
29062
|
return parseJsonl(filePath).length;
|
|
28745
29063
|
}
|
|
28746
29064
|
/**
|
|
@@ -28751,7 +29069,7 @@ var HistoryService = class _HistoryService {
|
|
|
28751
29069
|
const deadline = Date.now() + timeoutMs;
|
|
28752
29070
|
while (Date.now() < deadline) {
|
|
28753
29071
|
if (!this.currentConversationId) return null;
|
|
28754
|
-
const filePath =
|
|
29072
|
+
const filePath = path72.join(this.projectDir, `${this.currentConversationId}.jsonl`);
|
|
28755
29073
|
const messages = parseJsonl(filePath);
|
|
28756
29074
|
if (messages.length > previousCount) {
|
|
28757
29075
|
for (let i = messages.length - 1; i >= previousCount; i--) {
|
|
@@ -28777,16 +29095,16 @@ var HistoryService = class _HistoryService {
|
|
|
28777
29095
|
const dir = this.projectDir;
|
|
28778
29096
|
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
28779
29097
|
try {
|
|
28780
|
-
const files =
|
|
29098
|
+
const files = fs66.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
28781
29099
|
try {
|
|
28782
|
-
const stat3 =
|
|
29100
|
+
const stat3 = fs66.statSync(path72.join(dir, e.name));
|
|
28783
29101
|
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
28784
29102
|
} catch {
|
|
28785
29103
|
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
28786
29104
|
}
|
|
28787
29105
|
}).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
|
|
28788
29106
|
if (files.length > 0) {
|
|
28789
|
-
this.currentConversationId =
|
|
29107
|
+
this.currentConversationId = path72.basename(files[0].name, ".jsonl");
|
|
28790
29108
|
}
|
|
28791
29109
|
} catch {
|
|
28792
29110
|
}
|
|
@@ -28820,13 +29138,13 @@ var HistoryService = class _HistoryService {
|
|
|
28820
29138
|
const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
|
|
28821
29139
|
let entries;
|
|
28822
29140
|
try {
|
|
28823
|
-
entries =
|
|
29141
|
+
entries = fs66.readdirSync(dir, { withFileTypes: true });
|
|
28824
29142
|
} catch {
|
|
28825
29143
|
return null;
|
|
28826
29144
|
}
|
|
28827
29145
|
const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
|
|
28828
29146
|
try {
|
|
28829
|
-
const stat3 =
|
|
29147
|
+
const stat3 = fs66.statSync(path72.join(dir, e.name));
|
|
28830
29148
|
return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
|
|
28831
29149
|
} catch {
|
|
28832
29150
|
return { name: e.name, mtime: 0, birthtime: 0 };
|
|
@@ -28835,12 +29153,12 @@ var HistoryService = class _HistoryService {
|
|
|
28835
29153
|
if (files.length === 0) return null;
|
|
28836
29154
|
const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
|
|
28837
29155
|
if (!files.some((f) => f.name === targetFile)) return null;
|
|
28838
|
-
return this.extractUsageFromFile(
|
|
29156
|
+
return this.extractUsageFromFile(path72.join(dir, targetFile));
|
|
28839
29157
|
}
|
|
28840
29158
|
extractUsageFromFile(filePath) {
|
|
28841
29159
|
let raw;
|
|
28842
29160
|
try {
|
|
28843
|
-
raw =
|
|
29161
|
+
raw = fs66.readFileSync(filePath, "utf8");
|
|
28844
29162
|
} catch {
|
|
28845
29163
|
return null;
|
|
28846
29164
|
}
|
|
@@ -28885,9 +29203,9 @@ var HistoryService = class _HistoryService {
|
|
|
28885
29203
|
let totalCost = 0;
|
|
28886
29204
|
let files;
|
|
28887
29205
|
try {
|
|
28888
|
-
files =
|
|
29206
|
+
files = fs66.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
|
|
28889
29207
|
try {
|
|
28890
|
-
return
|
|
29208
|
+
return fs66.statSync(path72.join(projectDir, f)).mtimeMs >= monthStartMs;
|
|
28891
29209
|
} catch {
|
|
28892
29210
|
return false;
|
|
28893
29211
|
}
|
|
@@ -28898,7 +29216,7 @@ var HistoryService = class _HistoryService {
|
|
|
28898
29216
|
for (const file of files) {
|
|
28899
29217
|
let raw;
|
|
28900
29218
|
try {
|
|
28901
|
-
raw =
|
|
29219
|
+
raw = fs66.readFileSync(path72.join(projectDir, file), "utf8");
|
|
28902
29220
|
} catch {
|
|
28903
29221
|
continue;
|
|
28904
29222
|
}
|
|
@@ -28977,7 +29295,7 @@ var HistoryService = class _HistoryService {
|
|
|
28977
29295
|
if (this.runtime.resolveHistoryFile) {
|
|
28978
29296
|
return this.runtime.resolveHistoryFile(this.cwd, sessionId);
|
|
28979
29297
|
}
|
|
28980
|
-
return
|
|
29298
|
+
return path72.join(this.projectDir, `${sessionId}.jsonl`);
|
|
28981
29299
|
}
|
|
28982
29300
|
/**
|
|
28983
29301
|
* Parse a conversation's messages from disk, agent-aware. Claude uses the
|
|
@@ -28989,6 +29307,10 @@ var HistoryService = class _HistoryService {
|
|
|
28989
29307
|
* convention as parseJsonl.
|
|
28990
29308
|
*/
|
|
28991
29309
|
readConversation(sessionId) {
|
|
29310
|
+
const agentId = this.runtime.id;
|
|
29311
|
+
return scrubSquadContext(this.readConversationRaw(sessionId)).map((m) => ({ ...m, agentId }));
|
|
29312
|
+
}
|
|
29313
|
+
readConversationRaw(sessionId) {
|
|
28992
29314
|
if (this.runtime.resolveHistoryFile) {
|
|
28993
29315
|
const filePath = this.runtime.resolveHistoryFile(this.cwd, sessionId);
|
|
28994
29316
|
if (!filePath) return [];
|
|
@@ -29011,7 +29333,7 @@ var HistoryService = class _HistoryService {
|
|
|
29011
29333
|
};
|
|
29012
29334
|
});
|
|
29013
29335
|
}
|
|
29014
|
-
return parseJsonl(
|
|
29336
|
+
return parseJsonl(path72.join(this.projectDir, `${sessionId}.jsonl`));
|
|
29015
29337
|
}
|
|
29016
29338
|
async loadConversation(sessionId) {
|
|
29017
29339
|
const messages = this.readConversation(sessionId);
|
|
@@ -29079,7 +29401,7 @@ var HistoryService = class _HistoryService {
|
|
|
29079
29401
|
if (!filePath) return false;
|
|
29080
29402
|
let mtimeMs;
|
|
29081
29403
|
try {
|
|
29082
|
-
mtimeMs =
|
|
29404
|
+
mtimeMs = fs66.statSync(filePath).mtimeMs;
|
|
29083
29405
|
} catch {
|
|
29084
29406
|
return false;
|
|
29085
29407
|
}
|
|
@@ -29154,10 +29476,10 @@ var HistoryService = class _HistoryService {
|
|
|
29154
29476
|
|
|
29155
29477
|
// src/agents/acp/client.ts
|
|
29156
29478
|
var import_node_child_process29 = require("child_process");
|
|
29157
|
-
var
|
|
29479
|
+
var fs67 = __toESM(require("fs/promises"));
|
|
29158
29480
|
var fsSync = __toESM(require("fs"));
|
|
29159
|
-
var
|
|
29160
|
-
var
|
|
29481
|
+
var os58 = __toESM(require("os"));
|
|
29482
|
+
var path74 = __toESM(require("path"));
|
|
29161
29483
|
var import_node_stream = require("stream");
|
|
29162
29484
|
|
|
29163
29485
|
// ../../node_modules/@agentclientprotocol/sdk/dist/schema/index.js
|
|
@@ -33183,8 +33505,8 @@ function createIdleTimeout(idleMs, makeError, activeIdleMs = idleMs) {
|
|
|
33183
33505
|
}
|
|
33184
33506
|
|
|
33185
33507
|
// src/agents/acp/internal-paths.ts
|
|
33186
|
-
var
|
|
33187
|
-
var
|
|
33508
|
+
var path73 = __toESM(require("path"));
|
|
33509
|
+
var os57 = __toESM(require("os"));
|
|
33188
33510
|
var INTERNAL_TOKENS = [".codeam", "house-claude"];
|
|
33189
33511
|
var SELF_HOSTED_WORKSPACE_RE = /\.codeam[/\\]self-hosted/gi;
|
|
33190
33512
|
function textReferencesInternal(text) {
|
|
@@ -33192,13 +33514,13 @@ function textReferencesInternal(text) {
|
|
|
33192
33514
|
const scrubbed = text.replace(SELF_HOSTED_WORKSPACE_RE, " ");
|
|
33193
33515
|
return INTERNAL_TOKENS.some((t2) => scrubbed.includes(t2));
|
|
33194
33516
|
}
|
|
33195
|
-
function pathIsInternal(p2, homeDir2 =
|
|
33517
|
+
function pathIsInternal(p2, homeDir2 = os57.homedir()) {
|
|
33196
33518
|
if (!p2) return false;
|
|
33197
|
-
const abs =
|
|
33198
|
-
const home =
|
|
33199
|
-
const within = (root) => abs === root || abs.startsWith(root +
|
|
33200
|
-
if (within(
|
|
33201
|
-
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`);
|
|
33202
33524
|
}
|
|
33203
33525
|
function toolCallReferencesInternal(call) {
|
|
33204
33526
|
if (textReferencesInternal(call.title)) return true;
|
|
@@ -34157,7 +34479,7 @@ var AcpClient = class {
|
|
|
34157
34479
|
throw new RequestError(-32002, GUARDRAIL_SECRET_READ_BLOCK_REASON, { uri: params.path });
|
|
34158
34480
|
}
|
|
34159
34481
|
try {
|
|
34160
|
-
const content = await
|
|
34482
|
+
const content = await fs67.readFile(params.path, "utf8");
|
|
34161
34483
|
return applyLineRange(content, params.line ?? null, params.limit ?? null);
|
|
34162
34484
|
} catch (err) {
|
|
34163
34485
|
const code = err.code;
|
|
@@ -34180,7 +34502,7 @@ var AcpClient = class {
|
|
|
34180
34502
|
throw new RequestError(-32002, INTERNAL_BLOCK_REASON, { uri: params.path });
|
|
34181
34503
|
}
|
|
34182
34504
|
try {
|
|
34183
|
-
await
|
|
34505
|
+
await fs67.writeFile(params.path, params.content, "utf8");
|
|
34184
34506
|
return {};
|
|
34185
34507
|
} catch (err) {
|
|
34186
34508
|
const code = err.code;
|
|
@@ -34240,29 +34562,29 @@ function applyLineRange(content, line, limit) {
|
|
|
34240
34562
|
return { content: lines.slice(start2, end).join("\n") };
|
|
34241
34563
|
}
|
|
34242
34564
|
function knownAgentBinaryDirs() {
|
|
34243
|
-
const home =
|
|
34565
|
+
const home = os58.homedir();
|
|
34244
34566
|
const out2 = [];
|
|
34245
34567
|
out2.push("/tmp/codeam-node20/bin");
|
|
34246
34568
|
for (const root of [
|
|
34247
34569
|
"/usr/local/share/nvm/versions/node",
|
|
34248
|
-
|
|
34570
|
+
path74.join(home, ".nvm/versions/node")
|
|
34249
34571
|
]) {
|
|
34250
34572
|
try {
|
|
34251
34573
|
for (const child of fsSync.readdirSync(root)) {
|
|
34252
|
-
out2.push(
|
|
34574
|
+
out2.push(path74.join(root, child, "bin"));
|
|
34253
34575
|
}
|
|
34254
34576
|
} catch {
|
|
34255
34577
|
}
|
|
34256
34578
|
}
|
|
34257
|
-
out2.push(
|
|
34579
|
+
out2.push(path74.join(home, ".volta/bin"));
|
|
34258
34580
|
out2.push("/usr/local/bin");
|
|
34259
34581
|
out2.push("/usr/bin");
|
|
34260
|
-
out2.push(
|
|
34261
|
-
out2.push(
|
|
34582
|
+
out2.push(path74.join(home, ".local/bin"));
|
|
34583
|
+
out2.push(path74.join(home, "bin"));
|
|
34262
34584
|
if (process.platform === "win32") {
|
|
34263
34585
|
const { LOCALAPPDATA, APPDATA } = process.env;
|
|
34264
|
-
if (LOCALAPPDATA) out2.push(
|
|
34265
|
-
if (APPDATA) out2.push(
|
|
34586
|
+
if (LOCALAPPDATA) out2.push(path74.join(LOCALAPPDATA, "cursor-agent"));
|
|
34587
|
+
if (APPDATA) out2.push(path74.join(APPDATA, "npm"));
|
|
34266
34588
|
}
|
|
34267
34589
|
return out2.filter((p2) => {
|
|
34268
34590
|
try {
|
|
@@ -34274,7 +34596,7 @@ function knownAgentBinaryDirs() {
|
|
|
34274
34596
|
}
|
|
34275
34597
|
function expandPathForAgentBinaries(existingPath) {
|
|
34276
34598
|
const existing = new Set(
|
|
34277
|
-
existingPath.split(
|
|
34599
|
+
existingPath.split(path74.delimiter).filter((p2) => p2.length > 0)
|
|
34278
34600
|
);
|
|
34279
34601
|
const additions = [];
|
|
34280
34602
|
for (const dir of knownAgentBinaryDirs()) {
|
|
@@ -34284,7 +34606,7 @@ function expandPathForAgentBinaries(existingPath) {
|
|
|
34284
34606
|
}
|
|
34285
34607
|
}
|
|
34286
34608
|
if (additions.length === 0) return existingPath;
|
|
34287
|
-
return [...additions, existingPath].filter((p2) => p2.length > 0).join(
|
|
34609
|
+
return [...additions, existingPath].filter((p2) => p2.length > 0).join(path74.delimiter);
|
|
34288
34610
|
}
|
|
34289
34611
|
|
|
34290
34612
|
// src/agents/acp/headroom-budget-proxy.ts
|
|
@@ -34467,121 +34789,6 @@ async function performAgentSwitch(deps, rawAgentId, fastPath = {}) {
|
|
|
34467
34789
|
return { ok: true, agentId };
|
|
34468
34790
|
}
|
|
34469
34791
|
|
|
34470
|
-
// src/agents/acp/squad-roster.ts
|
|
34471
|
-
var fs67 = __toESM(require("fs"));
|
|
34472
|
-
var os58 = __toESM(require("os"));
|
|
34473
|
-
var path74 = __toESM(require("path"));
|
|
34474
|
-
var PROMPT_MAX = 500;
|
|
34475
|
-
var REPLY_SUMMARY_MAX = 1e3;
|
|
34476
|
-
var PREAMBLE_MAX = 2e3;
|
|
34477
|
-
var BRIEFING_DEFAULT_MAX = 8e3;
|
|
34478
|
-
var GENERIC_SPECIALTY = "general implementation tasks";
|
|
34479
|
-
function clip(s, max) {
|
|
34480
|
-
return s.length > max ? s.slice(0, max) : s;
|
|
34481
|
-
}
|
|
34482
|
-
function defaultMember() {
|
|
34483
|
-
return { acpSessionId: null, provisioned: false, binaryVerified: false, lastTurnIndex: 0 };
|
|
34484
|
-
}
|
|
34485
|
-
function journalPathFor(homeDir2, sessionId) {
|
|
34486
|
-
return path74.join(homeDir2, ".codeam", `squad-journal-${sessionId}.json`);
|
|
34487
|
-
}
|
|
34488
|
-
function loadJournal(journalPath) {
|
|
34489
|
-
try {
|
|
34490
|
-
const raw = JSON.parse(fs67.readFileSync(journalPath, "utf-8"));
|
|
34491
|
-
return Array.isArray(raw.turns) ? raw.turns : [];
|
|
34492
|
-
} catch {
|
|
34493
|
-
return [];
|
|
34494
|
-
}
|
|
34495
|
-
}
|
|
34496
|
-
var SquadState = class {
|
|
34497
|
-
/** Set by the caller after fetchSquadRoster; null until then. */
|
|
34498
|
-
roster = null;
|
|
34499
|
-
journalPath;
|
|
34500
|
-
turns;
|
|
34501
|
-
members = /* @__PURE__ */ new Map();
|
|
34502
|
-
constructor(opts) {
|
|
34503
|
-
this.journalPath = journalPathFor(opts.homeDir ?? os58.homedir(), opts.sessionId);
|
|
34504
|
-
this.turns = loadJournal(this.journalPath);
|
|
34505
|
-
}
|
|
34506
|
-
/** Returns (creating on first access) the mutable per-agent state. */
|
|
34507
|
-
member(agentId) {
|
|
34508
|
-
let m = this.members.get(agentId);
|
|
34509
|
-
if (!m) {
|
|
34510
|
-
m = defaultMember();
|
|
34511
|
-
this.members.set(agentId, m);
|
|
34512
|
-
}
|
|
34513
|
-
return m;
|
|
34514
|
-
}
|
|
34515
|
-
/** Appends a journal entry and persists (fire-and-forget durability). */
|
|
34516
|
-
recordTurn(entry) {
|
|
34517
|
-
const turn = {
|
|
34518
|
-
turn: this.turns.length + 1,
|
|
34519
|
-
agentId: entry.agentId,
|
|
34520
|
-
prompt: clip(entry.prompt, PROMPT_MAX),
|
|
34521
|
-
replySummary: clip(entry.replySummary, REPLY_SUMMARY_MAX),
|
|
34522
|
-
filesTouched: entry.filesTouched
|
|
34523
|
-
};
|
|
34524
|
-
this.turns.push(turn);
|
|
34525
|
-
this.persist();
|
|
34526
|
-
}
|
|
34527
|
-
turnCount() {
|
|
34528
|
-
return this.turns.length;
|
|
34529
|
-
}
|
|
34530
|
-
entriesSince(turnIndex) {
|
|
34531
|
-
return this.turns.filter((t2) => t2.turn > turnIndex);
|
|
34532
|
-
}
|
|
34533
|
-
persist() {
|
|
34534
|
-
try {
|
|
34535
|
-
fs67.mkdirSync(path74.dirname(this.journalPath), { recursive: true, mode: 448 });
|
|
34536
|
-
fs67.writeFileSync(this.journalPath, JSON.stringify({ turns: this.turns }), { mode: 384 });
|
|
34537
|
-
} catch {
|
|
34538
|
-
}
|
|
34539
|
-
}
|
|
34540
|
-
};
|
|
34541
|
-
function specialtyFor(agentId) {
|
|
34542
|
-
return SQUAD_SPECIALTIES[agentId] ?? GENERIC_SPECIALTY;
|
|
34543
|
-
}
|
|
34544
|
-
function buildTeamPreamble(roster, currentAgent, opts) {
|
|
34545
|
-
const others = roster.agents.filter((a) => a.agentId !== currentAgent);
|
|
34546
|
-
if (others.length === 0) return null;
|
|
34547
|
-
const lines = [
|
|
34548
|
-
"[Team context] You are the active agent in a CodeAgent Mobile session where the user",
|
|
34549
|
-
"has a squad of agents and can pass work between them. Your available teammates:",
|
|
34550
|
-
...others.map((a) => `- ${a.displayName} \u2014 best at: ${specialtyFor(a.agentId)}`)
|
|
34551
|
-
];
|
|
34552
|
-
if (opts.handoffInstructions) {
|
|
34553
|
-
lines.push(
|
|
34554
|
-
"If a task clearly fits a teammate better than you, you MAY propose a handoff by ending",
|
|
34555
|
-
`your reply with a fenced code block tagged ${HANDOFF_FENCE_TAG} containing ONE JSON object:`,
|
|
34556
|
-
'{"to":"<teammate id>","reason":"<one sentence>","prompt":"<the prompt they should run>"}',
|
|
34557
|
-
"Propose at most one handoff per reply, only when genuinely better, and never announce",
|
|
34558
|
-
"the block in prose \u2014 the app renders it as a card the user can accept."
|
|
34559
|
-
);
|
|
34560
|
-
}
|
|
34561
|
-
return clip(lines.join("\n"), PREAMBLE_MAX);
|
|
34562
|
-
}
|
|
34563
|
-
function renderJournalEntry(e) {
|
|
34564
|
-
const filesClause = e.filesTouched.length > 0 ? ` (files: ${e.filesTouched.join(", ")})` : "";
|
|
34565
|
-
return `- turn ${e.turn} (${e.agentId}): ${e.prompt} \u2192 ${e.replySummary}${filesClause}`;
|
|
34566
|
-
}
|
|
34567
|
-
function buildDeltaBriefing(entries, maxChars = BRIEFING_DEFAULT_MAX) {
|
|
34568
|
-
if (entries.length === 0) return null;
|
|
34569
|
-
const header = "[Team update] While you were away, other agents worked on this session:";
|
|
34570
|
-
const footer = "Continue from the CURRENT state of the working tree.";
|
|
34571
|
-
const envelope = header.length + 1 + footer.length + 1;
|
|
34572
|
-
const sorted = [...entries].sort((a, b) => a.turn - b.turn);
|
|
34573
|
-
const lines = [];
|
|
34574
|
-
let bodyLen = 0;
|
|
34575
|
-
for (let i = sorted.length - 1; i >= 0; i--) {
|
|
34576
|
-
const line = renderJournalEntry(sorted[i]);
|
|
34577
|
-
const addedLen = line.length + (lines.length > 0 ? 1 : 0);
|
|
34578
|
-
if (lines.length > 0 && envelope + bodyLen + addedLen > maxChars) break;
|
|
34579
|
-
lines.unshift(line);
|
|
34580
|
-
bodyLen += addedLen;
|
|
34581
|
-
}
|
|
34582
|
-
return [header, lines.join("\n"), footer].join("\n");
|
|
34583
|
-
}
|
|
34584
|
-
|
|
34585
34792
|
// src/services/streaming/transport.ts
|
|
34586
34793
|
var http7 = __toESM(require("http"));
|
|
34587
34794
|
var https8 = __toESM(require("https"));
|
|
@@ -34881,6 +35088,13 @@ var AcpPublisher = class {
|
|
|
34881
35088
|
* `mode: 'replace'` so a re-send overrides any stale persisted
|
|
34882
35089
|
* version — important for the ACP path because each turn we ship
|
|
34883
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.
|
|
34884
35098
|
*/
|
|
34885
35099
|
async pushConversation(args2) {
|
|
34886
35100
|
const url2 = `${this.apiBase}/api/sessions/conversation`;
|
|
@@ -35101,8 +35315,19 @@ function stripHandoffFences(text) {
|
|
|
35101
35315
|
}
|
|
35102
35316
|
return restore(stripFences(masked));
|
|
35103
35317
|
}
|
|
35104
|
-
function
|
|
35105
|
-
|
|
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;
|
|
35106
35331
|
}
|
|
35107
35332
|
|
|
35108
35333
|
// src/agents/acp/onboarding.ts
|
|
@@ -35882,11 +36107,34 @@ var TurnFileAggregator = class {
|
|
|
35882
36107
|
*/
|
|
35883
36108
|
baselineByKey = /* @__PURE__ */ new Map();
|
|
35884
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 = [];
|
|
35885
36121
|
/** Stop the outbox scheduler. Idempotent. */
|
|
35886
36122
|
stop() {
|
|
35887
36123
|
this.stopped = true;
|
|
35888
36124
|
this.outbox.stop();
|
|
35889
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
|
+
}
|
|
35890
36138
|
/**
|
|
35891
36139
|
* Run the discovery + git collection + POST pipeline for one
|
|
35892
36140
|
* turn. Errors are swallowed (logged) so an agent never blocks on a
|
|
@@ -35927,6 +36175,7 @@ var TurnFileAggregator = class {
|
|
|
35927
36175
|
if (!base) return true;
|
|
35928
36176
|
return base.linesAdded !== f.linesAdded || base.linesRemoved !== f.linesRemoved || base.fileStatus !== f.fileStatus;
|
|
35929
36177
|
});
|
|
36178
|
+
this.lastTurnPaths = novel.map((f) => f.filePath);
|
|
35930
36179
|
if (novel.length === 0) {
|
|
35931
36180
|
log.trace(
|
|
35932
36181
|
"turnFiles",
|
|
@@ -35946,10 +36195,7 @@ var TurnFileAggregator = class {
|
|
|
35946
36195
|
await this.outbox.enqueue(entry);
|
|
35947
36196
|
}
|
|
35948
36197
|
} catch (err) {
|
|
35949
|
-
log.warn(
|
|
35950
|
-
"turnFiles",
|
|
35951
|
-
`flushTurn failed: ${err.message ?? String(err)}`
|
|
35952
|
-
);
|
|
36198
|
+
log.warn("turnFiles", `flushTurn failed: ${err.message ?? String(err)}`);
|
|
35953
36199
|
}
|
|
35954
36200
|
}
|
|
35955
36201
|
/**
|
|
@@ -36945,6 +37191,56 @@ async function detectRepoStack(cwd, runtime) {
|
|
|
36945
37191
|
}
|
|
36946
37192
|
}
|
|
36947
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
|
+
|
|
36948
37244
|
// src/agents/acp/command-handlers.ts
|
|
36949
37245
|
var import_node_child_process31 = require("child_process");
|
|
36950
37246
|
|
|
@@ -37143,39 +37439,72 @@ async function routeSquadTask(ctx, target) {
|
|
|
37143
37439
|
}
|
|
37144
37440
|
return { ok: true };
|
|
37145
37441
|
}
|
|
37146
|
-
function
|
|
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
|
+
}
|
|
37147
37460
|
if (ctx.pendingHandoff?.current) {
|
|
37148
|
-
|
|
37461
|
+
pieces.push(ctx.pendingHandoff.current);
|
|
37149
37462
|
ctx.pendingHandoff.current = null;
|
|
37150
37463
|
}
|
|
37151
|
-
|
|
37152
|
-
|
|
37153
|
-
|
|
37154
|
-
|
|
37155
|
-
|
|
37156
|
-
|
|
37157
|
-
|
|
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;
|
|
37158
37474
|
}
|
|
37159
|
-
|
|
37160
|
-
|
|
37161
|
-
|
|
37162
|
-
|
|
37163
|
-
|
|
37164
|
-
|
|
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);
|
|
37165
37492
|
}
|
|
37166
37493
|
}
|
|
37167
37494
|
function recordSquadTurn(ctx, prompt, replySummary) {
|
|
37168
|
-
const { squad, opts } = ctx;
|
|
37495
|
+
const { squad, opts, turnFiles } = ctx;
|
|
37169
37496
|
if (!squad) return;
|
|
37170
37497
|
squad.recordTurn({
|
|
37171
37498
|
agentId: opts.agent,
|
|
37172
37499
|
prompt,
|
|
37173
37500
|
replySummary,
|
|
37174
37501
|
// TurnFileAggregator owns per-turn file changesets end-to-end (git diff →
|
|
37175
|
-
// outbox POST)
|
|
37176
|
-
//
|
|
37177
|
-
//
|
|
37178
|
-
|
|
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)
|
|
37179
37508
|
});
|
|
37180
37509
|
squad.member(opts.agent).lastTurnIndex = squad.turnCount();
|
|
37181
37510
|
}
|
|
@@ -37190,45 +37519,112 @@ function resolvePendingProposal(ctx, requestedAgentId) {
|
|
|
37190
37519
|
if (!slot || !pending) return;
|
|
37191
37520
|
slot.current = null;
|
|
37192
37521
|
const accepted = requestedAgentId === pending.toAgentId;
|
|
37522
|
+
if (accepted) ctx.squad?.countAccepted();
|
|
37193
37523
|
log.info(
|
|
37194
37524
|
"acpRunner",
|
|
37195
37525
|
`squad: handoff ${pending.proposalId} ${accepted ? "accepted" : "declined"}`
|
|
37196
37526
|
);
|
|
37197
|
-
|
|
37198
|
-
|
|
37199
|
-
|
|
37200
|
-
|
|
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;
|
|
37201
37533
|
}
|
|
37202
|
-
function emitHandoffProposal(ctx, proposal) {
|
|
37534
|
+
function emitHandoffProposal(ctx, proposal, hop) {
|
|
37203
37535
|
const { squad, pendingProposal, postSquadEvent, opts, cmd } = ctx;
|
|
37204
|
-
if (!proposal || !pendingProposal || !postSquadEvent) return;
|
|
37205
|
-
if (squad?.roster?.handoffsEnabled !== true) return;
|
|
37536
|
+
if (!proposal || !pendingProposal || !postSquadEvent) return null;
|
|
37537
|
+
if (squad?.roster?.handoffsEnabled !== true) return null;
|
|
37206
37538
|
if (pendingProposal.current) {
|
|
37207
37539
|
log.info("acpRunner", "squad: dropping handoff proposal \u2014 one is already pending");
|
|
37208
|
-
return;
|
|
37540
|
+
return null;
|
|
37209
37541
|
}
|
|
37542
|
+
const auto = squad.auto.enabled && squad.hopsRemaining() > 0;
|
|
37543
|
+
if (auto) squad.consumeHop();
|
|
37210
37544
|
const record2 = {
|
|
37211
|
-
proposalId:
|
|
37545
|
+
proposalId: proposalIdFor(cmd.id, hop),
|
|
37212
37546
|
fromAgentId: opts.agent,
|
|
37213
37547
|
toAgentId: proposal.to,
|
|
37214
37548
|
reason: proposal.reason,
|
|
37215
|
-
prompt: proposal.prompt
|
|
37549
|
+
prompt: proposal.prompt,
|
|
37550
|
+
...auto ? { auto: true, hopsRemaining: squad.hopsRemaining() } : {}
|
|
37216
37551
|
};
|
|
37217
|
-
|
|
37218
|
-
log.info(
|
|
37552
|
+
squad.countProposal({ auto });
|
|
37553
|
+
log.info(
|
|
37554
|
+
"acpRunner",
|
|
37555
|
+
`squad: handoff proposed ${opts.agent} \u2192 ${record2.toAgentId}${auto ? " (auto)" : ""}`
|
|
37556
|
+
);
|
|
37219
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
|
+
});
|
|
37220
37612
|
}
|
|
37221
37613
|
async function startTaskH(ctx) {
|
|
37222
37614
|
const { cmd, relay, streaming, opts, turnFiles, publisher, recentStderr, budgetReachedFlag } = ctx;
|
|
37223
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
|
+
}
|
|
37224
37622
|
const blocks = buildAcpPromptBlocks(payload ?? {});
|
|
37225
37623
|
if (blocks.length === 0) {
|
|
37226
37624
|
log.warn("acpRunner", "start_task with empty prompt + no attachments; ignoring");
|
|
37227
37625
|
await relay.sendResult(cmd.id, "failed", { error: "empty prompt" });
|
|
37228
37626
|
return;
|
|
37229
37627
|
}
|
|
37230
|
-
const requestedAgentId = typeof payload?.agentId === "string" ? payload.agentId.trim() : "";
|
|
37231
|
-
resolvePendingProposal(ctx, requestedAgentId);
|
|
37232
37628
|
if (requestedAgentId.length > 0 && requestedAgentId !== opts.agent) {
|
|
37233
37629
|
const routed = await routeSquadTask(ctx, requestedAgentId);
|
|
37234
37630
|
if (!routed.ok) {
|
|
@@ -37237,7 +37633,6 @@ async function startTaskH(ctx) {
|
|
|
37237
37633
|
return;
|
|
37238
37634
|
}
|
|
37239
37635
|
}
|
|
37240
|
-
const { client: client3, history, budgetRecovery } = ctx;
|
|
37241
37636
|
const promptText = blocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
37242
37637
|
const imageCount = blocks.filter((b) => b.type === "image").length;
|
|
37243
37638
|
log.info(
|
|
@@ -37249,133 +37644,214 @@ async function startTaskH(ctx) {
|
|
|
37249
37644
|
showInfo(echoLine);
|
|
37250
37645
|
}
|
|
37251
37646
|
await streaming.beginTurn();
|
|
37252
|
-
history.appendUserPrompt(promptText);
|
|
37647
|
+
ctx.history.appendUserPrompt(promptText);
|
|
37253
37648
|
maybePrefaceAgentStandard(blocks, opts.agent, opts.sessionId);
|
|
37254
|
-
|
|
37255
|
-
|
|
37256
|
-
|
|
37257
|
-
|
|
37258
|
-
|
|
37259
|
-
|
|
37260
|
-
|
|
37261
|
-
|
|
37262
|
-
|
|
37263
|
-
|
|
37264
|
-
|
|
37265
|
-
await
|
|
37266
|
-
|
|
37267
|
-
|
|
37268
|
-
|
|
37269
|
-
|
|
37270
|
-
|
|
37271
|
-
|
|
37272
|
-
|
|
37273
|
-
|
|
37274
|
-
|
|
37275
|
-
|
|
37276
|
-
|
|
37277
|
-
|
|
37278
|
-
|
|
37279
|
-
|
|
37280
|
-
|
|
37281
|
-
|
|
37282
|
-
|
|
37283
|
-
|
|
37284
|
-
|
|
37285
|
-
|
|
37286
|
-
|
|
37287
|
-
|
|
37288
|
-
|
|
37289
|
-
|
|
37290
|
-
|
|
37291
|
-
|
|
37292
|
-
|
|
37293
|
-
|
|
37294
|
-
|
|
37295
|
-
|
|
37296
|
-
|
|
37297
|
-
|
|
37298
|
-
|
|
37299
|
-
|
|
37300
|
-
|
|
37301
|
-
|
|
37302
|
-
|
|
37303
|
-
|
|
37304
|
-
|
|
37305
|
-
|
|
37306
|
-
|
|
37307
|
-
|
|
37308
|
-
|
|
37309
|
-
|
|
37310
|
-
|
|
37311
|
-
|
|
37312
|
-
|
|
37313
|
-
|
|
37314
|
-
|
|
37315
|
-
|
|
37316
|
-
|
|
37317
|
-
|
|
37318
|
-
|
|
37319
|
-
|
|
37320
|
-
|
|
37321
|
-
|
|
37322
|
-
|
|
37323
|
-
|
|
37324
|
-
|
|
37325
|
-
|
|
37326
|
-
|
|
37327
|
-
|
|
37328
|
-
|
|
37329
|
-
|
|
37330
|
-
|
|
37331
|
-
|
|
37332
|
-
|
|
37333
|
-
|
|
37334
|
-
|
|
37335
|
-
|
|
37336
|
-
|
|
37337
|
-
|
|
37338
|
-
|
|
37339
|
-
|
|
37340
|
-
|
|
37341
|
-
|
|
37342
|
-
|
|
37343
|
-
|
|
37344
|
-
|
|
37345
|
-
|
|
37346
|
-
|
|
37347
|
-
|
|
37348
|
-
|
|
37349
|
-
|
|
37350
|
-
|
|
37351
|
-
|
|
37352
|
-
|
|
37353
|
-
|
|
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)}`);
|
|
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
|
|
37354
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;
|
|
37355
37760
|
}
|
|
37356
|
-
|
|
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;
|
|
37768
|
+
}
|
|
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}
|
|
37357
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 });
|
|
37358
37808
|
return;
|
|
37359
37809
|
}
|
|
37360
|
-
|
|
37361
|
-
|
|
37362
|
-
|
|
37363
|
-
|
|
37364
|
-
|
|
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."
|
|
37365
37817
|
});
|
|
37366
|
-
|
|
37367
|
-
await streaming.closeWithBubble(bubble);
|
|
37368
|
-
history.appendAgentReply(bubble);
|
|
37369
|
-
void history.flush();
|
|
37370
|
-
} else {
|
|
37371
|
-
await streaming.closeAll();
|
|
37372
|
-
}
|
|
37373
|
-
if (bubble === AUTH_FAILURE_MESSAGE || bubble === ONE_M_CREDITS_MESSAGE) {
|
|
37374
|
-
void reportCredentialInvalid(opts);
|
|
37375
|
-
}
|
|
37376
|
-
await relay.sendResult(cmd.id, "failed", { error: detail });
|
|
37818
|
+
return;
|
|
37377
37819
|
}
|
|
37378
|
-
|
|
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 });
|
|
37379
37855
|
}
|
|
37380
37856
|
async function groupMentionTaskH(ctx) {
|
|
37381
37857
|
const { cmd, client: client3, relay, streaming, opts, history } = ctx;
|
|
@@ -37388,6 +37864,7 @@ async function groupMentionTaskH(ctx) {
|
|
|
37388
37864
|
});
|
|
37389
37865
|
return;
|
|
37390
37866
|
}
|
|
37867
|
+
ctx.squad?.resetHops();
|
|
37391
37868
|
await streaming.beginTurn();
|
|
37392
37869
|
history.appendUserPrompt(promptText);
|
|
37393
37870
|
let response = "";
|
|
@@ -37782,7 +38259,10 @@ async function integrationsSyncH(ctx) {
|
|
|
37782
38259
|
attached: manifest.integrations.map((e) => e.id)
|
|
37783
38260
|
});
|
|
37784
38261
|
} catch (err) {
|
|
37785
|
-
log.warn(
|
|
38262
|
+
log.warn(
|
|
38263
|
+
"acpRunner",
|
|
38264
|
+
`integrations_sync failed (tools apply next restart): ${describeError(err)}`
|
|
38265
|
+
);
|
|
37786
38266
|
await relay.sendResult(cmd.id, "completed", { synced: false, error: describeError(err) });
|
|
37787
38267
|
}
|
|
37788
38268
|
}
|
|
@@ -37852,7 +38332,9 @@ var ACP_COMMAND_HANDLERS = {
|
|
|
37852
38332
|
skills_configure: skillsConfigureH2,
|
|
37853
38333
|
pack_start: packStartH,
|
|
37854
38334
|
pack_action: packActionH,
|
|
37855
|
-
pack_status: packStatusH
|
|
38335
|
+
pack_status: packStatusH,
|
|
38336
|
+
[SQUAD_CONFIGURE_COMMAND]: squadConfigureH,
|
|
38337
|
+
[SQUAD_STATS_COMMAND]: squadStatsH
|
|
37856
38338
|
};
|
|
37857
38339
|
async function dispatchAcpCommand(ctx) {
|
|
37858
38340
|
const handler = ACP_COMMAND_HANDLERS[ctx.cmd.type];
|
|
@@ -38117,7 +38599,7 @@ var StreamingState = class {
|
|
|
38117
38599
|
let fenceCut = -1;
|
|
38118
38600
|
if (delta.kind === "text") {
|
|
38119
38601
|
this.recomputeText();
|
|
38120
|
-
fenceCut =
|
|
38602
|
+
fenceCut = handoffFenceStartMasked(this.text);
|
|
38121
38603
|
const visibleText = fenceCut === -1 ? this.text : this.text.slice(0, fenceCut).trimEnd();
|
|
38122
38604
|
void this.publisher.publishOutput({ type: "text", content: visibleText, done: false });
|
|
38123
38605
|
}
|
|
@@ -38165,11 +38647,12 @@ var StreamingState = class {
|
|
|
38165
38647
|
* {@link getCurrentText} keeps returning the RAW text so the turn-close
|
|
38166
38648
|
* extraction can parse the proposal out of it.
|
|
38167
38649
|
*
|
|
38168
|
-
* Masked-aware (`stripHandoffFences`),
|
|
38169
|
-
* cut (`
|
|
38650
|
+
* Masked-aware (`stripHandoffFences`), same as the live-stream `append()`
|
|
38651
|
+
* cut (`handoffFenceStartMasked`): a TERMINAL frame is the one that
|
|
38170
38652
|
* PERSISTS, so cutting on a fence quoted as an example inside a
|
|
38171
|
-
* 4+-backtick block here would permanently truncate the bubble
|
|
38172
|
-
* cut
|
|
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.
|
|
38173
38656
|
*/
|
|
38174
38657
|
visible(text) {
|
|
38175
38658
|
return stripHandoffFences(text);
|
|
@@ -38345,16 +38828,24 @@ var AcpHistory = class {
|
|
|
38345
38828
|
id: (0, import_node_crypto11.randomUUID)(),
|
|
38346
38829
|
role: "user",
|
|
38347
38830
|
text,
|
|
38348
|
-
timestamp: Date.now()
|
|
38831
|
+
timestamp: Date.now(),
|
|
38832
|
+
agentId: this.opts.agent
|
|
38349
38833
|
});
|
|
38350
38834
|
}
|
|
38351
|
-
|
|
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) {
|
|
38352
38842
|
if (text.length === 0) return;
|
|
38353
38843
|
this.messages.push({
|
|
38354
38844
|
id: (0, import_node_crypto11.randomUUID)(),
|
|
38355
38845
|
role: "agent",
|
|
38356
38846
|
text,
|
|
38357
|
-
timestamp: Date.now()
|
|
38847
|
+
timestamp: Date.now(),
|
|
38848
|
+
agentId
|
|
38358
38849
|
});
|
|
38359
38850
|
}
|
|
38360
38851
|
/**
|
|
@@ -38694,6 +39185,8 @@ async function runAcpSession(opts) {
|
|
|
38694
39185
|
pluginAuthToken: opts.pluginAuthToken,
|
|
38695
39186
|
agentId: opts.agent
|
|
38696
39187
|
});
|
|
39188
|
+
void turnFiles.flushTurn().catch(() => {
|
|
39189
|
+
});
|
|
38697
39190
|
const REPO_DIRTY_FLUSH_DEBOUNCE_MS = 2e3;
|
|
38698
39191
|
let repoDirtyTimer = null;
|
|
38699
39192
|
const fileWatcher = new FileWatcherService({
|
|
@@ -38739,9 +39232,12 @@ async function runAcpSession(opts) {
|
|
|
38739
39232
|
publisher,
|
|
38740
39233
|
recentStderr,
|
|
38741
39234
|
budgetRecovery,
|
|
38742
|
-
{
|
|
38743
|
-
|
|
38744
|
-
|
|
39235
|
+
{
|
|
39236
|
+
get: () => _budgetReachedPosted,
|
|
39237
|
+
set: (v) => {
|
|
39238
|
+
_budgetReachedPosted = v;
|
|
39239
|
+
}
|
|
39240
|
+
},
|
|
38745
39241
|
// resume_session re-points the runner's active conversation: the
|
|
38746
39242
|
// relay callback reads `acpSessionId` per command, so every FUTURE
|
|
38747
39243
|
// get_conversation / upload / one-shot serves the RESUMED id — not
|
|
@@ -38765,7 +39261,10 @@ async function runAcpSession(opts) {
|
|
|
38765
39261
|
);
|
|
38766
39262
|
const HANDOFF_MAX_CHARS = 16e3;
|
|
38767
39263
|
const pendingHandoff = { current: null };
|
|
38768
|
-
const squad = new SquadState({
|
|
39264
|
+
const squad = new SquadState({
|
|
39265
|
+
sessionId: opts.sessionId,
|
|
39266
|
+
auto: getSquadAuto(opts.pluginId)
|
|
39267
|
+
});
|
|
38769
39268
|
const refreshSquadRoster = () => {
|
|
38770
39269
|
void fetchSquadRoster({
|
|
38771
39270
|
sessionId: opts.sessionId,
|
|
@@ -43765,7 +44264,7 @@ function checkChokidar() {
|
|
|
43765
44264
|
}
|
|
43766
44265
|
async function doctor(args2 = []) {
|
|
43767
44266
|
const json = args2.includes("--json");
|
|
43768
|
-
const cliVersion = true ? "2.
|
|
44267
|
+
const cliVersion = true ? "2.65.0" : "0.0.0-dev";
|
|
43769
44268
|
const apiBase2 = resolveApiBaseUrl();
|
|
43770
44269
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
43771
44270
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -44156,7 +44655,7 @@ async function mcpRun(args2) {
|
|
|
44156
44655
|
// src/commands/version.ts
|
|
44157
44656
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
44158
44657
|
function version2() {
|
|
44159
|
-
const v = true ? "2.
|
|
44658
|
+
const v = true ? "2.65.0" : "unknown";
|
|
44160
44659
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
44161
44660
|
}
|
|
44162
44661
|
|