@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/cli.js
CHANGED
|
@@ -5233,7 +5233,7 @@ var require_websocket = __commonJS({
|
|
|
5233
5233
|
"use strict";
|
|
5234
5234
|
var EventEmitter2 = __require("events");
|
|
5235
5235
|
var https2 = __require("https");
|
|
5236
|
-
var
|
|
5236
|
+
var http2 = __require("http");
|
|
5237
5237
|
var net = __require("net");
|
|
5238
5238
|
var tls = __require("tls");
|
|
5239
5239
|
var { randomBytes: randomBytes3, createHash: createHash3 } = __require("crypto");
|
|
@@ -5767,7 +5767,7 @@ var require_websocket = __commonJS({
|
|
|
5767
5767
|
}
|
|
5768
5768
|
const defaultPort = isSecure ? 443 : 80;
|
|
5769
5769
|
const key = randomBytes3(16).toString("base64");
|
|
5770
|
-
const request = isSecure ? https2.request :
|
|
5770
|
+
const request = isSecure ? https2.request : http2.request;
|
|
5771
5771
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
5772
5772
|
let perMessageDeflate;
|
|
5773
5773
|
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
@@ -6261,7 +6261,7 @@ var require_websocket_server = __commonJS({
|
|
|
6261
6261
|
"../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js"(exports, module) {
|
|
6262
6262
|
"use strict";
|
|
6263
6263
|
var EventEmitter2 = __require("events");
|
|
6264
|
-
var
|
|
6264
|
+
var http2 = __require("http");
|
|
6265
6265
|
var { Duplex } = __require("stream");
|
|
6266
6266
|
var { createHash: createHash3 } = __require("crypto");
|
|
6267
6267
|
var extension = require_extension();
|
|
@@ -6336,8 +6336,8 @@ var require_websocket_server = __commonJS({
|
|
|
6336
6336
|
);
|
|
6337
6337
|
}
|
|
6338
6338
|
if (options.port != null) {
|
|
6339
|
-
this._server =
|
|
6340
|
-
const body =
|
|
6339
|
+
this._server = http2.createServer((req, res) => {
|
|
6340
|
+
const body = http2.STATUS_CODES[426];
|
|
6341
6341
|
res.writeHead(426, {
|
|
6342
6342
|
"Content-Length": body.length,
|
|
6343
6343
|
"Content-Type": "text/plain"
|
|
@@ -6624,7 +6624,7 @@ var require_websocket_server = __commonJS({
|
|
|
6624
6624
|
this.destroy();
|
|
6625
6625
|
}
|
|
6626
6626
|
function abortHandshake(socket, code, message, headers) {
|
|
6627
|
-
message = message ||
|
|
6627
|
+
message = message || http2.STATUS_CODES[code];
|
|
6628
6628
|
headers = {
|
|
6629
6629
|
Connection: "close",
|
|
6630
6630
|
"Content-Type": "text/html",
|
|
@@ -6633,7 +6633,7 @@ var require_websocket_server = __commonJS({
|
|
|
6633
6633
|
};
|
|
6634
6634
|
socket.once("finish", socket.destroy);
|
|
6635
6635
|
socket.end(
|
|
6636
|
-
`HTTP/1.1 ${code} ${
|
|
6636
|
+
`HTTP/1.1 ${code} ${http2.STATUS_CODES[code]}\r
|
|
6637
6637
|
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
|
|
6638
6638
|
);
|
|
6639
6639
|
}
|
|
@@ -25174,7 +25174,7 @@ var {
|
|
|
25174
25174
|
} = import_index.default;
|
|
25175
25175
|
|
|
25176
25176
|
// src/cli-version.ts
|
|
25177
|
-
var CLI_VERSION = "0.1.
|
|
25177
|
+
var CLI_VERSION = "0.1.46".length > 0 ? "0.1.46" : "0.0.0-dev";
|
|
25178
25178
|
|
|
25179
25179
|
// src/cli/defaults.ts
|
|
25180
25180
|
var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
|
|
@@ -27438,38 +27438,38 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
|
|
|
27438
27438
|
// src/agents/acp/acp-prompt-routing-registry.ts
|
|
27439
27439
|
var AcpPromptRoutingRegistry = class {
|
|
27440
27440
|
runs = /* @__PURE__ */ new Map();
|
|
27441
|
-
|
|
27441
|
+
streamingRunIdBySessionAgent = /* @__PURE__ */ new Map();
|
|
27442
27442
|
registerRun(route) {
|
|
27443
27443
|
this.runs.set(route.runId, route);
|
|
27444
27444
|
}
|
|
27445
27445
|
unregisterRun(runId) {
|
|
27446
27446
|
this.runs.delete(runId);
|
|
27447
|
-
for (const [
|
|
27447
|
+
for (const [sessionAgentKey, streamingRunId] of this.streamingRunIdBySessionAgent.entries()) {
|
|
27448
27448
|
if (streamingRunId === runId) {
|
|
27449
|
-
this.
|
|
27449
|
+
this.streamingRunIdBySessionAgent.delete(sessionAgentKey);
|
|
27450
27450
|
}
|
|
27451
27451
|
}
|
|
27452
27452
|
}
|
|
27453
27453
|
isRegisteredRun(runId) {
|
|
27454
27454
|
return this.runs.has(runId);
|
|
27455
27455
|
}
|
|
27456
|
-
setStreamingRunId(
|
|
27456
|
+
setStreamingRunId(sessionAgentKey, runId) {
|
|
27457
27457
|
if (runId) {
|
|
27458
|
-
this.
|
|
27458
|
+
this.streamingRunIdBySessionAgent.set(sessionAgentKey, runId);
|
|
27459
27459
|
} else {
|
|
27460
|
-
this.
|
|
27460
|
+
this.streamingRunIdBySessionAgent.delete(sessionAgentKey);
|
|
27461
27461
|
}
|
|
27462
27462
|
}
|
|
27463
|
-
clearStreamingRunId(
|
|
27464
|
-
if (this.
|
|
27465
|
-
this.
|
|
27463
|
+
clearStreamingRunId(sessionAgentKey, runId) {
|
|
27464
|
+
if (this.streamingRunIdBySessionAgent.get(sessionAgentKey) === runId) {
|
|
27465
|
+
this.streamingRunIdBySessionAgent.delete(sessionAgentKey);
|
|
27466
27466
|
}
|
|
27467
27467
|
}
|
|
27468
|
-
getStreamingRunId(
|
|
27469
|
-
return this.
|
|
27468
|
+
getStreamingRunId(sessionAgentKey) {
|
|
27469
|
+
return this.streamingRunIdBySessionAgent.get(sessionAgentKey);
|
|
27470
27470
|
}
|
|
27471
|
-
resolveRouting(
|
|
27472
|
-
const runId = this.
|
|
27471
|
+
resolveRouting(sessionAgentKey) {
|
|
27472
|
+
const runId = this.streamingRunIdBySessionAgent.get(sessionAgentKey);
|
|
27473
27473
|
if (!runId) return void 0;
|
|
27474
27474
|
return this.runs.get(runId);
|
|
27475
27475
|
}
|
|
@@ -27479,6 +27479,7 @@ var AcpPromptRoutingRegistry = class {
|
|
|
27479
27479
|
function createAcpManagerContext(options) {
|
|
27480
27480
|
return {
|
|
27481
27481
|
log: options.log,
|
|
27482
|
+
getBridgeAccessPort: options.getBridgeAccessPort,
|
|
27482
27483
|
reportAgentCapabilities: options.reportAgentCapabilities,
|
|
27483
27484
|
backendFallbackAgentType: null,
|
|
27484
27485
|
acpAgents: /* @__PURE__ */ new Map(),
|
|
@@ -27492,9 +27493,9 @@ function createAcpManagerContext(options) {
|
|
|
27492
27493
|
async function cancelRun(ctx, runId) {
|
|
27493
27494
|
const meta = ctx.runDispatch.get(runId);
|
|
27494
27495
|
if (!meta) return false;
|
|
27495
|
-
const streamingRunId = ctx.promptRouting.getStreamingRunId(meta.
|
|
27496
|
+
const streamingRunId = ctx.promptRouting.getStreamingRunId(meta.acpSessionAgentKey);
|
|
27496
27497
|
if (streamingRunId && streamingRunId !== runId) return false;
|
|
27497
|
-
const handle = ctx.acpAgents.get(meta.
|
|
27498
|
+
const handle = ctx.acpAgents.get(meta.acpSessionAgentKey)?.acpHandle;
|
|
27498
27499
|
if (handle?.cancel) {
|
|
27499
27500
|
ctx.log("[Agent] Stop requested");
|
|
27500
27501
|
try {
|
|
@@ -28751,7 +28752,7 @@ function createSdkStdioSessionContext(options) {
|
|
|
28751
28752
|
return {
|
|
28752
28753
|
cwd: options.cwd,
|
|
28753
28754
|
onFileChange: options.onFileChange,
|
|
28754
|
-
mcpServers: [],
|
|
28755
|
+
mcpServers: options.mcpServers ?? [],
|
|
28755
28756
|
persistedAcpSessionId: options.persistedAcpSessionId,
|
|
28756
28757
|
agentLabel: "ACP",
|
|
28757
28758
|
suppressLoadReplay: suppressLoadReplayRef,
|
|
@@ -29194,6 +29195,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
29194
29195
|
});
|
|
29195
29196
|
const sessionCtx = createSdkStdioSessionContext({
|
|
29196
29197
|
cwd,
|
|
29198
|
+
mcpServers: options.mcpServers,
|
|
29197
29199
|
persistedAcpSessionId,
|
|
29198
29200
|
backendAgentType,
|
|
29199
29201
|
agentConfig,
|
|
@@ -29325,7 +29327,7 @@ function createCursorAcpSessionContext(options) {
|
|
|
29325
29327
|
return {
|
|
29326
29328
|
cwd: options.cwd,
|
|
29327
29329
|
onFileChange: options.onFileChange,
|
|
29328
|
-
mcpServers: [],
|
|
29330
|
+
mcpServers: options.mcpServers ?? [],
|
|
29329
29331
|
persistedAcpSessionId: options.persistedAcpSessionId,
|
|
29330
29332
|
agentLabel: "Cursor",
|
|
29331
29333
|
suppressLoadReplay: suppressLoadReplayRef,
|
|
@@ -29751,6 +29753,7 @@ async function createCursorAcpClient(options) {
|
|
|
29751
29753
|
});
|
|
29752
29754
|
const sessionCtx = createCursorAcpSessionContext({
|
|
29753
29755
|
cwd,
|
|
29756
|
+
mcpServers: options.mcpServers,
|
|
29754
29757
|
persistedAcpSessionId,
|
|
29755
29758
|
backendAgentType,
|
|
29756
29759
|
agentConfig: options.agentConfig,
|
|
@@ -30696,6 +30699,11 @@ function writeLocalAgentSessionFile(cloudSessionId, patch) {
|
|
|
30696
30699
|
}
|
|
30697
30700
|
|
|
30698
30701
|
// src/agents/acp/acp-agent.ts
|
|
30702
|
+
function computeAcpSessionAgentKey(cloudSessionId, acpAgentKey) {
|
|
30703
|
+
const sid = cloudSessionId?.trim();
|
|
30704
|
+
if (!sid) return `::${acpAgentKey}`;
|
|
30705
|
+
return `${sid}::${acpAgentKey}`;
|
|
30706
|
+
}
|
|
30699
30707
|
function computeAcpAgentKey(preferredAgentType, mode, agentConfig) {
|
|
30700
30708
|
const resolved = resolveAgentCommand(preferredAgentType);
|
|
30701
30709
|
if (!resolved) return null;
|
|
@@ -30704,6 +30712,30 @@ function computeAcpAgentKey(preferredAgentType, mode, agentConfig) {
|
|
|
30704
30712
|
return `${resolved.label}::${fullCmd.join("\0")}${cacheConfig}`;
|
|
30705
30713
|
}
|
|
30706
30714
|
|
|
30715
|
+
// src/mcp/bridge-mcp-constants.ts
|
|
30716
|
+
var BRIDGE_MCP_SERVER_NAME = "buildautomaton-bridge";
|
|
30717
|
+
|
|
30718
|
+
// src/mcp/resolve-bridge-mcp-server-path.ts
|
|
30719
|
+
import { dirname as dirname4, join as join3 } from "node:path";
|
|
30720
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
30721
|
+
function resolveBridgeMcpServerScriptPath() {
|
|
30722
|
+
const cliDir = dirname4(fileURLToPath4(import.meta.url));
|
|
30723
|
+
return join3(cliDir, "bridge-mcp-server.js");
|
|
30724
|
+
}
|
|
30725
|
+
|
|
30726
|
+
// src/mcp/build-acp-mcp-servers.ts
|
|
30727
|
+
function buildAcpMcpServers(bridgeAccessPort) {
|
|
30728
|
+
if (!Number.isFinite(bridgeAccessPort) || bridgeAccessPort <= 0) return [];
|
|
30729
|
+
return [
|
|
30730
|
+
{
|
|
30731
|
+
name: BRIDGE_MCP_SERVER_NAME,
|
|
30732
|
+
command: process.execPath,
|
|
30733
|
+
args: [resolveBridgeMcpServerScriptPath(), String(bridgeAccessPort)],
|
|
30734
|
+
env: []
|
|
30735
|
+
}
|
|
30736
|
+
];
|
|
30737
|
+
}
|
|
30738
|
+
|
|
30707
30739
|
// src/agents/acp/ensure-acp-client.ts
|
|
30708
30740
|
function invalidateAcpClientState(state) {
|
|
30709
30741
|
state.clientEpoch += 1;
|
|
@@ -30735,11 +30767,13 @@ async function ensureAcpClient(options) {
|
|
|
30735
30767
|
sessionParentPath,
|
|
30736
30768
|
resolveRouting,
|
|
30737
30769
|
cloudSessionId,
|
|
30770
|
+
bridgeAccessPort,
|
|
30738
30771
|
reportAgentCapabilities,
|
|
30739
30772
|
sendSessionUpdate,
|
|
30740
30773
|
sendRequest,
|
|
30741
30774
|
log: log2
|
|
30742
30775
|
} = options;
|
|
30776
|
+
const mcpServers = buildAcpMcpServers(bridgeAccessPort ?? 0);
|
|
30743
30777
|
state.latestAgentConfigForBridgeHooks = agentConfig != null && typeof agentConfig === "object" && !Array.isArray(agentConfig) ? agentConfig : null;
|
|
30744
30778
|
const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
|
|
30745
30779
|
if (state.acpStartPromise && !state.acpHandle) {
|
|
@@ -30855,7 +30889,8 @@ async function ensureAcpClient(options) {
|
|
|
30855
30889
|
state.lastAcpStartError = "Agent subprocess exited";
|
|
30856
30890
|
},
|
|
30857
30891
|
...hooks,
|
|
30858
|
-
cwd: targetSessionParentPath
|
|
30892
|
+
cwd: targetSessionParentPath,
|
|
30893
|
+
mcpServers
|
|
30859
30894
|
}).then(async (h) => {
|
|
30860
30895
|
if (spawnEpoch !== state.clientEpoch || isCliImmediateShutdownRequested()) {
|
|
30861
30896
|
try {
|
|
@@ -35836,7 +35871,7 @@ async function putEncryptedChangeSummaryRows(params) {
|
|
|
35836
35871
|
return { path: path46, summary: JSON.stringify(enc) };
|
|
35837
35872
|
});
|
|
35838
35873
|
const res = await fetch(
|
|
35839
|
-
`${base}/
|
|
35874
|
+
`${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
35840
35875
|
{
|
|
35841
35876
|
method: "PUT",
|
|
35842
35877
|
headers: {
|
|
@@ -35975,8 +36010,8 @@ async function fetchSessionAttachmentPayloadsForAgent(params) {
|
|
|
35975
36010
|
for (const a of attachments) {
|
|
35976
36011
|
const id = typeof a.attachmentId === "string" ? a.attachmentId.trim() : "";
|
|
35977
36012
|
if (!id) continue;
|
|
35978
|
-
const metaUrl = `${base}/
|
|
35979
|
-
const blobUrl = `${base}/
|
|
36013
|
+
const metaUrl = `${base}/internal/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/meta`;
|
|
36014
|
+
const blobUrl = `${base}/internal/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/blob`;
|
|
35980
36015
|
const metaRes = await fetch(metaUrl, { headers: { Authorization: `Bearer ${token}` } });
|
|
35981
36016
|
if (!metaRes.ok) {
|
|
35982
36017
|
const t = await metaRes.text().catch(() => "");
|
|
@@ -36024,6 +36059,106 @@ async function fetchSessionAttachmentPayloadsForAgent(params) {
|
|
|
36024
36059
|
return { ok: true, images: out };
|
|
36025
36060
|
}
|
|
36026
36061
|
|
|
36062
|
+
// src/agents/acp/build-forked-session-agent-prompt.ts
|
|
36063
|
+
function buildForkedSessionAgentPrompt(userPrompt, childSessionId, parentSessionId) {
|
|
36064
|
+
const childRef = childSessionId.trim();
|
|
36065
|
+
const parentRef = parentSessionId.trim();
|
|
36066
|
+
return [
|
|
36067
|
+
"You are working in a new session that was forked from a prior session.",
|
|
36068
|
+
`This forked session id: ${childRef}`,
|
|
36069
|
+
`Parent session id: ${parentRef}`,
|
|
36070
|
+
"",
|
|
36071
|
+
"The parent session history is available through MCP tools on the buildautomaton-bridge server:",
|
|
36072
|
+
"- list_parent_session_prompts \u2014 list each user prompt / turn in the parent session",
|
|
36073
|
+
"- get_parent_session_turn_transcript \u2014 full transcript for one turn (including tool calls); requires turnId",
|
|
36074
|
+
"",
|
|
36075
|
+
"When calling these tools, always pass this forked session id as sessionId (not the parent session id).",
|
|
36076
|
+
"",
|
|
36077
|
+
"Use these tools when you need prior context instead of guessing.",
|
|
36078
|
+
"",
|
|
36079
|
+
"User request:",
|
|
36080
|
+
userPrompt.trim()
|
|
36081
|
+
].join("\n");
|
|
36082
|
+
}
|
|
36083
|
+
|
|
36084
|
+
// src/mcp/tools/session-history/fetch-parent-session-cloud.ts
|
|
36085
|
+
function internalApiBase(cloudApiBaseUrl) {
|
|
36086
|
+
return cloudApiBaseUrl.replace(/\/+$/, "");
|
|
36087
|
+
}
|
|
36088
|
+
async function fetchCloudSessionMeta(params) {
|
|
36089
|
+
const token = params.getCloudAccessToken();
|
|
36090
|
+
if (!token) return { ok: false, error: "Missing cloud access token." };
|
|
36091
|
+
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.sessionId)}/meta`;
|
|
36092
|
+
const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
|
|
36093
|
+
if (!res.ok) {
|
|
36094
|
+
const t = await res.text().catch(() => "");
|
|
36095
|
+
return { ok: false, error: `Session meta fetch failed (${res.status}): ${t.slice(0, 200)}` };
|
|
36096
|
+
}
|
|
36097
|
+
const data = await res.json().catch(() => null);
|
|
36098
|
+
if (!data) return { ok: false, error: "Invalid session meta response." };
|
|
36099
|
+
return { ok: true, data };
|
|
36100
|
+
}
|
|
36101
|
+
async function fetchCloudParentSessionPrompts(params) {
|
|
36102
|
+
const token = params.getCloudAccessToken();
|
|
36103
|
+
if (!token) return { ok: false, error: "Missing cloud access token." };
|
|
36104
|
+
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/prompts`;
|
|
36105
|
+
const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
|
|
36106
|
+
if (!res.ok) {
|
|
36107
|
+
const t = await res.text().catch(() => "");
|
|
36108
|
+
return { ok: false, error: `Parent prompts fetch failed (${res.status}): ${t.slice(0, 200)}` };
|
|
36109
|
+
}
|
|
36110
|
+
const data = await res.json().catch(() => null);
|
|
36111
|
+
if (!data) return { ok: false, error: "Invalid parent prompts response." };
|
|
36112
|
+
return { ok: true, data };
|
|
36113
|
+
}
|
|
36114
|
+
async function fetchCloudParentTurnTranscript(params) {
|
|
36115
|
+
const token = params.getCloudAccessToken();
|
|
36116
|
+
if (!token) return { ok: false, error: "Missing cloud access token." };
|
|
36117
|
+
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/turns/${encodeURIComponent(params.turnId)}/transcript`;
|
|
36118
|
+
const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
|
|
36119
|
+
if (!res.ok) {
|
|
36120
|
+
const t = await res.text().catch(() => "");
|
|
36121
|
+
return { ok: false, error: `Transcript fetch failed (${res.status}): ${t.slice(0, 200)}` };
|
|
36122
|
+
}
|
|
36123
|
+
const data = await res.json().catch(() => null);
|
|
36124
|
+
const rows = Array.isArray(data?.transcript) ? data.transcript : [];
|
|
36125
|
+
const lines = rows.map((row) => {
|
|
36126
|
+
const kind = typeof row.kind === "string" ? row.kind : "event";
|
|
36127
|
+
const payload = typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload ?? "");
|
|
36128
|
+
return `[${kind}] ${payload}`;
|
|
36129
|
+
});
|
|
36130
|
+
return { ok: true, text: lines.join("\n") };
|
|
36131
|
+
}
|
|
36132
|
+
async function resolveParentSessionIdForChild(params) {
|
|
36133
|
+
const meta = await fetchCloudSessionMeta({
|
|
36134
|
+
sessionId: params.childSessionId,
|
|
36135
|
+
cloudApiBaseUrl: params.cloudApiBaseUrl,
|
|
36136
|
+
getCloudAccessToken: params.getCloudAccessToken
|
|
36137
|
+
});
|
|
36138
|
+
if (!meta.ok) return meta;
|
|
36139
|
+
const parent = meta.data.parentSessionId?.trim() ?? "";
|
|
36140
|
+
if (!parent) {
|
|
36141
|
+
return {
|
|
36142
|
+
ok: false,
|
|
36143
|
+
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.`
|
|
36144
|
+
};
|
|
36145
|
+
}
|
|
36146
|
+
return { ok: true, parentSessionId: parent };
|
|
36147
|
+
}
|
|
36148
|
+
|
|
36149
|
+
// src/agents/acp/enrich-forked-session-prompt-for-agent.ts
|
|
36150
|
+
async function enrichForkedSessionPromptForAgent(params) {
|
|
36151
|
+
const meta = await fetchCloudSessionMeta({
|
|
36152
|
+
sessionId: params.sessionId,
|
|
36153
|
+
cloudApiBaseUrl: params.cloudApiBaseUrl,
|
|
36154
|
+
getCloudAccessToken: params.getCloudAccessToken
|
|
36155
|
+
});
|
|
36156
|
+
if (!meta.ok) return params.promptText;
|
|
36157
|
+
const parentSessionId = meta.data.parentSessionId?.trim() ?? "";
|
|
36158
|
+
if (!parentSessionId) return params.promptText;
|
|
36159
|
+
return buildForkedSessionAgentPrompt(params.promptText, params.sessionId, parentSessionId);
|
|
36160
|
+
}
|
|
36161
|
+
|
|
36027
36162
|
// src/agents/acp/send-prompt-to-agent.ts
|
|
36028
36163
|
async function sendPromptToAgent(options) {
|
|
36029
36164
|
const {
|
|
@@ -36045,6 +36180,15 @@ async function sendPromptToAgent(options) {
|
|
|
36045
36180
|
attachments
|
|
36046
36181
|
} = options;
|
|
36047
36182
|
try {
|
|
36183
|
+
let agentPromptText = promptText;
|
|
36184
|
+
if (sessionId && cloudApiBaseUrl && getCloudAccessToken) {
|
|
36185
|
+
agentPromptText = await enrichForkedSessionPromptForAgent({
|
|
36186
|
+
sessionId,
|
|
36187
|
+
promptText,
|
|
36188
|
+
cloudApiBaseUrl,
|
|
36189
|
+
getCloudAccessToken
|
|
36190
|
+
});
|
|
36191
|
+
}
|
|
36048
36192
|
let sendOpts = {};
|
|
36049
36193
|
if (attachments && attachments.length > 0) {
|
|
36050
36194
|
if (!sessionId || !cloudApiBaseUrl || !getCloudAccessToken) {
|
|
@@ -36083,7 +36227,7 @@ async function sendPromptToAgent(options) {
|
|
|
36083
36227
|
}
|
|
36084
36228
|
sendOpts = { images: resolved.images };
|
|
36085
36229
|
}
|
|
36086
|
-
const result = await handle.sendPrompt(
|
|
36230
|
+
const result = await handle.sendPrompt(agentPromptText, sendOpts);
|
|
36087
36231
|
if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
|
|
36088
36232
|
await collectTurnGitDiffFromPreTurnSnapshot({
|
|
36089
36233
|
sessionId,
|
|
@@ -36144,11 +36288,11 @@ async function sendPromptToAgent(options) {
|
|
|
36144
36288
|
}
|
|
36145
36289
|
|
|
36146
36290
|
// src/agents/acp/manager/get-acp-agent-state.ts
|
|
36147
|
-
function
|
|
36148
|
-
let state = ctx.acpAgents.get(
|
|
36291
|
+
function getAcpSessionAgentState(ctx, acpSessionAgentKey) {
|
|
36292
|
+
let state = ctx.acpAgents.get(acpSessionAgentKey);
|
|
36149
36293
|
if (!state) {
|
|
36150
36294
|
state = createEmptyAcpClientState();
|
|
36151
|
-
ctx.acpAgents.set(
|
|
36295
|
+
ctx.acpAgents.set(acpSessionAgentKey, state);
|
|
36152
36296
|
}
|
|
36153
36297
|
return state;
|
|
36154
36298
|
}
|
|
@@ -36192,11 +36336,12 @@ function handlePrompt(ctx, opts) {
|
|
|
36192
36336
|
return;
|
|
36193
36337
|
}
|
|
36194
36338
|
const activeAcpAgentKey = acpAgentKey;
|
|
36339
|
+
const activeAcpSessionAgentKey = computeAcpSessionAgentKey(sessionId, activeAcpAgentKey);
|
|
36195
36340
|
ctx.pendingCancelRunIds.delete(activeRunId);
|
|
36196
36341
|
ctx.promptRouting.registerRun({ sessionId, runId: activeRunId });
|
|
36197
|
-
ctx.runDispatch.set(activeRunId, {
|
|
36342
|
+
ctx.runDispatch.set(activeRunId, { acpSessionAgentKey: activeAcpSessionAgentKey });
|
|
36198
36343
|
async function run() {
|
|
36199
|
-
const acpAgentState =
|
|
36344
|
+
const acpAgentState = getAcpSessionAgentState(ctx, activeAcpSessionAgentKey);
|
|
36200
36345
|
const handle = await ensureAcpClient({
|
|
36201
36346
|
state: acpAgentState,
|
|
36202
36347
|
acpAgentKey: activeAcpAgentKey,
|
|
@@ -36204,8 +36349,9 @@ function handlePrompt(ctx, opts) {
|
|
|
36204
36349
|
mode,
|
|
36205
36350
|
agentConfig: agentConfig ?? null,
|
|
36206
36351
|
sessionParentPath,
|
|
36207
|
-
resolveRouting: () => ctx.promptRouting.resolveRouting(
|
|
36352
|
+
resolveRouting: () => ctx.promptRouting.resolveRouting(activeAcpSessionAgentKey),
|
|
36208
36353
|
cloudSessionId: sessionId,
|
|
36354
|
+
bridgeAccessPort: ctx.getBridgeAccessPort(),
|
|
36209
36355
|
sendSessionUpdate,
|
|
36210
36356
|
sendRequest: sendSessionUpdate,
|
|
36211
36357
|
log: ctx.log,
|
|
@@ -36247,7 +36393,7 @@ function handlePrompt(ctx, opts) {
|
|
|
36247
36393
|
});
|
|
36248
36394
|
return;
|
|
36249
36395
|
}
|
|
36250
|
-
ctx.promptRouting.setStreamingRunId(
|
|
36396
|
+
ctx.promptRouting.setStreamingRunId(activeAcpSessionAgentKey, activeRunId);
|
|
36251
36397
|
try {
|
|
36252
36398
|
await sendPromptToAgent({
|
|
36253
36399
|
handle,
|
|
@@ -36268,7 +36414,7 @@ function handlePrompt(ctx, opts) {
|
|
|
36268
36414
|
attachments
|
|
36269
36415
|
});
|
|
36270
36416
|
} finally {
|
|
36271
|
-
ctx.promptRouting.clearStreamingRunId(
|
|
36417
|
+
ctx.promptRouting.clearStreamingRunId(activeAcpSessionAgentKey, activeRunId);
|
|
36272
36418
|
}
|
|
36273
36419
|
}
|
|
36274
36420
|
void run().finally(() => {
|
|
@@ -36292,6 +36438,7 @@ function logPromptReceivedFromBridge(ctx, opts) {
|
|
|
36292
36438
|
async function createAcpManager(options) {
|
|
36293
36439
|
const ctx = createAcpManagerContext({
|
|
36294
36440
|
log: options.log,
|
|
36441
|
+
getBridgeAccessPort: options.getBridgeAccessPort,
|
|
36295
36442
|
reportAgentCapabilities: options.reportAgentCapabilities
|
|
36296
36443
|
});
|
|
36297
36444
|
return {
|
|
@@ -41944,6 +42091,232 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
41944
42091
|
})();
|
|
41945
42092
|
}
|
|
41946
42093
|
|
|
42094
|
+
// src/mcp/tools/session-history/fork-session-mcp-tools.ts
|
|
42095
|
+
var LIST_PARENT_SESSION_PROMPTS_TOOL = "list_parent_session_prompts";
|
|
42096
|
+
var GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL = "get_parent_session_turn_transcript";
|
|
42097
|
+
var SESSION_ID_SCHEMA = {
|
|
42098
|
+
type: "string",
|
|
42099
|
+
description: "Id of this forked (child) session \u2014 not the parent session id. Required on every tool call."
|
|
42100
|
+
};
|
|
42101
|
+
var FORK_SESSION_MCP_TOOL_DEFINITIONS = [
|
|
42102
|
+
{
|
|
42103
|
+
name: LIST_PARENT_SESSION_PROMPTS_TOOL,
|
|
42104
|
+
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).",
|
|
42105
|
+
inputSchema: {
|
|
42106
|
+
type: "object",
|
|
42107
|
+
properties: { sessionId: SESSION_ID_SCHEMA },
|
|
42108
|
+
required: ["sessionId"],
|
|
42109
|
+
additionalProperties: false
|
|
42110
|
+
}
|
|
42111
|
+
},
|
|
42112
|
+
{
|
|
42113
|
+
name: GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL,
|
|
42114
|
+
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).",
|
|
42115
|
+
inputSchema: {
|
|
42116
|
+
type: "object",
|
|
42117
|
+
properties: {
|
|
42118
|
+
sessionId: SESSION_ID_SCHEMA,
|
|
42119
|
+
turnId: { type: "string", description: "Turn id from list_parent_session_prompts" }
|
|
42120
|
+
},
|
|
42121
|
+
required: ["sessionId", "turnId"],
|
|
42122
|
+
additionalProperties: false
|
|
42123
|
+
}
|
|
42124
|
+
}
|
|
42125
|
+
];
|
|
42126
|
+
function textResult(text, isError = false) {
|
|
42127
|
+
return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
|
|
42128
|
+
}
|
|
42129
|
+
function isForkSessionMcpToolName(name) {
|
|
42130
|
+
return name === LIST_PARENT_SESSION_PROMPTS_TOOL || name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL;
|
|
42131
|
+
}
|
|
42132
|
+
async function callForkSessionMcpTool(ctx, cloud, name, args) {
|
|
42133
|
+
const childSessionId = ctx.sessionId.trim();
|
|
42134
|
+
if (!childSessionId) {
|
|
42135
|
+
return textResult("sessionId is required.", true);
|
|
42136
|
+
}
|
|
42137
|
+
const parentResolved = await resolveParentSessionIdForChild({
|
|
42138
|
+
childSessionId,
|
|
42139
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
42140
|
+
getCloudAccessToken: cloud.getCloudAccessToken
|
|
42141
|
+
});
|
|
42142
|
+
if (!parentResolved.ok) {
|
|
42143
|
+
return textResult(parentResolved.error, true);
|
|
42144
|
+
}
|
|
42145
|
+
const parentSessionId = parentResolved.parentSessionId;
|
|
42146
|
+
if (name === LIST_PARENT_SESSION_PROMPTS_TOOL) {
|
|
42147
|
+
const detail = await fetchCloudParentSessionPrompts({
|
|
42148
|
+
childSessionId,
|
|
42149
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
42150
|
+
getCloudAccessToken: cloud.getCloudAccessToken
|
|
42151
|
+
});
|
|
42152
|
+
if (!detail.ok) return textResult(detail.error, true);
|
|
42153
|
+
const turns = Array.isArray(detail.data.turns) ? detail.data.turns : [];
|
|
42154
|
+
const payload = turns.map((t) => ({
|
|
42155
|
+
turnId: typeof t.turnId === "string" ? t.turnId : "",
|
|
42156
|
+
title: typeof t.title === "string" ? t.title : null,
|
|
42157
|
+
status: typeof t.status === "string" ? t.status : null,
|
|
42158
|
+
promptText: typeof t.promptText === "string" ? t.promptText : null
|
|
42159
|
+
}));
|
|
42160
|
+
return textResult(JSON.stringify({ parentSessionId, turns: payload }, null, 2));
|
|
42161
|
+
}
|
|
42162
|
+
if (name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL) {
|
|
42163
|
+
const turnId = typeof args.turnId === "string" ? args.turnId.trim() : "";
|
|
42164
|
+
if (!turnId) return textResult("turnId is required.", true);
|
|
42165
|
+
const transcript = await fetchCloudParentTurnTranscript({
|
|
42166
|
+
childSessionId,
|
|
42167
|
+
turnId,
|
|
42168
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
42169
|
+
getCloudAccessToken: cloud.getCloudAccessToken,
|
|
42170
|
+
e2ee: ctx.e2ee
|
|
42171
|
+
});
|
|
42172
|
+
if (!transcript.ok) return textResult(transcript.error, true);
|
|
42173
|
+
return textResult(transcript.text);
|
|
42174
|
+
}
|
|
42175
|
+
return textResult(`Unknown fork session tool: ${name}`, true);
|
|
42176
|
+
}
|
|
42177
|
+
|
|
42178
|
+
// src/mcp/bridge-mcp-tools.ts
|
|
42179
|
+
function requireCloud(shared) {
|
|
42180
|
+
const cloudApiBaseUrl = shared.cloudApiBaseUrl?.trim() ?? "";
|
|
42181
|
+
const getCloudAccessToken = shared.getCloudAccessToken;
|
|
42182
|
+
if (!cloudApiBaseUrl || !getCloudAccessToken) return null;
|
|
42183
|
+
return { cloudApiBaseUrl, getCloudAccessToken };
|
|
42184
|
+
}
|
|
42185
|
+
function createBridgeMcpToolRegistry(shared) {
|
|
42186
|
+
const cloud = requireCloud(shared);
|
|
42187
|
+
return {
|
|
42188
|
+
listTools: async () => [...FORK_SESSION_MCP_TOOL_DEFINITIONS],
|
|
42189
|
+
callTool: async (name, args) => {
|
|
42190
|
+
if (isForkSessionMcpToolName(name)) {
|
|
42191
|
+
if (!cloud) {
|
|
42192
|
+
return {
|
|
42193
|
+
content: [{ type: "text", text: "Bridge MCP tools require cloud API credentials." }],
|
|
42194
|
+
isError: true
|
|
42195
|
+
};
|
|
42196
|
+
}
|
|
42197
|
+
const sessionId = typeof args.sessionId === "string" ? args.sessionId.trim() : "";
|
|
42198
|
+
if (!sessionId) {
|
|
42199
|
+
return {
|
|
42200
|
+
content: [{ type: "text", text: "sessionId is required." }],
|
|
42201
|
+
isError: true
|
|
42202
|
+
};
|
|
42203
|
+
}
|
|
42204
|
+
return callForkSessionMcpTool({ ...shared, sessionId }, cloud, name, args);
|
|
42205
|
+
}
|
|
42206
|
+
throw new Error(`Unknown bridge MCP tool: ${name}`);
|
|
42207
|
+
}
|
|
42208
|
+
};
|
|
42209
|
+
}
|
|
42210
|
+
|
|
42211
|
+
// src/mcp/bridge-access/start-server.ts
|
|
42212
|
+
import * as http from "node:http";
|
|
42213
|
+
|
|
42214
|
+
// src/mcp/bridge-access/read-json-body.ts
|
|
42215
|
+
function readJsonBody(req) {
|
|
42216
|
+
return new Promise((resolve22, reject) => {
|
|
42217
|
+
const chunks = [];
|
|
42218
|
+
req.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
42219
|
+
req.on("end", () => {
|
|
42220
|
+
try {
|
|
42221
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
42222
|
+
if (!raw) {
|
|
42223
|
+
resolve22({});
|
|
42224
|
+
return;
|
|
42225
|
+
}
|
|
42226
|
+
const parsed = JSON.parse(raw);
|
|
42227
|
+
resolve22(parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {});
|
|
42228
|
+
} catch (e) {
|
|
42229
|
+
reject(e);
|
|
42230
|
+
}
|
|
42231
|
+
});
|
|
42232
|
+
req.on("error", reject);
|
|
42233
|
+
});
|
|
42234
|
+
}
|
|
42235
|
+
|
|
42236
|
+
// src/mcp/bridge-access/send-json-response.ts
|
|
42237
|
+
function sendJsonResponse(res, status, body) {
|
|
42238
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
42239
|
+
res.end(JSON.stringify(body));
|
|
42240
|
+
}
|
|
42241
|
+
|
|
42242
|
+
// src/mcp/bridge-access/handle-request.ts
|
|
42243
|
+
function createBridgeAccessRequestHandler(registry2) {
|
|
42244
|
+
return (req, res) => {
|
|
42245
|
+
void (async () => {
|
|
42246
|
+
if (req.method !== "POST") {
|
|
42247
|
+
sendJsonResponse(res, 405, { ok: false, error: "Method not allowed" });
|
|
42248
|
+
return;
|
|
42249
|
+
}
|
|
42250
|
+
try {
|
|
42251
|
+
const body = await readJsonBody(req);
|
|
42252
|
+
if (req.url === "/tools/list") {
|
|
42253
|
+
const tools = await Promise.resolve(registry2.listTools());
|
|
42254
|
+
sendJsonResponse(res, 200, { ok: true, data: tools });
|
|
42255
|
+
return;
|
|
42256
|
+
}
|
|
42257
|
+
if (req.url === "/tools/call") {
|
|
42258
|
+
const sessionId = typeof body.sessionId === "string" ? body.sessionId.trim() : "";
|
|
42259
|
+
if (!sessionId) {
|
|
42260
|
+
sendJsonResponse(res, 400, { ok: false, error: "sessionId is required" });
|
|
42261
|
+
return;
|
|
42262
|
+
}
|
|
42263
|
+
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
42264
|
+
const args = body.args && typeof body.args === "object" && !Array.isArray(body.args) ? body.args : {};
|
|
42265
|
+
if (!name) {
|
|
42266
|
+
sendJsonResponse(res, 400, { ok: false, error: "name is required" });
|
|
42267
|
+
return;
|
|
42268
|
+
}
|
|
42269
|
+
const data = await registry2.callTool(name, { ...args, sessionId });
|
|
42270
|
+
sendJsonResponse(res, 200, { ok: true, data });
|
|
42271
|
+
return;
|
|
42272
|
+
}
|
|
42273
|
+
sendJsonResponse(res, 404, { ok: false, error: "Not found" });
|
|
42274
|
+
} catch (err) {
|
|
42275
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
42276
|
+
sendJsonResponse(res, 500, { ok: false, error: message });
|
|
42277
|
+
}
|
|
42278
|
+
})();
|
|
42279
|
+
};
|
|
42280
|
+
}
|
|
42281
|
+
|
|
42282
|
+
// src/mcp/bridge-access/start-server.ts
|
|
42283
|
+
function startBridgeAccessServer(registry2) {
|
|
42284
|
+
const server = http.createServer(createBridgeAccessRequestHandler(registry2));
|
|
42285
|
+
return new Promise((resolve22, reject) => {
|
|
42286
|
+
server.once("error", reject);
|
|
42287
|
+
server.listen(0, "127.0.0.1", () => {
|
|
42288
|
+
const addr = server.address();
|
|
42289
|
+
if (!addr || typeof addr === "string") {
|
|
42290
|
+
reject(new Error("Bridge access server did not bind"));
|
|
42291
|
+
return;
|
|
42292
|
+
}
|
|
42293
|
+
resolve22({
|
|
42294
|
+
port: addr.port,
|
|
42295
|
+
close: () => new Promise((closeResolve, closeReject) => {
|
|
42296
|
+
server.close((err) => err ? closeReject(err) : closeResolve());
|
|
42297
|
+
})
|
|
42298
|
+
});
|
|
42299
|
+
});
|
|
42300
|
+
});
|
|
42301
|
+
}
|
|
42302
|
+
|
|
42303
|
+
// src/mcp/bridge-access/create-state.ts
|
|
42304
|
+
async function createBridgeAccessState(options = {}) {
|
|
42305
|
+
const registry2 = createBridgeMcpToolRegistry({
|
|
42306
|
+
cloudApiBaseUrl: options.cloudApiBaseUrl,
|
|
42307
|
+
getCloudAccessToken: options.getCloudAccessToken,
|
|
42308
|
+
e2ee: options.e2ee
|
|
42309
|
+
});
|
|
42310
|
+
const server = await startBridgeAccessServer(registry2);
|
|
42311
|
+
return {
|
|
42312
|
+
getPort: () => server.port,
|
|
42313
|
+
getRegistry: () => registry2,
|
|
42314
|
+
close: async () => {
|
|
42315
|
+
await server.close();
|
|
42316
|
+
}
|
|
42317
|
+
};
|
|
42318
|
+
}
|
|
42319
|
+
|
|
41947
42320
|
// src/connection/create-bridge-connection.ts
|
|
41948
42321
|
async function createBridgeConnection(options) {
|
|
41949
42322
|
const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
|
|
@@ -42006,12 +42379,18 @@ async function createBridgeConnection(options) {
|
|
|
42006
42379
|
worktreesRootPath,
|
|
42007
42380
|
log: logFn
|
|
42008
42381
|
});
|
|
42382
|
+
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
42383
|
+
const bridgeAccess = await createBridgeAccessState({
|
|
42384
|
+
cloudApiBaseUrl: apiUrl,
|
|
42385
|
+
getCloudAccessToken: () => tokens.accessToken,
|
|
42386
|
+
e2ee
|
|
42387
|
+
});
|
|
42009
42388
|
const acpManager = await createAcpManager({
|
|
42010
42389
|
log: logFn,
|
|
42390
|
+
getBridgeAccessPort: () => bridgeAccess.getPort(),
|
|
42011
42391
|
reportAgentCapabilities: sendAgentCapabilitiesToBridge
|
|
42012
42392
|
});
|
|
42013
42393
|
logFn("CLI running. Press Ctrl+C to exit.");
|
|
42014
|
-
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
42015
42394
|
const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
|
|
42016
42395
|
const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
|
|
42017
42396
|
const baseOnBridgeIdentified = createOnBridgeIdentified({
|
|
@@ -42077,6 +42456,7 @@ async function createBridgeConnection(options) {
|
|
|
42077
42456
|
stopFileIndexWatcher();
|
|
42078
42457
|
bridgeHeartbeat.stop();
|
|
42079
42458
|
await closeBridgeConnection(state, acpManager, devServerManager, logFn);
|
|
42459
|
+
await bridgeAccess.close();
|
|
42080
42460
|
}
|
|
42081
42461
|
};
|
|
42082
42462
|
}
|