@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/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 http = __require("http");
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 : http.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 http = __require("http");
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 = http.createServer((req, res) => {
3344
- const body = http.STATUS_CODES[426];
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 || http.STATUS_CODES[code];
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} ${http.STATUS_CODES[code]}\r
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.45".length > 0 ? "0.1.45" : "0.0.0-dev";
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}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
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}/api/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/meta`;
33024
- const blobUrl = `${base}/api/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/blob`;
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(promptText, sendOpts);
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
  }