@buildautomaton/cli 0.1.45 → 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 +390 -16
- 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 +390 -16
- 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";
|
|
@@ -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(),
|
|
@@ -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,
|
|
@@ -30709,6 +30712,30 @@ function computeAcpAgentKey(preferredAgentType, mode, agentConfig) {
|
|
|
30709
30712
|
return `${resolved.label}::${fullCmd.join("\0")}${cacheConfig}`;
|
|
30710
30713
|
}
|
|
30711
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
|
+
|
|
30712
30739
|
// src/agents/acp/ensure-acp-client.ts
|
|
30713
30740
|
function invalidateAcpClientState(state) {
|
|
30714
30741
|
state.clientEpoch += 1;
|
|
@@ -30740,11 +30767,13 @@ async function ensureAcpClient(options) {
|
|
|
30740
30767
|
sessionParentPath,
|
|
30741
30768
|
resolveRouting,
|
|
30742
30769
|
cloudSessionId,
|
|
30770
|
+
bridgeAccessPort,
|
|
30743
30771
|
reportAgentCapabilities,
|
|
30744
30772
|
sendSessionUpdate,
|
|
30745
30773
|
sendRequest,
|
|
30746
30774
|
log: log2
|
|
30747
30775
|
} = options;
|
|
30776
|
+
const mcpServers = buildAcpMcpServers(bridgeAccessPort ?? 0);
|
|
30748
30777
|
state.latestAgentConfigForBridgeHooks = agentConfig != null && typeof agentConfig === "object" && !Array.isArray(agentConfig) ? agentConfig : null;
|
|
30749
30778
|
const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
|
|
30750
30779
|
if (state.acpStartPromise && !state.acpHandle) {
|
|
@@ -30860,7 +30889,8 @@ async function ensureAcpClient(options) {
|
|
|
30860
30889
|
state.lastAcpStartError = "Agent subprocess exited";
|
|
30861
30890
|
},
|
|
30862
30891
|
...hooks,
|
|
30863
|
-
cwd: targetSessionParentPath
|
|
30892
|
+
cwd: targetSessionParentPath,
|
|
30893
|
+
mcpServers
|
|
30864
30894
|
}).then(async (h) => {
|
|
30865
30895
|
if (spawnEpoch !== state.clientEpoch || isCliImmediateShutdownRequested()) {
|
|
30866
30896
|
try {
|
|
@@ -35841,7 +35871,7 @@ async function putEncryptedChangeSummaryRows(params) {
|
|
|
35841
35871
|
return { path: path46, summary: JSON.stringify(enc) };
|
|
35842
35872
|
});
|
|
35843
35873
|
const res = await fetch(
|
|
35844
|
-
`${base}/
|
|
35874
|
+
`${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
35845
35875
|
{
|
|
35846
35876
|
method: "PUT",
|
|
35847
35877
|
headers: {
|
|
@@ -35980,8 +36010,8 @@ async function fetchSessionAttachmentPayloadsForAgent(params) {
|
|
|
35980
36010
|
for (const a of attachments) {
|
|
35981
36011
|
const id = typeof a.attachmentId === "string" ? a.attachmentId.trim() : "";
|
|
35982
36012
|
if (!id) continue;
|
|
35983
|
-
const metaUrl = `${base}/
|
|
35984
|
-
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`;
|
|
35985
36015
|
const metaRes = await fetch(metaUrl, { headers: { Authorization: `Bearer ${token}` } });
|
|
35986
36016
|
if (!metaRes.ok) {
|
|
35987
36017
|
const t = await metaRes.text().catch(() => "");
|
|
@@ -36029,6 +36059,106 @@ async function fetchSessionAttachmentPayloadsForAgent(params) {
|
|
|
36029
36059
|
return { ok: true, images: out };
|
|
36030
36060
|
}
|
|
36031
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
|
+
|
|
36032
36162
|
// src/agents/acp/send-prompt-to-agent.ts
|
|
36033
36163
|
async function sendPromptToAgent(options) {
|
|
36034
36164
|
const {
|
|
@@ -36050,6 +36180,15 @@ async function sendPromptToAgent(options) {
|
|
|
36050
36180
|
attachments
|
|
36051
36181
|
} = options;
|
|
36052
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
|
+
}
|
|
36053
36192
|
let sendOpts = {};
|
|
36054
36193
|
if (attachments && attachments.length > 0) {
|
|
36055
36194
|
if (!sessionId || !cloudApiBaseUrl || !getCloudAccessToken) {
|
|
@@ -36088,7 +36227,7 @@ async function sendPromptToAgent(options) {
|
|
|
36088
36227
|
}
|
|
36089
36228
|
sendOpts = { images: resolved.images };
|
|
36090
36229
|
}
|
|
36091
|
-
const result = await handle.sendPrompt(
|
|
36230
|
+
const result = await handle.sendPrompt(agentPromptText, sendOpts);
|
|
36092
36231
|
if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
|
|
36093
36232
|
await collectTurnGitDiffFromPreTurnSnapshot({
|
|
36094
36233
|
sessionId,
|
|
@@ -36212,6 +36351,7 @@ function handlePrompt(ctx, opts) {
|
|
|
36212
36351
|
sessionParentPath,
|
|
36213
36352
|
resolveRouting: () => ctx.promptRouting.resolveRouting(activeAcpSessionAgentKey),
|
|
36214
36353
|
cloudSessionId: sessionId,
|
|
36354
|
+
bridgeAccessPort: ctx.getBridgeAccessPort(),
|
|
36215
36355
|
sendSessionUpdate,
|
|
36216
36356
|
sendRequest: sendSessionUpdate,
|
|
36217
36357
|
log: ctx.log,
|
|
@@ -36298,6 +36438,7 @@ function logPromptReceivedFromBridge(ctx, opts) {
|
|
|
36298
36438
|
async function createAcpManager(options) {
|
|
36299
36439
|
const ctx = createAcpManagerContext({
|
|
36300
36440
|
log: options.log,
|
|
36441
|
+
getBridgeAccessPort: options.getBridgeAccessPort,
|
|
36301
36442
|
reportAgentCapabilities: options.reportAgentCapabilities
|
|
36302
36443
|
});
|
|
36303
36444
|
return {
|
|
@@ -41950,6 +42091,232 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
41950
42091
|
})();
|
|
41951
42092
|
}
|
|
41952
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
|
+
|
|
41953
42320
|
// src/connection/create-bridge-connection.ts
|
|
41954
42321
|
async function createBridgeConnection(options) {
|
|
41955
42322
|
const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
|
|
@@ -42012,12 +42379,18 @@ async function createBridgeConnection(options) {
|
|
|
42012
42379
|
worktreesRootPath,
|
|
42013
42380
|
log: logFn
|
|
42014
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
|
+
});
|
|
42015
42388
|
const acpManager = await createAcpManager({
|
|
42016
42389
|
log: logFn,
|
|
42390
|
+
getBridgeAccessPort: () => bridgeAccess.getPort(),
|
|
42017
42391
|
reportAgentCapabilities: sendAgentCapabilitiesToBridge
|
|
42018
42392
|
});
|
|
42019
42393
|
logFn("CLI running. Press Ctrl+C to exit.");
|
|
42020
|
-
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
42021
42394
|
const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
|
|
42022
42395
|
const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
|
|
42023
42396
|
const baseOnBridgeIdentified = createOnBridgeIdentified({
|
|
@@ -42083,6 +42456,7 @@ async function createBridgeConnection(options) {
|
|
|
42083
42456
|
stopFileIndexWatcher();
|
|
42084
42457
|
bridgeHeartbeat.stop();
|
|
42085
42458
|
await closeBridgeConnection(state, acpManager, devServerManager, logFn);
|
|
42459
|
+
await bridgeAccess.close();
|
|
42086
42460
|
}
|
|
42087
42461
|
};
|
|
42088
42462
|
}
|