@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/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;
|
|
@@ -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(),
|
|
@@ -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,
|
|
@@ -27723,6 +27726,30 @@ function computeAcpAgentKey(preferredAgentType, mode, agentConfig) {
|
|
|
27723
27726
|
return `${resolved.label}::${fullCmd.join("\0")}${cacheConfig}`;
|
|
27724
27727
|
}
|
|
27725
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
|
+
|
|
27726
27753
|
// src/agents/acp/ensure-acp-client.ts
|
|
27727
27754
|
function invalidateAcpClientState(state) {
|
|
27728
27755
|
state.clientEpoch += 1;
|
|
@@ -27754,11 +27781,13 @@ async function ensureAcpClient(options) {
|
|
|
27754
27781
|
sessionParentPath,
|
|
27755
27782
|
resolveRouting,
|
|
27756
27783
|
cloudSessionId,
|
|
27784
|
+
bridgeAccessPort,
|
|
27757
27785
|
reportAgentCapabilities,
|
|
27758
27786
|
sendSessionUpdate,
|
|
27759
27787
|
sendRequest,
|
|
27760
27788
|
log: log2
|
|
27761
27789
|
} = options;
|
|
27790
|
+
const mcpServers = buildAcpMcpServers(bridgeAccessPort ?? 0);
|
|
27762
27791
|
state.latestAgentConfigForBridgeHooks = agentConfig != null && typeof agentConfig === "object" && !Array.isArray(agentConfig) ? agentConfig : null;
|
|
27763
27792
|
const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
|
|
27764
27793
|
if (state.acpStartPromise && !state.acpHandle) {
|
|
@@ -27874,7 +27903,8 @@ async function ensureAcpClient(options) {
|
|
|
27874
27903
|
state.lastAcpStartError = "Agent subprocess exited";
|
|
27875
27904
|
},
|
|
27876
27905
|
...hooks,
|
|
27877
|
-
cwd: targetSessionParentPath
|
|
27906
|
+
cwd: targetSessionParentPath,
|
|
27907
|
+
mcpServers
|
|
27878
27908
|
}).then(async (h) => {
|
|
27879
27909
|
if (spawnEpoch !== state.clientEpoch || isCliImmediateShutdownRequested()) {
|
|
27880
27910
|
try {
|
|
@@ -32855,7 +32885,7 @@ async function putEncryptedChangeSummaryRows(params) {
|
|
|
32855
32885
|
return { path: path44, summary: JSON.stringify(enc) };
|
|
32856
32886
|
});
|
|
32857
32887
|
const res = await fetch(
|
|
32858
|
-
`${base}/
|
|
32888
|
+
`${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
32859
32889
|
{
|
|
32860
32890
|
method: "PUT",
|
|
32861
32891
|
headers: {
|
|
@@ -33020,8 +33050,8 @@ async function fetchSessionAttachmentPayloadsForAgent(params) {
|
|
|
33020
33050
|
for (const a of attachments) {
|
|
33021
33051
|
const id = typeof a.attachmentId === "string" ? a.attachmentId.trim() : "";
|
|
33022
33052
|
if (!id) continue;
|
|
33023
|
-
const metaUrl = `${base}/
|
|
33024
|
-
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`;
|
|
33025
33055
|
const metaRes = await fetch(metaUrl, { headers: { Authorization: `Bearer ${token}` } });
|
|
33026
33056
|
if (!metaRes.ok) {
|
|
33027
33057
|
const t = await metaRes.text().catch(() => "");
|
|
@@ -33069,6 +33099,106 @@ async function fetchSessionAttachmentPayloadsForAgent(params) {
|
|
|
33069
33099
|
return { ok: true, images: out };
|
|
33070
33100
|
}
|
|
33071
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
|
+
|
|
33072
33202
|
// src/agents/acp/send-prompt-to-agent.ts
|
|
33073
33203
|
async function sendPromptToAgent(options) {
|
|
33074
33204
|
const {
|
|
@@ -33090,6 +33220,15 @@ async function sendPromptToAgent(options) {
|
|
|
33090
33220
|
attachments
|
|
33091
33221
|
} = options;
|
|
33092
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
|
+
}
|
|
33093
33232
|
let sendOpts = {};
|
|
33094
33233
|
if (attachments && attachments.length > 0) {
|
|
33095
33234
|
if (!sessionId || !cloudApiBaseUrl || !getCloudAccessToken) {
|
|
@@ -33128,7 +33267,7 @@ async function sendPromptToAgent(options) {
|
|
|
33128
33267
|
}
|
|
33129
33268
|
sendOpts = { images: resolved.images };
|
|
33130
33269
|
}
|
|
33131
|
-
const result = await handle.sendPrompt(
|
|
33270
|
+
const result = await handle.sendPrompt(agentPromptText, sendOpts);
|
|
33132
33271
|
if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
|
|
33133
33272
|
await collectTurnGitDiffFromPreTurnSnapshot({
|
|
33134
33273
|
sessionId,
|
|
@@ -33252,6 +33391,7 @@ function handlePrompt(ctx, opts) {
|
|
|
33252
33391
|
sessionParentPath,
|
|
33253
33392
|
resolveRouting: () => ctx.promptRouting.resolveRouting(activeAcpSessionAgentKey),
|
|
33254
33393
|
cloudSessionId: sessionId,
|
|
33394
|
+
bridgeAccessPort: ctx.getBridgeAccessPort(),
|
|
33255
33395
|
sendSessionUpdate,
|
|
33256
33396
|
sendRequest: sendSessionUpdate,
|
|
33257
33397
|
log: ctx.log,
|
|
@@ -33338,6 +33478,7 @@ function logPromptReceivedFromBridge(ctx, opts) {
|
|
|
33338
33478
|
async function createAcpManager(options) {
|
|
33339
33479
|
const ctx = createAcpManagerContext({
|
|
33340
33480
|
log: options.log,
|
|
33481
|
+
getBridgeAccessPort: options.getBridgeAccessPort,
|
|
33341
33482
|
reportAgentCapabilities: options.reportAgentCapabilities
|
|
33342
33483
|
});
|
|
33343
33484
|
return {
|
|
@@ -38727,6 +38868,232 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
38727
38868
|
})();
|
|
38728
38869
|
}
|
|
38729
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
|
+
|
|
38730
39097
|
// src/connection/create-bridge-connection.ts
|
|
38731
39098
|
async function createBridgeConnection(options) {
|
|
38732
39099
|
const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
|
|
@@ -38789,12 +39156,18 @@ async function createBridgeConnection(options) {
|
|
|
38789
39156
|
worktreesRootPath,
|
|
38790
39157
|
log: logFn
|
|
38791
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
|
+
});
|
|
38792
39165
|
const acpManager = await createAcpManager({
|
|
38793
39166
|
log: logFn,
|
|
39167
|
+
getBridgeAccessPort: () => bridgeAccess.getPort(),
|
|
38794
39168
|
reportAgentCapabilities: sendAgentCapabilitiesToBridge
|
|
38795
39169
|
});
|
|
38796
39170
|
logFn("CLI running. Press Ctrl+C to exit.");
|
|
38797
|
-
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
38798
39171
|
const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
|
|
38799
39172
|
const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
|
|
38800
39173
|
const baseOnBridgeIdentified = createOnBridgeIdentified({
|
|
@@ -38860,6 +39233,7 @@ async function createBridgeConnection(options) {
|
|
|
38860
39233
|
stopFileIndexWatcher();
|
|
38861
39234
|
bridgeHeartbeat.stop();
|
|
38862
39235
|
await closeBridgeConnection(state, acpManager, devServerManager, logFn);
|
|
39236
|
+
await bridgeAccess.close();
|
|
38863
39237
|
}
|
|
38864
39238
|
};
|
|
38865
39239
|
}
|