@buildautomaton/cli 0.1.25 → 0.1.27

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
@@ -22933,8 +22933,77 @@ function isClaudeCodePermissionMode(value) {
22933
22933
  return MODE_SET.has(value);
22934
22934
  }
22935
22935
 
22936
+ // ../types/src/cli-permission-mode.ts
22937
+ var CLI_PERMISSION_MODE_DEFAULT = "default";
22938
+ var CLI_PERMISSION_MODE_DANGEROUS = "dangerous";
22939
+ function normalizeCliPermissionModeInput(raw) {
22940
+ if (typeof raw !== "string") return CLI_PERMISSION_MODE_DEFAULT;
22941
+ const t = raw.trim();
22942
+ if (t === CLI_PERMISSION_MODE_DANGEROUS) return CLI_PERMISSION_MODE_DANGEROUS;
22943
+ if (t === "standard") return CLI_PERMISSION_MODE_DEFAULT;
22944
+ return CLI_PERMISSION_MODE_DEFAULT;
22945
+ }
22946
+
22947
+ // ../types/src/acp-permission-auto-approve.ts
22948
+ function isRejectKind(kind) {
22949
+ return kind === "reject_once" || kind === "reject_always";
22950
+ }
22951
+ function normalizeOptions(raw) {
22952
+ if (!Array.isArray(raw)) return [];
22953
+ const out = [];
22954
+ for (const item of raw) {
22955
+ if (item == null || typeof item !== "object" || Array.isArray(item)) continue;
22956
+ const o = item;
22957
+ const rawId = o.optionId ?? o.id;
22958
+ const optionId = typeof rawId === "string" && rawId.trim() !== "" ? rawId.trim() : typeof rawId === "number" && Number.isFinite(rawId) ? String(rawId) : "";
22959
+ if (!optionId) continue;
22960
+ const kind = typeof o.kind === "string" ? o.kind : void 0;
22961
+ out.push({ optionId, ...kind ? { kind } : {} });
22962
+ }
22963
+ return out;
22964
+ }
22965
+ function pickAllowOption(options) {
22966
+ const nonReject = options.filter((o) => !isRejectKind(o.kind));
22967
+ if (nonReject.length === 0) return null;
22968
+ const allowOnce = nonReject.find((o) => o.kind === "allow_once");
22969
+ if (allowOnce) return allowOnce;
22970
+ const notAlways = nonReject.filter((o) => o.kind !== "allow_always");
22971
+ if (notAlways.length > 0) return notAlways[0] ?? null;
22972
+ return nonReject.find((o) => o.kind === "allow_always") ?? nonReject[0] ?? null;
22973
+ }
22974
+ function firstNonEmptyOptionsArray(...candidates) {
22975
+ for (const c of candidates) {
22976
+ if (Array.isArray(c) && c.length > 0) return c;
22977
+ }
22978
+ const fallback = candidates[0];
22979
+ return Array.isArray(fallback) ? fallback : [];
22980
+ }
22981
+ function extractAcpPermissionRequestOptionArray(params) {
22982
+ const toolCall = params.toolCall;
22983
+ const fromToolCall = toolCall != null && typeof toolCall === "object" && !Array.isArray(toolCall) ? toolCall : null;
22984
+ return firstNonEmptyOptionsArray(
22985
+ params.options,
22986
+ params.permissionOptions,
22987
+ fromToolCall?.options,
22988
+ fromToolCall?.permissionOptions
22989
+ );
22990
+ }
22991
+ function buildCliAutoApprovedPermissionRpcResult(requestParams) {
22992
+ const opt = pickAllowOption(normalizeOptions(extractAcpPermissionRequestOptionArray(requestParams)));
22993
+ if (!opt) return null;
22994
+ const kind = opt.kind?.trim();
22995
+ return {
22996
+ outcome: {
22997
+ outcome: "selected",
22998
+ optionId: opt.optionId,
22999
+ ...kind ? { _meta: { permissionOptionKind: kind } } : {}
23000
+ }
23001
+ };
23002
+ }
23003
+
22936
23004
  // ../types/src/agent-config.ts
22937
23005
  var AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY = "claude_permission_mode";
