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