@buildautomaton/cli 0.1.44 → 0.1.46
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/dist/bridge-mcp-server.js +161 -0
- package/dist/bridge-mcp-server.js.map +7 -0
- package/dist/cli.js +419 -39
- package/dist/cli.js.map +4 -4
- package/dist/fork-history-mcp-server.js +132 -0
- package/dist/fork-history-mcp-server.js.map +7 -0
- package/dist/index.js +419 -39
- package/dist/index.js.map +4 -4
- package/dist/migrations/004_bridge_runtime.sql +14 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2237,7 +2237,7 @@ var require_websocket = __commonJS({
|
|
|
2237
2237
|
"use strict";
|
|
2238
2238
|
var EventEmitter2 = __require("events");
|
|
2239
2239
|
var https2 = __require("https");
|
|
2240
|
-
var
|
|
2240
|
+
var http2 = __require("http");
|
|
2241
2241
|
var net = __require("net");
|
|
2242
2242
|
var tls = __require("tls");
|
|
2243
2243
|
var { randomBytes: randomBytes2, createHash: createHash2 } = __require("crypto");
|
|
@@ -2771,7 +2771,7 @@ var require_websocket = __commonJS({
|
|
|
2771
2771
|
}
|
|
2772
2772
|
const defaultPort = isSecure ? 443 : 80;
|
|
2773
2773
|
const key = randomBytes2(16).toString("base64");
|
|
2774
|
-
const request = isSecure ? https2.request :
|
|
2774
|
+
const request = isSecure ? https2.request : http2.request;
|
|
2775
2775
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
2776
2776
|
let perMessageDeflate;
|
|
2777
2777
|
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
@@ -3265,7 +3265,7 @@ var require_websocket_server = __commonJS({
|
|
|
3265
3265
|
"../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js"(exports, module) {
|
|
3266
3266
|
"use strict";
|
|
3267
3267
|
var EventEmitter2 = __require("events");
|
|
3268
|
-
var
|
|
3268
|
+
var http2 = __require("http");
|
|
3269
3269
|
var { Duplex } = __require("stream");
|
|
3270
3270
|
var { createHash: createHash2 } = __require("crypto");
|
|
3271
3271
|
var extension = require_extension();
|
|
@@ -3340,8 +3340,8 @@ var require_websocket_server = __commonJS({
|
|
|
3340
3340
|
);
|
|
3341
3341
|
}
|
|
3342
3342
|
if (options.port != null) {
|
|
3343
|
-
this._server =
|
|
3344
|
-
const body =
|
|
3343
|
+
this._server = http2.createServer((req, res) => {
|
|
3344
|
+
const body = http2.STATUS_CODES[426];
|
|
3345
3345
|
res.writeHead(426, {
|
|
3346
3346
|
"Content-Length": body.length,
|
|
3347
3347
|
"Content-Type": "text/plain"
|
|
@@ -3628,7 +3628,7 @@ var require_websocket_server = __commonJS({
|
|
|
3628
3628
|
this.destroy();
|
|
3629
3629
|
}
|
|
3630
3630
|
function abortHandshake(socket, code, message, headers) {
|
|
3631
|
-
message = message ||
|
|
3631
|
+
message = message || http2.STATUS_CODES[code];
|
|
3632
3632
|
headers = {
|
|
3633
3633
|
Connection: "close",
|
|
3634
3634
|
"Content-Type": "text/html",
|
|
@@ -3637,7 +3637,7 @@ var require_websocket_server = __commonJS({
|
|
|
3637
3637
|
};
|
|
3638
3638
|
socket.once("finish", socket.destroy);
|
|
3639
3639
|
socket.end(
|
|
3640
|
-
`HTTP/1.1 ${code} ${
|
|
3640
|
+
`HTTP/1.1 ${code} ${http2.STATUS_CODES[code]}\r
|
|
3641
3641
|
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
|
|
3642
3642
|
);
|
|
3643
3643
|
}
|
|
@@ -23553,7 +23553,7 @@ function createSdkStdioSessionContext(options) {
|
|
|
23553
23553
|
return {
|
|
23554
23554
|
cwd: options.cwd,
|
|
23555
23555
|
onFileChange: options.onFileChange,
|
|
23556
|
-
mcpServers: [],
|
|
23556
|
+
mcpServers: options.mcpServers ?? [],
|
|
23557
23557
|
persistedAcpSessionId: options.persistedAcpSessionId,
|
|
23558
23558
|
agentLabel: "ACP",
|
|
23559
23559
|
suppressLoadReplay: suppressLoadReplayRef,
|
|
@@ -23996,6 +23996,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
23996
23996
|
});
|
|
23997
23997
|
const sessionCtx = createSdkStdioSessionContext({
|
|
23998
23998
|
cwd,
|
|
23999
|
+
mcpServers: options.mcpServers,
|
|
23999
24000
|
persistedAcpSessionId,
|
|
24000
24001
|
backendAgentType,
|
|
24001
24002
|
agentConfig,
|
|
@@ -24468,7 +24469,7 @@ function installBridgeProcessResilience() {
|
|
|
24468
24469
|
}
|
|
24469
24470
|
|
|
24470
24471
|
// src/cli-version.ts
|
|
24471
|
-
var CLI_VERSION = "0.1.
|
|
24472
|
+
var CLI_VERSION = "0.1.46".length > 0 ? "0.1.46" : "0.0.0-dev";
|
|
24472
24473
|
|
|
24473
24474
|
// src/connection/heartbeat/constants.ts
|
|
24474
24475
|
var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -26116,38 +26117,38 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
26116
26117
|
// src/agents/acp/acp-prompt-routing-registry.ts
|
|
26117
26118
|
var AcpPromptRoutingRegistry = class {
|
|
26118
26119
|
runs = /* @__PURE__ */ new Map();
|
|
26119
|
-
|
|
26120
|
+
streamingRunIdBySessionAgent = /* @__PURE__ */ new Map();
|
|
26120
26121
|
registerRun(route) {
|
|
26121
26122
|
this.runs.set(route.runId, route);
|
|
26122
26123
|
}
|
|
26123
26124
|
unregisterRun(runId) {
|
|
26124
26125
|
this.runs.delete(runId);
|
|
26125
|
-
for (const [
|
|
26126
|
+
for (const [sessionAgentKey, streamingRunId] of this.streamingRunIdBySessionAgent.entries()) {
|
|
26126
26127
|
if (streamingRunId === runId) {
|
|
26127
|
-
this.
|
|
26128
|
+
this.streamingRunIdBySessionAgent.delete(sessionAgentKey);
|
|
26128
26129
|
}
|
|
26129
26130
|
}
|
|
26130
26131
|
}
|
|
26131
26132
|
isRegisteredRun(runId) {
|
|
26132
26133
|
return this.runs.has(runId);
|
|
26133
26134
|
}
|
|
26134
|
-
setStreamingRunId(
|
|
26135
|
+
setStreamingRunId(sessionAgentKey, runId) {
|
|
26135
26136
|
if (runId) {
|
|
26136
|
-
this.
|
|
26137
|
+
this.streamingRunIdBySessionAgent.set(sessionAgentKey, runId);
|
|
26137
26138
|
} else {
|
|
26138
|
-
this.
|
|
26139
|
+
this.streamingRunIdBySessionAgent.delete(sessionAgentKey);
|
|
26139
26140
|
}
|
|
26140
26141
|
}
|
|
26141
|
-
clearStreamingRunId(
|
|
26142
|
-
if (this.
|
|
26143
|
-
this.
|
|
26142
|
+
clearStreamingRunId(sessionAgentKey, runId) {
|
|
26143
|
+
if (this.streamingRunIdBySessionAgent.get(sessionAgentKey) === runId) {
|
|
26144
|
+
this.streamingRunIdBySessionAgent.delete(sessionAgentKey);
|
|
26144
26145
|
}
|
|
26145
26146
|
}
|
|
26146
|
-
getStreamingRunId(
|
|
26147
|
-
return this.
|
|
26147
|
+
getStreamingRunId(sessionAgentKey) {
|
|
26148
|
+
return this.streamingRunIdBySessionAgent.get(sessionAgentKey);
|
|
26148
26149
|
}
|
|
26149
|
-
resolveRouting(
|
|
26150
|
-
const runId = this.
|
|
26150
|
+
resolveRouting(sessionAgentKey) {
|
|
26151
|
+
const runId = this.streamingRunIdBySessionAgent.get(sessionAgentKey);
|
|
26151
26152
|
if (!runId) return void 0;
|
|
26152
26153
|
return this.runs.get(runId);
|
|
26153
26154
|
}
|
|
@@ -26157,6 +26158,7 @@ var AcpPromptRoutingRegistry = class {
|
|
|
26157
26158
|
function createAcpManagerContext(options) {
|
|
26158
26159
|
return {
|
|
26159
26160
|
log: options.log,
|
|
26161
|
+
getBridgeAccessPort: options.getBridgeAccessPort,
|
|
26160
26162
|
reportAgentCapabilities: options.reportAgentCapabilities,
|
|
26161
26163
|
backendFallbackAgentType: null,
|
|
26162
26164
|
acpAgents: /* @__PURE__ */ new Map(),
|
|
@@ -26170,9 +26172,9 @@ function createAcpManagerContext(options) {
|
|
|
26170
26172
|
async function cancelRun(ctx, runId) {
|
|
26171
26173
|
const meta = ctx.runDispatch.get(runId);
|
|
26172
26174
|
if (!meta) return false;
|
|
26173
|
-
const streamingRunId = ctx.promptRouting.getStreamingRunId(meta.
|
|
26175
|
+
const streamingRunId = ctx.promptRouting.getStreamingRunId(meta.acpSessionAgentKey);
|
|
26174
26176
|
if (streamingRunId && streamingRunId !== runId) return false;
|
|
26175
|
-
const handle = ctx.acpAgents.get(meta.
|
|
26177
|
+
const handle = ctx.acpAgents.get(meta.acpSessionAgentKey)?.acpHandle;
|
|
26176
26178
|
if (handle?.cancel) {
|
|
26177
26179
|
ctx.log("[Agent] Stop requested");
|
|
26178
26180
|
try {
|
|
@@ -26339,7 +26341,7 @@ function createCursorAcpSessionContext(options) {
|
|
|
26339
26341
|
return {
|
|
26340
26342
|
cwd: options.cwd,
|
|
26341
26343
|
onFileChange: options.onFileChange,
|
|
26342
|
-
mcpServers: [],
|
|
26344
|
+
mcpServers: options.mcpServers ?? [],
|
|
26343
26345
|
persistedAcpSessionId: options.persistedAcpSessionId,
|
|
26344
26346
|
agentLabel: "Cursor",
|
|
26345
26347
|
suppressLoadReplay: suppressLoadReplayRef,
|
|
@@ -26765,6 +26767,7 @@ async function createCursorAcpClient(options) {
|
|
|
26765
26767
|
});
|
|
26766
26768
|
const sessionCtx = createCursorAcpSessionContext({
|
|
26767
26769
|
cwd,
|
|
26770
|
+
mcpServers: options.mcpServers,
|
|
26768
26771
|
persistedAcpSessionId,
|
|
26769
26772
|
backendAgentType,
|
|
26770
26773
|
agentConfig: options.agentConfig,
|
|
@@ -27710,6 +27713,11 @@ function writeLocalAgentSessionFile(cloudSessionId, patch) {
|
|
|
27710
27713
|
}
|
|
27711
27714
|
|
|
27712
27715
|
// src/agents/acp/acp-agent.ts
|
|
27716
|
+
function computeAcpSessionAgentKey(cloudSessionId, acpAgentKey) {
|
|
27717
|
+
const sid = cloudSessionId?.trim();
|
|
27718
|
+
if (!sid) return `::${acpAgentKey}`;
|
|
27719
|
+
return `${sid}::${acpAgentKey}`;
|
|
27720
|
+
}
|
|
27713
27721
|
function computeAcpAgentKey(preferredAgentType, mode, agentConfig) {
|
|
27714
27722
|
const resolved = resolveAgentCommand(preferredAgentType);
|
|
27715
27723
|
if (!resolved) return null;
|
|
@@ -27718,6 +27726,30 @@ function computeAcpAgentKey(preferredAgentType, mode, agentConfig) {
|
|
|
27718
27726
|
return `${resolved.label}::${fullCmd.join("\0")}${cacheConfig}`;
|
|
27719
27727
|
}
|
|
27720
27728
|
|
|
27729
|
+
// src/mcp/bridge-mcp-constants.ts
|
|
27730
|
+
var BRIDGE_MCP_SERVER_NAME = "buildautomaton-bridge";
|
|
27731
|
+
|
|
27732
|
+
// src/mcp/resolve-bridge-mcp-server-path.ts
|
|
27733
|
+
import { dirname as dirname4, join as join2 } from "node:path";
|
|
27734
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
27735
|
+
function resolveBridgeMcpServerScriptPath() {
|
|
27736
|
+
const cliDir = dirname4(fileURLToPath4(import.meta.url));
|
|
27737
|
+
return join2(cliDir, "bridge-mcp-server.js");
|
|
27738
|
+
}
|
|
27739
|
+
|
|
27740
|
+
// src/mcp/build-acp-mcp-servers.ts
|
|
27741
|
+
function buildAcpMcpServers(bridgeAccessPort) {
|
|
27742
|
+
if (!Number.isFinite(bridgeAccessPort) || bridgeAccessPort <= 0) return [];
|
|
27743
|
+
return [
|
|
27744
|
+
{
|
|
27745
|
+
name: BRIDGE_MCP_SERVER_NAME,
|
|
27746
|
+
command: process.execPath,
|
|
27747
|
+
args: [resolveBridgeMcpServerScriptPath(), String(bridgeAccessPort)],
|
|
27748
|
+
env: []
|
|
27749
|
+
}
|
|
27750
|
+
];
|
|
27751
|
+
}
|
|
27752
|
+
|
|
27721
27753
|
// src/agents/acp/ensure-acp-client.ts
|
|
27722
27754
|
function invalidateAcpClientState(state) {
|
|
27723
27755
|
state.clientEpoch += 1;
|
|
@@ -27749,11 +27781,13 @@ async function ensureAcpClient(options) {
|
|
|
27749
27781
|
sessionParentPath,
|
|
27750
27782
|
resolveRouting,
|
|
27751
27783
|
cloudSessionId,
|
|
27784
|
+
bridgeAccessPort,
|
|
27752
27785
|
reportAgentCapabilities,
|
|
27753
27786
|
sendSessionUpdate,
|
|
27754
27787
|
sendRequest,
|
|
27755
27788
|
log: log2
|
|
27756
27789
|
} = options;
|
|
27790
|
+
const mcpServers = buildAcpMcpServers(bridgeAccessPort ?? 0);
|
|
27757
27791
|
state.latestAgentConfigForBridgeHooks = agentConfig != null && typeof agentConfig === "object" && !Array.isArray(agentConfig) ? agentConfig : null;
|
|
27758
27792
|
const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
|
|
27759
27793
|
if (state.acpStartPromise && !state.acpHandle) {
|
|
@@ -27869,7 +27903,8 @@ async function ensureAcpClient(options) {
|
|
|
27869
27903
|
state.lastAcpStartError = "Agent subprocess exited";
|
|
27870
27904
|
},
|
|
27871
27905
|
...hooks,
|
|
27872
|
-
cwd: targetSessionParentPath
|
|
27906
|
+
cwd: targetSessionParentPath,
|
|
27907
|
+
mcpServers
|
|
27873
27908
|
}).then(async (h) => {
|
|
27874
27909
|
if (spawnEpoch !== state.clientEpoch || isCliImmediateShutdownRequested()) {
|
|
27875
27910
|
try {
|
|
@@ -32850,7 +32885,7 @@ async function putEncryptedChangeSummaryRows(params) {
|
|
|
32850
32885
|
return { path: path44, summary: JSON.stringify(enc) };
|
|
32851
32886
|
});
|
|
32852
32887
|
const res = await fetch(
|
|
32853
|
-
`${base}/
|
|
32888
|
+
`${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
32854
32889
|
{
|
|
32855
32890
|
method: "PUT",
|
|
32856
32891
|
headers: {
|
|
@@ -33015,8 +33050,8 @@ async function fetchSessionAttachmentPayloadsForAgent(params) {
|
|
|
33015
33050
|
for (const a of attachments) {
|
|
33016
33051
|
const id = typeof a.attachmentId === "string" ? a.attachmentId.trim() : "";
|
|
33017
33052
|
if (!id) continue;
|
|
33018
|
-
const metaUrl = `${base}/
|
|
33019
|
-
const blobUrl = `${base}/
|
|
33053
|
+
const metaUrl = `${base}/internal/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/meta`;
|
|
33054
|
+
const blobUrl = `${base}/internal/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/blob`;
|
|
33020
33055
|
const metaRes = await fetch(metaUrl, { headers: { Authorization: `Bearer ${token}` } });
|
|
33021
33056
|
if (!metaRes.ok) {
|
|
33022
33057
|
const t = await metaRes.text().catch(() => "");
|
|
@@ -33064,6 +33099,106 @@ async function fetchSessionAttachmentPayloadsForAgent(params) {
|
|
|
33064
33099
|
return { ok: true, images: out };
|
|
33065
33100
|
}
|
|
33066
33101
|
|
|
33102
|
+
// src/agents/acp/build-forked-session-agent-prompt.ts
|
|
33103
|
+
function buildForkedSessionAgentPrompt(userPrompt, childSessionId, parentSessionId) {
|
|
33104
|
+
const childRef = childSessionId.trim();
|
|
33105
|
+
const parentRef = parentSessionId.trim();
|
|
33106
|
+
return [
|
|
33107
|
+
"You are working in a new session that was forked from a prior session.",
|
|
33108
|
+
`This forked session id: ${childRef}`,
|
|
33109
|
+
`Parent session id: ${parentRef}`,
|
|
33110
|
+
"",
|
|
33111
|
+
"The parent session history is available through MCP tools on the buildautomaton-bridge server:",
|
|
33112
|
+
"- list_parent_session_prompts \u2014 list each user prompt / turn in the parent session",
|
|
33113
|
+
"- get_parent_session_turn_transcript \u2014 full transcript for one turn (including tool calls); requires turnId",
|
|
33114
|
+
"",
|
|
33115
|
+
"When calling these tools, always pass this forked session id as sessionId (not the parent session id).",
|
|
33116
|
+
"",
|
|
33117
|
+
"Use these tools when you need prior context instead of guessing.",
|
|
33118
|
+
"",
|
|
33119
|
+
"User request:",
|
|
33120
|
+
userPrompt.trim()
|
|
33121
|
+
].join("\n");
|
|
33122
|
+
}
|
|
33123
|
+
|
|
33124
|
+
// src/mcp/tools/session-history/fetch-parent-session-cloud.ts
|
|
33125
|
+
function internalApiBase(cloudApiBaseUrl) {
|
|
33126
|
+
return cloudApiBaseUrl.replace(/\/+$/, "");
|
|
33127
|
+
}
|
|
33128
|
+
async function fetchCloudSessionMeta(params) {
|
|
33129
|
+
const token = params.getCloudAccessToken();
|
|
33130
|
+
if (!token) return { ok: false, error: "Missing cloud access token." };
|
|
33131
|
+
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.sessionId)}/meta`;
|
|
33132
|
+
const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
|
|
33133
|
+
if (!res.ok) {
|
|
33134
|
+
const t = await res.text().catch(() => "");
|
|
33135
|
+
return { ok: false, error: `Session meta fetch failed (${res.status}): ${t.slice(0, 200)}` };
|
|
33136
|
+
}
|
|
33137
|
+
const data = await res.json().catch(() => null);
|
|
33138
|
+
if (!data) return { ok: false, error: "Invalid session meta response." };
|
|
33139
|
+
return { ok: true, data };
|
|
33140
|
+
}
|
|
33141
|
+
async function fetchCloudParentSessionPrompts(params) {
|
|
33142
|
+
const token = params.getCloudAccessToken();
|
|
33143
|
+
if (!token) return { ok: false, error: "Missing cloud access token." };
|
|
33144
|
+
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/prompts`;
|
|
33145
|
+
const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
|
|
33146
|
+
if (!res.ok) {
|
|
33147
|
+
const t = await res.text().catch(() => "");
|
|
33148
|
+
return { ok: false, error: `Parent prompts fetch failed (${res.status}): ${t.slice(0, 200)}` };
|
|
33149
|
+
}
|
|
33150
|
+
const data = await res.json().catch(() => null);
|
|
33151
|
+
if (!data) return { ok: false, error: "Invalid parent prompts response." };
|
|
33152
|
+
return { ok: true, data };
|
|
33153
|
+
}
|
|
33154
|
+
async function fetchCloudParentTurnTranscript(params) {
|
|
33155
|
+
const token = params.getCloudAccessToken();
|
|
33156
|
+
if (!token) return { ok: false, error: "Missing cloud access token." };
|
|
33157
|
+
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/turns/${encodeURIComponent(params.turnId)}/transcript`;
|
|
33158
|
+
const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
|
|
33159
|
+
if (!res.ok) {
|
|
33160
|
+
const t = await res.text().catch(() => "");
|
|
33161
|
+
return { ok: false, error: `Transcript fetch failed (${res.status}): ${t.slice(0, 200)}` };
|
|
33162
|
+
}
|
|
33163
|
+
const data = await res.json().catch(() => null);
|
|
33164
|
+
const rows = Array.isArray(data?.transcript) ? data.transcript : [];
|
|
33165
|
+
const lines = rows.map((row) => {
|
|
33166
|
+
const kind = typeof row.kind === "string" ? row.kind : "event";
|
|
33167
|
+
const payload = typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload ?? "");
|
|
33168
|
+
return `[${kind}] ${payload}`;
|
|
33169
|
+
});
|
|
33170
|
+
return { ok: true, text: lines.join("\n") };
|
|
33171
|
+
}
|
|
33172
|
+
async function resolveParentSessionIdForChild(params) {
|
|
33173
|
+
const meta = await fetchCloudSessionMeta({
|
|
33174
|
+
sessionId: params.childSessionId,
|
|
33175
|
+
cloudApiBaseUrl: params.cloudApiBaseUrl,
|
|
33176
|
+
getCloudAccessToken: params.getCloudAccessToken
|
|
33177
|
+
});
|
|
33178
|
+
if (!meta.ok) return meta;
|
|
33179
|
+
const parent = meta.data.parentSessionId?.trim() ?? "";
|
|
33180
|
+
if (!parent) {
|
|
33181
|
+
return {
|
|
33182
|
+
ok: false,
|
|
33183
|
+
error: `Session ${params.childSessionId} has no parent session history. It was not forked from another session (no parent_session_id). Pass the forked child session id as sessionId, not the parent session id.`
|
|
33184
|
+
};
|
|
33185
|
+
}
|
|
33186
|
+
return { ok: true, parentSessionId: parent };
|
|
33187
|
+
}
|
|
33188
|
+
|
|
33189
|
+
// src/agents/acp/enrich-forked-session-prompt-for-agent.ts
|
|
33190
|
+
async function enrichForkedSessionPromptForAgent(params) {
|
|
33191
|
+
const meta = await fetchCloudSessionMeta({
|
|
33192
|
+
sessionId: params.sessionId,
|
|
33193
|
+
cloudApiBaseUrl: params.cloudApiBaseUrl,
|
|
33194
|
+
getCloudAccessToken: params.getCloudAccessToken
|
|
33195
|
+
});
|
|
33196
|
+
if (!meta.ok) return params.promptText;
|
|
33197
|
+
const parentSessionId = meta.data.parentSessionId?.trim() ?? "";
|
|
33198
|
+
if (!parentSessionId) return params.promptText;
|
|
33199
|
+
return buildForkedSessionAgentPrompt(params.promptText, params.sessionId, parentSessionId);
|
|
33200
|
+
}
|
|
33201
|
+
|
|
33067
33202
|
// src/agents/acp/send-prompt-to-agent.ts
|
|
33068
33203
|
async function sendPromptToAgent(options) {
|
|
33069
33204
|
const {
|
|
@@ -33085,6 +33220,15 @@ async function sendPromptToAgent(options) {
|
|
|
33085
33220
|
attachments
|
|
33086
33221
|
} = options;
|
|
33087
33222
|
try {
|
|
33223
|
+
let agentPromptText = promptText;
|
|
33224
|
+
if (sessionId && cloudApiBaseUrl && getCloudAccessToken) {
|
|
33225
|
+
agentPromptText = await enrichForkedSessionPromptForAgent({
|
|
33226
|
+
sessionId,
|
|
33227
|
+
promptText,
|
|
33228
|
+
cloudApiBaseUrl,
|
|
33229
|
+
getCloudAccessToken
|
|
33230
|
+
});
|
|
33231
|
+
}
|
|
33088
33232
|
let sendOpts = {};
|
|
33089
33233
|
if (attachments && attachments.length > 0) {
|
|
33090
33234
|
if (!sessionId || !cloudApiBaseUrl || !getCloudAccessToken) {
|
|
@@ -33123,7 +33267,7 @@ async function sendPromptToAgent(options) {
|
|
|
33123
33267
|
}
|
|
33124
33268
|
sendOpts = { images: resolved.images };
|
|
33125
33269
|
}
|
|
33126
|
-
const result = await handle.sendPrompt(
|
|
33270
|
+
const result = await handle.sendPrompt(agentPromptText, sendOpts);
|
|
33127
33271
|
if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
|
|
33128
33272
|
await collectTurnGitDiffFromPreTurnSnapshot({
|
|
33129
33273
|
sessionId,
|
|
@@ -33184,11 +33328,11 @@ async function sendPromptToAgent(options) {
|
|
|
33184
33328
|
}
|
|
33185
33329
|
|
|
33186
33330
|
// src/agents/acp/manager/get-acp-agent-state.ts
|
|
33187
|
-
function
|
|
33188
|
-
let state = ctx.acpAgents.get(
|
|
33331
|
+
function getAcpSessionAgentState(ctx, acpSessionAgentKey) {
|
|
33332
|
+
let state = ctx.acpAgents.get(acpSessionAgentKey);
|
|
33189
33333
|
if (!state) {
|
|
33190
33334
|
state = createEmptyAcpClientState();
|
|
33191
|
-
ctx.acpAgents.set(
|
|
33335
|
+
ctx.acpAgents.set(acpSessionAgentKey, state);
|
|
33192
33336
|
}
|
|
33193
33337
|
return state;
|
|
33194
33338
|
}
|
|
@@ -33232,11 +33376,12 @@ function handlePrompt(ctx, opts) {
|
|
|
33232
33376
|
return;
|
|
33233
33377
|
}
|
|
33234
33378
|
const activeAcpAgentKey = acpAgentKey;
|
|
33379
|
+
const activeAcpSessionAgentKey = computeAcpSessionAgentKey(sessionId, activeAcpAgentKey);
|
|
33235
33380
|
ctx.pendingCancelRunIds.delete(activeRunId);
|
|
33236
33381
|
ctx.promptRouting.registerRun({ sessionId, runId: activeRunId });
|
|
33237
|
-
ctx.runDispatch.set(activeRunId, {
|
|
33382
|
+
ctx.runDispatch.set(activeRunId, { acpSessionAgentKey: activeAcpSessionAgentKey });
|
|
33238
33383
|
async function run() {
|
|
33239
|
-
const acpAgentState =
|
|
33384
|
+
const acpAgentState = getAcpSessionAgentState(ctx, activeAcpSessionAgentKey);
|
|
33240
33385
|
const handle = await ensureAcpClient({
|
|
33241
33386
|
state: acpAgentState,
|
|
33242
33387
|
acpAgentKey: activeAcpAgentKey,
|
|
@@ -33244,8 +33389,9 @@ function handlePrompt(ctx, opts) {
|
|
|
33244
33389
|
mode,
|
|
33245
33390
|
agentConfig: agentConfig ?? null,
|
|
33246
33391
|
sessionParentPath,
|
|
33247
|
-
resolveRouting: () => ctx.promptRouting.resolveRouting(
|
|
33392
|
+
resolveRouting: () => ctx.promptRouting.resolveRouting(activeAcpSessionAgentKey),
|
|
33248
33393
|
cloudSessionId: sessionId,
|
|
33394
|
+
bridgeAccessPort: ctx.getBridgeAccessPort(),
|
|
33249
33395
|
sendSessionUpdate,
|
|
33250
33396
|
sendRequest: sendSessionUpdate,
|
|
33251
33397
|
log: ctx.log,
|
|
@@ -33287,7 +33433,7 @@ function handlePrompt(ctx, opts) {
|
|
|
33287
33433
|
});
|
|
33288
33434
|
return;
|
|
33289
33435
|
}
|
|
33290
|
-
ctx.promptRouting.setStreamingRunId(
|
|
33436
|
+
ctx.promptRouting.setStreamingRunId(activeAcpSessionAgentKey, activeRunId);
|
|
33291
33437
|
try {
|
|
33292
33438
|
await sendPromptToAgent({
|
|
33293
33439
|
handle,
|
|
@@ -33308,7 +33454,7 @@ function handlePrompt(ctx, opts) {
|
|
|
33308
33454
|
attachments
|
|
33309
33455
|
});
|
|
33310
33456
|
} finally {
|
|
33311
|
-
ctx.promptRouting.clearStreamingRunId(
|
|
33457
|
+
ctx.promptRouting.clearStreamingRunId(activeAcpSessionAgentKey, activeRunId);
|
|
33312
33458
|
}
|
|
33313
33459
|
}
|
|
33314
33460
|
void run().finally(() => {
|
|
@@ -33332,6 +33478,7 @@ function logPromptReceivedFromBridge(ctx, opts) {
|
|
|
33332
33478
|
async function createAcpManager(options) {
|
|
33333
33479
|
const ctx = createAcpManagerContext({
|
|
33334
33480
|
log: options.log,
|
|
33481
|
+
getBridgeAccessPort: options.getBridgeAccessPort,
|
|
33335
33482
|
reportAgentCapabilities: options.reportAgentCapabilities
|
|
33336
33483
|
});
|
|
33337
33484
|
return {
|
|
@@ -38721,6 +38868,232 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
38721
38868
|
})();
|
|
38722
38869
|
}
|
|
38723
38870
|
|
|
38871
|
+
// src/mcp/tools/session-history/fork-session-mcp-tools.ts
|
|
38872
|
+
var LIST_PARENT_SESSION_PROMPTS_TOOL = "list_parent_session_prompts";
|
|
38873
|
+
var GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL = "get_parent_session_turn_transcript";
|
|
38874
|
+
var SESSION_ID_SCHEMA = {
|
|
38875
|
+
type: "string",
|
|
38876
|
+
description: "Id of this forked (child) session \u2014 not the parent session id. Required on every tool call."
|
|
38877
|
+
};
|
|
38878
|
+
var FORK_SESSION_MCP_TOOL_DEFINITIONS = [
|
|
38879
|
+
{
|
|
38880
|
+
name: LIST_PARENT_SESSION_PROMPTS_TOOL,
|
|
38881
|
+
description: "List user prompts (main turns) from the parent session a fork was created from. Requires sessionId. Returns an error if the session has no parent history (not a fork).",
|
|
38882
|
+
inputSchema: {
|
|
38883
|
+
type: "object",
|
|
38884
|
+
properties: { sessionId: SESSION_ID_SCHEMA },
|
|
38885
|
+
required: ["sessionId"],
|
|
38886
|
+
additionalProperties: false
|
|
38887
|
+
}
|
|
38888
|
+
},
|
|
38889
|
+
{
|
|
38890
|
+
name: GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL,
|
|
38891
|
+
description: "Full transcript for one parent-session turn, including tool calls and agent messages. Requires sessionId and turnId. Returns an error if the session has no parent history (not a fork).",
|
|
38892
|
+
inputSchema: {
|
|
38893
|
+
type: "object",
|
|
38894
|
+
properties: {
|
|
38895
|
+
sessionId: SESSION_ID_SCHEMA,
|
|
38896
|
+
turnId: { type: "string", description: "Turn id from list_parent_session_prompts" }
|
|
38897
|
+
},
|
|
38898
|
+
required: ["sessionId", "turnId"],
|
|
38899
|
+
additionalProperties: false
|
|
38900
|
+
}
|
|
38901
|
+
}
|
|
38902
|
+
];
|
|
38903
|
+
function textResult(text, isError = false) {
|
|
38904
|
+
return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
|
|
38905
|
+
}
|
|
38906
|
+
function isForkSessionMcpToolName(name) {
|
|
38907
|
+
return name === LIST_PARENT_SESSION_PROMPTS_TOOL || name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL;
|
|
38908
|
+
}
|
|
38909
|
+
async function callForkSessionMcpTool(ctx, cloud, name, args) {
|
|
38910
|
+
const childSessionId = ctx.sessionId.trim();
|
|
38911
|
+
if (!childSessionId) {
|
|
38912
|
+
return textResult("sessionId is required.", true);
|
|
38913
|
+
}
|
|
38914
|
+
const parentResolved = await resolveParentSessionIdForChild({
|
|
38915
|
+
childSessionId,
|
|
38916
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
38917
|
+
getCloudAccessToken: cloud.getCloudAccessToken
|
|
38918
|
+
});
|
|
38919
|
+
if (!parentResolved.ok) {
|
|
38920
|
+
return textResult(parentResolved.error, true);
|
|
38921
|
+
}
|
|
38922
|
+
const parentSessionId = parentResolved.parentSessionId;
|
|
38923
|
+
if (name === LIST_PARENT_SESSION_PROMPTS_TOOL) {
|
|
38924
|
+
const detail = await fetchCloudParentSessionPrompts({
|
|
38925
|
+
childSessionId,
|
|
38926
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
38927
|
+
getCloudAccessToken: cloud.getCloudAccessToken
|
|
38928
|
+
});
|
|
38929
|
+
if (!detail.ok) return textResult(detail.error, true);
|
|
38930
|
+
const turns = Array.isArray(detail.data.turns) ? detail.data.turns : [];
|
|
38931
|
+
const payload = turns.map((t) => ({
|
|
38932
|
+
turnId: typeof t.turnId === "string" ? t.turnId : "",
|
|
38933
|
+
title: typeof t.title === "string" ? t.title : null,
|
|
38934
|
+
status: typeof t.status === "string" ? t.status : null,
|
|
38935
|
+
promptText: typeof t.promptText === "string" ? t.promptText : null
|
|
38936
|
+
}));
|
|
38937
|
+
return textResult(JSON.stringify({ parentSessionId, turns: payload }, null, 2));
|
|
38938
|
+
}
|
|
38939
|
+
if (name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL) {
|
|
38940
|
+
const turnId = typeof args.turnId === "string" ? args.turnId.trim() : "";
|
|
38941
|
+
if (!turnId) return textResult("turnId is required.", true);
|
|
38942
|
+
const transcript = await fetchCloudParentTurnTranscript({
|
|
38943
|
+
childSessionId,
|
|
38944
|
+
turnId,
|
|
38945
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
38946
|
+
getCloudAccessToken: cloud.getCloudAccessToken,
|
|
38947
|
+
e2ee: ctx.e2ee
|
|
38948
|
+
});
|
|
38949
|
+
if (!transcript.ok) return textResult(transcript.error, true);
|
|
38950
|
+
return textResult(transcript.text);
|
|
38951
|
+
}
|
|
38952
|
+
return textResult(`Unknown fork session tool: ${name}`, true);
|
|
38953
|
+
}
|
|
38954
|
+
|
|
38955
|
+
// src/mcp/bridge-mcp-tools.ts
|
|
38956
|
+
function requireCloud(shared) {
|
|
38957
|
+
const cloudApiBaseUrl = shared.cloudApiBaseUrl?.trim() ?? "";
|
|
38958
|
+
const getCloudAccessToken = shared.getCloudAccessToken;
|
|
38959
|
+
if (!cloudApiBaseUrl || !getCloudAccessToken) return null;
|
|
38960
|
+
return { cloudApiBaseUrl, getCloudAccessToken };
|
|
38961
|
+
}
|
|
38962
|
+
function createBridgeMcpToolRegistry(shared) {
|
|
38963
|
+
const cloud = requireCloud(shared);
|
|
38964
|
+
return {
|
|
38965
|
+
listTools: async () => [...FORK_SESSION_MCP_TOOL_DEFINITIONS],
|
|
38966
|
+
callTool: async (name, args) => {
|
|
38967
|
+
if (isForkSessionMcpToolName(name)) {
|
|
38968
|
+
if (!cloud) {
|
|
38969
|
+
return {
|
|
38970
|
+
content: [{ type: "text", text: "Bridge MCP tools require cloud API credentials." }],
|
|
38971
|
+
isError: true
|
|
38972
|
+
};
|
|
38973
|
+
}
|
|
38974
|
+
const sessionId = typeof args.sessionId === "string" ? args.sessionId.trim() : "";
|
|
38975
|
+
if (!sessionId) {
|
|
38976
|
+
return {
|
|
38977
|
+
content: [{ type: "text", text: "sessionId is required." }],
|
|
38978
|
+
isError: true
|
|
38979
|
+
};
|
|
38980
|
+
}
|
|
38981
|
+
return callForkSessionMcpTool({ ...shared, sessionId }, cloud, name, args);
|
|
38982
|
+
}
|
|
38983
|
+
throw new Error(`Unknown bridge MCP tool: ${name}`);
|
|
38984
|
+
}
|
|
38985
|
+
};
|
|
38986
|
+
}
|
|
38987
|
+
|
|
38988
|
+
// src/mcp/bridge-access/start-server.ts
|
|
38989
|
+
import * as http from "node:http";
|
|
38990
|
+
|
|
38991
|
+
// src/mcp/bridge-access/read-json-body.ts
|
|
38992
|
+
function readJsonBody(req) {
|
|
38993
|
+
return new Promise((resolve20, reject) => {
|
|
38994
|
+
const chunks = [];
|
|
38995
|
+
req.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
38996
|
+
req.on("end", () => {
|
|
38997
|
+
try {
|
|
38998
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
38999
|
+
if (!raw) {
|
|
39000
|
+
resolve20({});
|
|
39001
|
+
return;
|
|
39002
|
+
}
|
|
39003
|
+
const parsed = JSON.parse(raw);
|
|
39004
|
+
resolve20(parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {});
|
|
39005
|
+
} catch (e) {
|
|
39006
|
+
reject(e);
|
|
39007
|
+
}
|
|
39008
|
+
});
|
|
39009
|
+
req.on("error", reject);
|
|
39010
|
+
});
|
|
39011
|
+
}
|
|
39012
|
+
|
|
39013
|
+
// src/mcp/bridge-access/send-json-response.ts
|
|
39014
|
+
function sendJsonResponse(res, status, body) {
|
|
39015
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
39016
|
+
res.end(JSON.stringify(body));
|
|
39017
|
+
}
|
|
39018
|
+
|
|
39019
|
+
// src/mcp/bridge-access/handle-request.ts
|
|
39020
|
+
function createBridgeAccessRequestHandler(registry2) {
|
|
39021
|
+
return (req, res) => {
|
|
39022
|
+
void (async () => {
|
|
39023
|
+
if (req.method !== "POST") {
|
|
39024
|
+
sendJsonResponse(res, 405, { ok: false, error: "Method not allowed" });
|
|
39025
|
+
return;
|
|
39026
|
+
}
|
|
39027
|
+
try {
|
|
39028
|
+
const body = await readJsonBody(req);
|
|
39029
|
+
if (req.url === "/tools/list") {
|
|
39030
|
+
const tools = await Promise.resolve(registry2.listTools());
|
|
39031
|
+
sendJsonResponse(res, 200, { ok: true, data: tools });
|
|
39032
|
+
return;
|
|
39033
|
+
}
|
|
39034
|
+
if (req.url === "/tools/call") {
|
|
39035
|
+
const sessionId = typeof body.sessionId === "string" ? body.sessionId.trim() : "";
|
|
39036
|
+
if (!sessionId) {
|
|
39037
|
+
sendJsonResponse(res, 400, { ok: false, error: "sessionId is required" });
|
|
39038
|
+
return;
|
|
39039
|
+
}
|
|
39040
|
+
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
39041
|
+
const args = body.args && typeof body.args === "object" && !Array.isArray(body.args) ? body.args : {};
|
|
39042
|
+
if (!name) {
|
|
39043
|
+
sendJsonResponse(res, 400, { ok: false, error: "name is required" });
|
|
39044
|
+
return;
|
|
39045
|
+
}
|
|
39046
|
+
const data = await registry2.callTool(name, { ...args, sessionId });
|
|
39047
|
+
sendJsonResponse(res, 200, { ok: true, data });
|
|
39048
|
+
return;
|
|
39049
|
+
}
|
|
39050
|
+
sendJsonResponse(res, 404, { ok: false, error: "Not found" });
|
|
39051
|
+
} catch (err) {
|
|
39052
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
39053
|
+
sendJsonResponse(res, 500, { ok: false, error: message });
|
|
39054
|
+
}
|
|
39055
|
+
})();
|
|
39056
|
+
};
|
|
39057
|
+
}
|
|
39058
|
+
|
|
39059
|
+
// src/mcp/bridge-access/start-server.ts
|
|
39060
|
+
function startBridgeAccessServer(registry2) {
|
|
39061
|
+
const server = http.createServer(createBridgeAccessRequestHandler(registry2));
|
|
39062
|
+
return new Promise((resolve20, reject) => {
|
|
39063
|
+
server.once("error", reject);
|
|
39064
|
+
server.listen(0, "127.0.0.1", () => {
|
|
39065
|
+
const addr = server.address();
|
|
39066
|
+
if (!addr || typeof addr === "string") {
|
|
39067
|
+
reject(new Error("Bridge access server did not bind"));
|
|
39068
|
+
return;
|
|
39069
|
+
}
|
|
39070
|
+
resolve20({
|
|
39071
|
+
port: addr.port,
|
|
39072
|
+
close: () => new Promise((closeResolve, closeReject) => {
|
|
39073
|
+
server.close((err) => err ? closeReject(err) : closeResolve());
|
|
39074
|
+
})
|
|
39075
|
+
});
|
|
39076
|
+
});
|
|
39077
|
+
});
|
|
39078
|
+
}
|
|
39079
|
+
|
|
39080
|
+
// src/mcp/bridge-access/create-state.ts
|
|
39081
|
+
async function createBridgeAccessState(options = {}) {
|
|
39082
|
+
const registry2 = createBridgeMcpToolRegistry({
|
|
39083
|
+
cloudApiBaseUrl: options.cloudApiBaseUrl,
|
|
39084
|
+
getCloudAccessToken: options.getCloudAccessToken,
|
|
39085
|
+
e2ee: options.e2ee
|
|
39086
|
+
});
|
|
39087
|
+
const server = await startBridgeAccessServer(registry2);
|
|
39088
|
+
return {
|
|
39089
|
+
getPort: () => server.port,
|
|
39090
|
+
getRegistry: () => registry2,
|
|
39091
|
+
close: async () => {
|
|
39092
|
+
await server.close();
|
|
39093
|
+
}
|
|
39094
|
+
};
|
|
39095
|
+
}
|
|
39096
|
+
|
|
38724
39097
|
// src/connection/create-bridge-connection.ts
|
|
38725
39098
|
async function createBridgeConnection(options) {
|
|
38726
39099
|
const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
|
|
@@ -38783,12 +39156,18 @@ async function createBridgeConnection(options) {
|
|
|
38783
39156
|
worktreesRootPath,
|
|
38784
39157
|
log: logFn
|
|
38785
39158
|
});
|
|
39159
|
+
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
39160
|
+
const bridgeAccess = await createBridgeAccessState({
|
|
39161
|
+
cloudApiBaseUrl: apiUrl,
|
|
39162
|
+
getCloudAccessToken: () => tokens.accessToken,
|
|
39163
|
+
e2ee
|
|
39164
|
+
});
|
|
38786
39165
|
const acpManager = await createAcpManager({
|
|
38787
39166
|
log: logFn,
|
|
39167
|
+
getBridgeAccessPort: () => bridgeAccess.getPort(),
|
|
38788
39168
|
reportAgentCapabilities: sendAgentCapabilitiesToBridge
|
|
38789
39169
|
});
|
|
38790
39170
|
logFn("CLI running. Press Ctrl+C to exit.");
|
|
38791
|
-
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
38792
39171
|
const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
|
|
38793
39172
|
const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
|
|
38794
39173
|
const baseOnBridgeIdentified = createOnBridgeIdentified({
|
|
@@ -38854,6 +39233,7 @@ async function createBridgeConnection(options) {
|
|
|
38854
39233
|
stopFileIndexWatcher();
|
|
38855
39234
|
bridgeHeartbeat.stop();
|
|
38856
39235
|
await closeBridgeConnection(state, acpManager, devServerManager, logFn);
|
|
39236
|
+
await bridgeAccess.close();
|
|
38857
39237
|
}
|
|
38858
39238
|
};
|
|
38859
39239
|
}
|