23006
+ var AGENT_CONFIG_CLI_PERMISSION_MODE_KEY = "cli_permission_mode";
22938
23007
  function getClaudePermissionModeFromAgentConfig(config2) {
22939
23008
  if (!config2) return null;
22940
23009
  const raw = config2[AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY];
@@ -22942,6 +23011,10 @@ function getClaudePermissionModeFromAgentConfig(config2) {
22942
23011
  const t = raw.trim();
22943
23012
  return isClaudeCodePermissionMode(t) ? t : null;
22944
23013
  }
23014
+ function getCliPermissionModeFromAgentConfig(config2) {
23015
+ if (!config2) return CLI_PERMISSION_MODE_DEFAULT;
23016
+ return normalizeCliPermissionModeInput(config2[AGENT_CONFIG_CLI_PERMISSION_MODE_KEY]);
23017
+ }
22945
23018
 
22946
23019
  // src/agents/acp/claude-acp-permission-from-session.ts
22947
23020
  function flattenSelectOptions(options) {
@@ -23055,13 +23128,16 @@ function parseAcpInitAgentCapabilities(initResult) {
23055
23128
  const canLoad = agentCapabilities?.loadSession === true;
23056
23129
  const sessionCaps = agentCapabilities?.sessionCapabilities;
23057
23130
  const canResume = Boolean(sessionCaps?.resume);
23058
- return { canResume, canLoad };
23131
+ const promptCaps = agentCapabilities?.promptCapabilities;
23132
+ const promptSupportsImage = promptCaps?.image === true;
23133
+ return { canResume, canLoad, promptSupportsImage };
23059
23134
  }
23060
23135
 
23061
23136
  // src/agents/acp/clients/shared/bootstrap-acp-wire-session.ts
23062
23137
  async function bootstrapAcpWireSession(transport, ctx, initializeRequest) {
23063
23138
  const initResult = await transport.initialize(initializeRequest);
23064
- const { canResume, canLoad } = parseAcpInitAgentCapabilities(initResult);
23139
+ const { canResume, canLoad, promptSupportsImage } = parseAcpInitAgentCapabilities(initResult);
23140
+ ctx.agentPromptImageSupported = promptSupportsImage;
23065
23141
  await transport.afterInitialize?.();
23066
23142
  const established = await establishAcpSessionWithTransport(transport, ctx, canResume, canLoad);
23067
23143
  const sessionId = established.sessionId;
@@ -23152,11 +23228,27 @@ function normalizeAcpPromptTurnFailure(err, stderrCaptureText) {
23152
23228
  }
23153
23229
 
23154
23230
  // src/agents/acp/clients/shared/send-acp-prompt-via-transport.ts
23155
- async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText) {
23231
+ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText, images) {
23232
+ if (images && images.length > 0 && !ctx.agentPromptImageSupported) {
23233
+ await new Promise((r) => setImmediate(r));
23234
+ return normalizeAcpPromptTurnFailure(
23235
+ new Error(
23236
+ "This agent does not advertise image support in ACP (missing agentCapabilities.promptCapabilities.image). Remove images or use an agent that supports prompt images."
23237
+ ),
23238
+ ctx.getStderrText()
23239
+ );
23240
+ }
23241
+ const textBlock = promptText.trim() !== "" ? promptText : images?.length ? " " : "";
23242
+ const prompt = [{ type: "text", text: textBlock }];
23243
+ if (images && images.length > 0) {
23244
+ for (const im of images) {
23245
+ prompt.push({ type: "image", mimeType: im.mimeType, data: im.data });
23246
+ }
23247
+ }
23156
23248
  try {
23157
23249
  const response = await transport.prompt({
23158
23250
  sessionId,
23159
- prompt: [{ type: "text", text: promptText }]
23251
+ prompt
23160
23252
  });
23161
23253
  await new Promise((r2) => setImmediate(r2));
23162
23254
  const r = response;
@@ -23172,6 +23264,22 @@ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText)
23172
23264
  }
23173
23265
  }
23174
23266
 
23267
+ // src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
23268
+ function awaitSdkStdioPermissionRequestHandshake(params) {
23269
+ const { requestId, paramsRecord, pending, onRequest } = params;
23270
+ return new Promise((resolve16) => {
23271
+ pending.set(requestId, { resolve: resolve16, params: paramsRecord });
23272
+ try {
23273
+ onRequest?.({
23274
+ requestId,
23275
+ method: "session/request_permission",
23276
+ params: paramsRecord
23277
+ });
23278
+ } catch {
23279
+ }
23280
+ });
23281
+ }
23282
+
23175
23283
  // src/agents/acp/clients/sdk/sdk-acp-session-transport.ts
23176
23284
  function createSdkAcpSessionTransport(connection) {
23177
23285
  const c = connection;
@@ -23282,16 +23390,11 @@ async function createSdkStdioAcpClient(options) {
23282
23390
  async requestPermission(params) {
23283
23391
  const requestId = `perm-${++permissionSeq}`;
23284
23392
  const paramsRecord = params != null && typeof params === "object" ? params : {};
23285
- try {
23286
- onRequest?.({
23287
- requestId,
23288
- method: "session/request_permission",
23289
- params: paramsRecord
23290
- });
23291
- } catch {
23292
- }
23293
- return await new Promise((resolve17) => {
23294
- pendingPermissionReplies.set(requestId, { resolve: resolve17, params: paramsRecord });
23393
+ return await awaitSdkStdioPermissionRequestHandshake({
23394
+ requestId,
23395
+ paramsRecord,
23396
+ pending: pendingPermissionReplies,
23397
+ onRequest
23295
23398
  });
23296
23399
  },
23297
23400
  async readTextFile(params) {
@@ -23321,15 +23424,17 @@ async function createSdkStdioAcpClient(options) {
23321
23424
  const established = await bootstrapAcpWireSession(transport, sessionCtx, {
23322
23425
  protocolVersion: PROTOCOL_VERSION2,
23323
23426
  clientCapabilities: {
23324
- fs: { readTextFile: true, writeTextFile: true }
23427
+ fs: { readTextFile: true, writeTextFile: true },
23428
+ prompt: { image: true }
23325
23429
  },
23326
23430
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
23327
23431
  });
23328
23432
  const sessionId = established.sessionId;
23329
23433
  settleResolve({
23330
23434
  sessionId,
23331
- async sendPrompt(prompt, _options) {
23332
- return sendAcpPromptViaTransport(transport, sessionCtx, sessionId, prompt);
23435
+ async sendPrompt(prompt, options2) {
23436
+ const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
23437
+ return sendAcpPromptViaTransport(transport, sessionCtx, sessionId, prompt, imgs);
23333
23438
  },
23334
23439
  async cancel() {
23335
23440
  for (const [id, entry] of [...pendingPermissionReplies.entries()]) {
@@ -23791,7 +23896,7 @@ function installBridgeProcessResilience() {
23791
23896
  }
23792
23897
 
23793
23898
  // src/cli-version.ts
23794
- var CLI_VERSION = "0.1.25".length > 0 ? "0.1.25" : "0.0.0-dev";
23899
+ var CLI_VERSION = "0.1.27".length > 0 ? "0.1.27" : "0.0.0-dev";
23795
23900
 
23796
23901
  // ../../node_modules/.pnpm/open@10.2.0/node_modules/open/index.js
23797
23902
  import process7 from "node:process";
@@ -29867,6 +29972,122 @@ function augmentPromptResultAuthFields(agentType, errorText) {
29867
29972
  return { agentAuthRequired: true, agentType };
29868
29973
  }
29869
29974
 
29975
+ // ../e2ee/src/constants.ts
29976
+ var E2EE_NONCE_BYTES = 12;
29977
+
29978
+ // ../e2ee/src/types.ts
29979
+ function isE2eeEnvelope(value) {
29980
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
29981
+ const o = value;
29982
+ return typeof o.k === "string" && typeof o.n === "string" && typeof o.c === "string";
29983
+ }
29984
+
29985
+ // ../e2ee/src/encoding.ts
29986
+ function base64UrlEncode(bytes) {
29987
+ let binary = "";
29988
+ for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
29989
+ const b64 = btoa(binary);
29990
+ return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
29991
+ }
29992
+ function base64UrlDecode(value) {
29993
+ const b64 = value.replace(/-/g, "+").replace(/_/g, "/");
29994
+ const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
29995
+ const binary = atob(padded);
29996
+ const out = new Uint8Array(binary.length);
29997
+ for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
29998
+ return out;
29999
+ }
30000
+
30001
+ // src/agents/acp/fetch-session-attachments.ts
30002
+ function metaSaysEncrypted(meta) {
30003
+ if (!meta) return false;
30004
+ const e = meta.encrypted;
30005
+ return e === true || e === "true" || e === 1;
30006
+ }
30007
+ function warnIfDecodedImageMagicUnexpected(buf, mimeType, idShort) {
30008
+ const m = mimeType.toLowerCase();
30009
+ if (!m.startsWith("image/") || buf.length < 4) return;
30010
+ const b0 = buf[0];
30011
+ const b1 = buf[1];
30012
+ const b2 = buf[2];
30013
+ const b3 = buf[3];
30014
+ let looksOk = false;
30015
+ if (m.includes("png") && b0 === 137 && b1 === 80 && b2 === 78 && b3 === 71) looksOk = true;
30016
+ else if ((m.includes("jpeg") || m.includes("jpg")) && b0 === 255 && b1 === 216 && b2 === 255) looksOk = true;
30017
+ else if (m.includes("gif") && b0 === 71 && b1 === 73 && b2 === 70) looksOk = true;
30018
+ else if (m.includes("webp") && buf.length >= 12 && buf.subarray(0, 4).toString("ascii") === "RIFF" && buf.subarray(8, 12).toString("ascii") === "WEBP")
30019
+ looksOk = true;
30020
+ else if (!/(png|jpe?g|gif|webp)/i.test(m)) looksOk = true;
30021
+ if (!looksOk) {
30022
+ logDebug(
30023
+ `[Agent] Attachment ${idShort} (${mimeType}): decoded bytes do not match common image signatures \u2014 wrong E2EE key, corrupt blob, or metadata mismatch. First 12 bytes (hex): ${buf.subarray(0, Math.min(12, buf.length)).toString("hex")}`
30024
+ );
30025
+ }
30026
+ }
30027
+ async function fetchSessionAttachmentPayloadsForAgent(params) {
30028
+ const { attachments, sessionId, cloudApiBaseUrl, getCloudAccessToken, e2ee, log: log2 } = params;
30029
+ const token = getCloudAccessToken();
30030
+ if (!token) {
30031
+ return { ok: false, error: "Missing cloud access token; cannot download attachments." };
30032
+ }
30033
+ const wantedCount = attachments.filter((a) => typeof a.attachmentId === "string" && a.attachmentId.trim() !== "").length;
30034
+ if (wantedCount === 0) {
30035
+ return { ok: false, error: "No valid attachment ids in prompt." };
30036
+ }
30037
+ const base = cloudApiBaseUrl.replace(/\/+$/, "");
30038
+ const out = [];
30039
+ for (const a of attachments) {
30040
+ const id = typeof a.attachmentId === "string" ? a.attachmentId.trim() : "";
30041
+ if (!id) continue;
30042
+ const metaUrl = `${base}/api/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/meta`;
30043
+ const blobUrl = `${base}/api/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/blob`;
30044
+ const metaRes = await fetch(metaUrl, { headers: { Authorization: `Bearer ${token}` } });
30045
+ if (!metaRes.ok) {
30046
+ const t = await metaRes.text().catch(() => "");
30047
+ log2(`[Agent] Attachment meta fetch failed ${metaRes.status}: ${t.slice(0, 200)}`);
30048
+ return { ok: false, error: `Could not load attachment (${id.slice(0, 8)}\u2026): ${metaRes.status}` };
30049
+ }
30050
+ const meta = await metaRes.json().catch(() => null);
30051
+ const mimeType = typeof meta?.mimeType === "string" && meta.mimeType.trim() ? meta.mimeType.trim() : a.mimeType;
30052
+ const blobRes = await fetch(blobUrl, { headers: { Authorization: `Bearer ${token}` } });
30053
+ if (!blobRes.ok) {
30054
+ const t = await blobRes.text().catch(() => "");
30055
+ log2(`[Agent] Attachment blob fetch failed ${blobRes.status}: ${t.slice(0, 200)}`);
30056
+ return { ok: false, error: `Could not load attachment data (${id.slice(0, 8)}\u2026): ${blobRes.status}` };
30057
+ }
30058
+ const buf = Buffer.from(await blobRes.arrayBuffer());
30059
+ let imageBytes;
30060
+ const encrypted = metaSaysEncrypted(meta);
30061
+ if (encrypted) {
30062
+ if (!e2ee) {
30063
+ return { ok: false, error: "Encrypted attachments require E2EE keys on this bridge." };
30064
+ }
30065
+ const k = typeof meta?.k === "string" ? meta.k : "";
30066
+ const n = typeof meta?.n === "string" ? meta.n : "";
30067
+ if (!k || !n) {
30068
+ return { ok: false, error: "Invalid encrypted attachment metadata (missing key id or nonce)." };
30069
+ }
30070
+ const c = base64UrlEncode(buf);
30071
+ imageBytes = e2ee.decryptEnvelopeToBuffer({ k, n, c });
30072
+ } else {
30073
+ imageBytes = buf;
30074
+ }
30075
+ warnIfDecodedImageMagicUnexpected(imageBytes, mimeType, id.slice(0, 8));
30076
+ logDebug(
30077
+ `[Agent] Loaded prompt image ${id.slice(0, 8)}\u2026: ${imageBytes.length} bytes, mime=${mimeType}, ${encrypted ? "E2EE decrypted" : "plaintext from storage"}`
30078
+ );
30079
+ const dataBase64 = imageBytes.toString("base64");
30080
+ out.push({ mimeType, dataBase64 });
30081
+ }
30082
+ if (out.length !== wantedCount) {
30083
+ return {
30084
+ ok: false,
30085
+ error: `Expected ${wantedCount} image attachment(s) but only loaded ${out.length} (check attachment ids and empty rows).`
30086
+ };
30087
+ }
30088
+ return { ok: true, images: out };
30089
+ }
30090
+
29870
30091
  // src/agents/acp/send-prompt-to-agent.ts
29871
30092
  async function sendPromptToAgent(options) {
29872
30093
  const {
@@ -29884,10 +30105,49 @@ async function sendPromptToAgent(options) {
29884
30105
  sessionChangeSummaryFilePaths,
29885
30106
  cloudApiBaseUrl,
29886
30107
  getCloudAccessToken,
29887
- e2ee
30108
+ e2ee,
30109
+ attachments
29888
30110
  } = options;
29889
30111
  try {
29890
- const result = await handle.sendPrompt(promptText, {});
30112
+ let sendOpts = {};
30113
+ if (attachments && attachments.length > 0) {
30114
+ if (!sessionId || !cloudApiBaseUrl || !getCloudAccessToken) {
30115
+ sendResult2({
30116
+ type: "prompt_result",
30117
+ id: promptId,
30118
+ ...sessionId ? { sessionId } : {},
30119
+ ...runId ? { runId } : {},
30120
+ success: false,
30121
+ error: "Prompt includes images but the bridge is not configured with a cloud API URL and token to fetch them.",
30122
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
30123
+ ...augmentPromptResultAuthFields(agentType, "missing cloud for images download")
30124
+ });
30125
+ return;
30126
+ }
30127
+ const resolved = await fetchSessionAttachmentPayloadsForAgent({
30128
+ attachments,
30129
+ sessionId,
30130
+ cloudApiBaseUrl,
30131
+ getCloudAccessToken,
30132
+ e2ee,
30133
+ log: log2
30134
+ });
30135
+ if (!resolved.ok) {
30136
+ sendResult2({
30137
+ type: "prompt_result",
30138
+ id: promptId,
30139
+ ...sessionId ? { sessionId } : {},
30140
+ ...runId ? { runId } : {},
30141
+ success: false,
30142
+ error: resolved.error,
30143
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
30144
+ ...augmentPromptResultAuthFields(agentType, resolved.error)
30145
+ });
30146
+ return;
30147
+ }
30148
+ sendOpts = { images: resolved.images };
30149
+ }
30150
+ const result = await handle.sendPrompt(promptText, sendOpts);
29891
30151
  if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
29892
30152
  await collectTurnGitDiffFromPreTurnSnapshot({
29893
30153
  sessionId,
@@ -30308,14 +30568,19 @@ async function createCursorAcpClient(options) {
30308
30568
  });
30309
30569
  const established = await bootstrapAcpWireSession(transport, sessionCtx, {
30310
30570
  protocolVersion: 1,
30311
- clientCapabilities: { fs: { readTextFile: true, writeTextFile: true }, terminal: false },
30571
+ clientCapabilities: {
30572
+ fs: { readTextFile: true, writeTextFile: true },
30573
+ terminal: false,
30574
+ prompt: { image: true }
30575
+ },
30312
30576
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
30313
30577
  });
30314
30578
  const sessionId = established.sessionId;
30315
30579
  resolve16({
30316
30580
  sessionId,
30317
- async sendPrompt(prompt, _options) {
30318
- return sendAcpPromptViaTransport(transport, sessionCtx, sessionId, prompt);
30581
+ async sendPrompt(prompt, options2) {
30582
+ const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
30583
+ return sendAcpPromptViaTransport(transport, sessionCtx, sessionId, prompt, imgs);
30319
30584
  },
30320
30585
  async cancel() {
30321
30586
  cancelPendingPermissionRequests2();
@@ -30603,7 +30868,7 @@ function createBridgeOnFileChange(opts) {
30603
30868
 
30604
30869
  // src/agents/acp/hooks/bridge-on-request.ts
30605
30870
  function createBridgeOnRequest(opts) {
30606
- const { routing, getSendRequest, log: log2 } = opts;
30871
+ const { routing, getSendRequest, log: log2, getAutoApproveAcpPermissions, resolveAcpPermissionRequest } = opts;
30607
30872
  return (request) => {
30608
30873
  const runId = routing.runId;
30609
30874
  const sessionId = routing.sessionId;
@@ -30611,6 +30876,26 @@ function createBridgeOnRequest(opts) {
30611
30876
  if (!runId || !sendReq) return;
30612
30877
  const kind = request.method === "cursor/create_plan" ? "plan" : request.method === "session/request_permission" ? "permission" : "question";
30613
30878
  const sessionUpdate = request.method === "cursor/create_plan" ? "plan" : request.method === "session/request_permission" ? "permission" : "question";
30879
+ if (request.method === "session/request_permission" && getAutoApproveAcpPermissions() && resolveAcpPermissionRequest) {
30880
+ const params = request.params != null && typeof request.params === "object" && !Array.isArray(request.params) ? request.params : {};
30881
+ const auto = buildCliAutoApprovedPermissionRpcResult(params);
30882
+ if (auto != null) {
30883
+ try {
30884
+ resolveAcpPermissionRequest(request.requestId, auto);
30885
+ log2(
30886
+ `[Bridge service] CLI dangerous mode: auto-approved permission requestId=${request.requestId} runId=${runId}`
30887
+ );
30888
+ return;
30889
+ } catch (err) {
30890
+ log2(
30891
+ `[Bridge service] CLI dangerous mode: auto-approve failed (${errorMessage(err)}); forwarding permission`
30892
+ );
30893
+ }
30894
+ }
30895
+ log2(
30896
+ `[Bridge service] CLI dangerous mode: no allow option to auto-select; forwarding permission requestId=${request.requestId}`
30897
+ );
30898
+ }
30614
30899
  try {
30615
30900
  sendReq({
30616
30901
  type: "session_update",
@@ -31161,6 +31446,7 @@ async function ensureAcpClient(options) {
31161
31446
  sendRequest,
31162
31447
  log: log2
31163
31448
  } = options;
31449
+ state.latestAgentConfigForBridgeHooks = agentConfig != null && typeof agentConfig === "object" && !Array.isArray(agentConfig) ? agentConfig : null;
31164
31450
  const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
31165
31451
  if (state.acpStartPromise && !state.acpHandle) {
31166
31452
  await state.acpStartPromise;
@@ -31218,7 +31504,11 @@ async function ensureAcpClient(options) {
31218
31504
  sessionParentPath: targetSessionParentPath,
31219
31505
  getSendSessionUpdate: () => sendSessionUpdate,
31220
31506
  getSendRequest: () => sendRequest,
31221
- log: log2
31507
+ log: log2,
31508
+ getAutoApproveAcpPermissions: () => getCliPermissionModeFromAgentConfig(state.latestAgentConfigForBridgeHooks) === CLI_PERMISSION_MODE_DANGEROUS,
31509
+ resolveAcpPermissionRequest: (requestId, result) => {
31510
+ state.acpHandle?.resolveRequest?.(requestId, result);
31511
+ }
31222
31512
  });
31223
31513
  const persisted = cloudSessionId != null && cloudSessionId !== "" && preferredAgentType != null && preferredAgentType !== "" ? readLocalAgentSessionFile(cloudSessionId) : null;
31224
31514
  const persistedAcpSessionId = persisted && persisted.backendAgentType === preferredAgentType && typeof persisted.acpSessionId === "string" && persisted.acpSessionId.trim() !== "" ? persisted.acpSessionId.trim() : null;
@@ -31254,6 +31544,7 @@ async function ensureAcpClient(options) {
31254
31544
  state.acpStartPromise = null;
31255
31545
  state.acpAgentKey = null;
31256
31546
  state.activeSessionConfigOptions = null;
31547
+ state.latestAgentConfigForBridgeHooks = null;
31257
31548
  state.lastAcpStartError = "Agent subprocess exited";
31258
31549
  },
31259
31550
  ...hooks,
@@ -31284,7 +31575,8 @@ async function createAcpManager(options) {
31284
31575
  lastAcpStartError: null,
31285
31576
  lastAcpCwd: null,
31286
31577
  acpAgentKey: null,
31287
- activeSessionConfigOptions: null
31578
+ activeSessionConfigOptions: null,
31579
+ latestAgentConfigForBridgeHooks: null
31288
31580
  };
31289
31581
  let backendFallbackAgentType = null;
31290
31582
  const promptRouting = {};
@@ -31319,7 +31611,8 @@ async function createAcpManager(options) {
31319
31611
  sessionChangeSummaryFilePaths,
31320
31612
  cloudApiBaseUrl,
31321
31613
  getCloudAccessToken,
31322
- e2ee
31614
+ e2ee,
31615
+ attachments
31323
31616
  } = opts;
31324
31617
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
31325
31618
  pendingCancelRunId = void 0;
@@ -31390,7 +31683,8 @@ async function createAcpManager(options) {
31390
31683
  sessionChangeSummaryFilePaths,
31391
31684
  cloudApiBaseUrl,
31392
31685
  getCloudAccessToken,
31393
- e2ee
31686
+ e2ee,
31687
+ attachments
31394
31688
  });
31395
31689
  }
31396
31690
  void run().finally(() => {
@@ -31426,6 +31720,7 @@ async function createAcpManager(options) {
31426
31720
  state.acpStartPromise = null;
31427
31721
  state.acpAgentKey = null;
31428
31722
  state.activeSessionConfigOptions = null;
31723
+ state.latestAgentConfigForBridgeHooks = null;
31429
31724
  }
31430
31725
  return {
31431
31726
  setPreferredAgentType,
@@ -34593,7 +34888,8 @@ function dispatchLocalPrompt(next, deps) {
34593
34888
  ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
34594
34889
  ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
34595
34890
  ...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
34596
- ...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {}
34891
+ ...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
34892
+ ...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
34597
34893
  };
34598
34894
  handleBridgePrompt(msg, deps);
34599
34895
  }
@@ -34804,32 +35100,6 @@ function parseFollowUpFieldsFromPromptMessage(msg) {
34804
35100
  return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
34805
35101
  }
34806
35102
 
34807
- // ../e2ee/src/constants.ts
34808
- var E2EE_NONCE_BYTES = 12;
34809
-
34810
- // ../e2ee/src/types.ts
34811
- function isE2eeEnvelope(value) {
34812
- if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
34813
- const o = value;
34814
- return typeof o.k === "string" && typeof o.n === "string" && typeof o.c === "string";
34815
- }
34816
-
34817
- // ../e2ee/src/encoding.ts
34818
- function base64UrlEncode(bytes) {
34819
- let binary = "";
34820
- for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
34821
- const b64 = btoa(binary);
34822
- return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
34823
- }
34824
- function base64UrlDecode(value) {
34825
- const b64 = value.replace(/-/g, "+").replace(/_/g, "/");
34826
- const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
34827
- const binary = atob(padded);
34828
- const out = new Uint8Array(binary.length);
34829
- for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
34830
- return out;
34831
- }
34832
-
34833
35103
  // src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
34834
35104
  function decryptChangeSummaryFileInput(row, e2ee) {
34835
35105
  if (!e2ee) return row;
@@ -34886,15 +35156,30 @@ function resolveChangeSummaryPromptForAgent(params) {
34886
35156
  }
34887
35157
 
34888
35158
  // src/agents/acp/from-bridge/handle-bridge-prompt.ts
35159
+ function parseBridgeAttachments(msg) {
35160
+ const raw = msg.attachments;
35161
+ if (!Array.isArray(raw)) return [];
35162
+ const out = [];
35163
+ for (const x of raw) {
35164
+ if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
35165
+ const o = x;
35166
+ const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
35167
+ if (!id) continue;
35168
+ const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
35169
+ out.push({ attachmentId: id, mimeType: mt });
35170
+ }
35171
+ return out;
35172
+ }
34889
35173
  function handleBridgePrompt(msg, deps) {
34890
35174
  const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
34891
35175
  const rawPrompt = msg.prompt;
34892
35176
  const promptText = typeof rawPrompt === "string" ? rawPrompt : rawPrompt != null ? String(rawPrompt) : "";
35177
+ const attachments = parseBridgeAttachments(msg);
34893
35178
  const sessionId = msg.sessionId;
34894
35179
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
34895
35180
  const promptId = typeof msg.id === "string" ? msg.id : void 0;
34896
35181
  const { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate } = createBridgePromptSenders(deps, getWs);
34897
- if (!promptText.trim()) {
35182
+ if (!promptText.trim() && attachments.length === 0) {
34898
35183
  log2(
34899
35184
  `[Bridge service] Prompt ignored: empty or missing prompt text (session ${typeof msg.sessionId === "string" ? msg.sessionId.slice(0, 8) : "\u2014"}\u2026, run ${typeof msg.runId === "string" ? msg.runId.slice(0, 8) : "\u2014"}\u2026).`
34900
35185
  );
@@ -34961,7 +35246,8 @@ function handleBridgePrompt(msg, deps) {
34961
35246
  sessionChangeSummaryFilePaths: pathsForUpload,
34962
35247
  cloudApiBaseUrl: deps.cloudApiBaseUrl,
34963
35248
  getCloudAccessToken: deps.getCloudAccessToken,
34964
- e2ee: deps.e2ee
35249
+ e2ee: deps.e2ee,
35250
+ ...attachments.length > 0 ? { attachments } : {}
34965
35251
  });
34966
35252
  }
34967
35253
  void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
@@ -35931,6 +36217,17 @@ function createCliE2eeRuntime(key) {
35931
36217
  const merged = { ...message, ...parsed };
35932
36218
  delete merged.ee;
35933
36219
  return merged;
36220
+ },
36221
+ decryptEnvelopeToBuffer(envelope) {
36222
+ if (envelope.k !== key.id) throw new Error(`E2EE key mismatch: ${envelope.k}`);
36223
+ const sealed = Buffer.from(base64UrlDecode(envelope.c));
36224
+ if (sealed.length < 16) throw new Error("Invalid E2EE payload.");
36225
+ const ciphertext = sealed.subarray(0, sealed.length - 16);
36226
+ const tag = sealed.subarray(sealed.length - 16);
36227
+ const nonce = Buffer.from(base64UrlDecode(envelope.n));
36228
+ const decipher = createDecipheriv("aes-256-gcm", rawKey, nonce);
36229
+ decipher.setAuthTag(tag);
36230
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
35934
36231
  }
35935
36232
  };
35936
36233
  }