@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/cli.js CHANGED
@@ -25064,7 +25064,7 @@ var {
25064
25064
  } = import_index.default;
25065
25065
 
25066
25066
  // src/cli-version.ts
25067
- var CLI_VERSION = "0.1.25".length > 0 ? "0.1.25" : "0.0.0-dev";
25067
+ var CLI_VERSION = "0.1.27".length > 0 ? "0.1.27" : "0.0.0-dev";
25068
25068
 
25069
25069
  // src/cli/defaults.ts
25070
25070
  var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
@@ -25352,6 +25352,17 @@ function createCliE2eeRuntime(key) {
25352
25352
  const merged = { ...message, ...parsed };
25353
25353
  delete merged.ee;
25354
25354
  return merged;
25355
+ },
25356
+ decryptEnvelopeToBuffer(envelope) {
25357
+ if (envelope.k !== key.id) throw new Error(`E2EE key mismatch: ${envelope.k}`);
25358
+ const sealed = Buffer.from(base64UrlDecode(envelope.c));
25359
+ if (sealed.length < 16) throw new Error("Invalid E2EE payload.");
25360
+ const ciphertext = sealed.subarray(0, sealed.length - 16);
25361
+ const tag = sealed.subarray(sealed.length - 16);
25362
+ const nonce = Buffer.from(base64UrlDecode(envelope.n));
25363
+ const decipher = createDecipheriv("aes-256-gcm", rawKey, nonce);
25364
+ decipher.setAuthTag(tag);
25365
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
25355
25366
  }
25356
25367
  };
25357
25368
  }
@@ -27204,8 +27215,77 @@ function isClaudeCodePermissionMode(value) {
27204
27215
  return MODE_SET.has(value);
27205
27216
  }
27206
27217
 
27218
+ // ../types/src/cli-permission-mode.ts
27219
+ var CLI_PERMISSION_MODE_DEFAULT = "default";
27220
+ var CLI_PERMISSION_MODE_DANGEROUS = "dangerous";
27221
+ function normalizeCliPermissionModeInput(raw) {
27222
+ if (typeof raw !== "string") return CLI_PERMISSION_MODE_DEFAULT;
27223
+ const t = raw.trim();
27224
+ if (t === CLI_PERMISSION_MODE_DANGEROUS) return CLI_PERMISSION_MODE_DANGEROUS;
27225
+ if (t === "standard") return CLI_PERMISSION_MODE_DEFAULT;
27226
+ return CLI_PERMISSION_MODE_DEFAULT;
27227
+ }
27228
+
27229
+ // ../types/src/acp-permission-auto-approve.ts
27230
+ function isRejectKind(kind) {
27231
+ return kind === "reject_once" || kind === "reject_always";
27232
+ }
27233
+ function normalizeOptions(raw) {
27234
+ if (!Array.isArray(raw)) return [];
27235
+ const out = [];
27236
+ for (const item of raw) {
27237
+ if (item == null || typeof item !== "object" || Array.isArray(item)) continue;
27238
+ const o = item;
27239
+ const rawId = o.optionId ?? o.id;
27240
+ const optionId = typeof rawId === "string" && rawId.trim() !== "" ? rawId.trim() : typeof rawId === "number" && Number.isFinite(rawId) ? String(rawId) : "";
27241
+ if (!optionId) continue;
27242
+ const kind = typeof o.kind === "string" ? o.kind : void 0;
27243
+ out.push({ optionId, ...kind ? { kind } : {} });
27244
+ }
27245
+ return out;
27246
+ }
27247
+ function pickAllowOption(options) {
27248
+ const nonReject = options.filter((o) => !isRejectKind(o.kind));
27249
+ if (nonReject.length === 0) return null;
27250
+ const allowOnce = nonReject.find((o) => o.kind === "allow_once");
27251
+ if (allowOnce) return allowOnce;
27252
+ const notAlways = nonReject.filter((o) => o.kind !== "allow_always");
27253
+ if (notAlways.length > 0) return notAlways[0] ?? null;
27254
+ return nonReject.find((o) => o.kind === "allow_always") ?? nonReject[0] ?? null;
27255
+ }
27256
+ function firstNonEmptyOptionsArray(...candidates) {
27257
+ for (const c of candidates) {
27258
+ if (Array.isArray(c) && c.length > 0) return c;
27259
+ }
27260
+ const fallback = candidates[0];
27261
+ return Array.isArray(fallback) ? fallback : [];
27262
+ }
27263
+ function extractAcpPermissionRequestOptionArray(params) {
27264
+ const toolCall = params.toolCall;
27265
+ const fromToolCall = toolCall != null && typeof toolCall === "object" && !Array.isArray(toolCall) ? toolCall : null;
27266
+ return firstNonEmptyOptionsArray(
27267
+ params.options,
27268
+ params.permissionOptions,
27269
+ fromToolCall?.options,
27270
+ fromToolCall?.permissionOptions
27271
+ );
27272
+ }
27273
+ function buildCliAutoApprovedPermissionRpcResult(requestParams) {
27274
+ const opt = pickAllowOption(normalizeOptions(extractAcpPermissionRequestOptionArray(requestParams)));
27275
+ if (!opt) return null;
27276
+ const kind = opt.kind?.trim();
27277
+ return {
27278
+ outcome: {
27279
+ outcome: "selected",
27280
+ optionId: opt.optionId,
27281
+ ...kind ? { _meta: { permissionOptionKind: kind } } : {}
27282
+ }
27283
+ };
27284
+ }
27285
+
27207
27286
  // ../types/src/agent-config.ts
27208
27287
  var AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY = "claude_permission_mode";
27288
+ var AGENT_CONFIG_CLI_PERMISSION_MODE_KEY = "cli_permission_mode";
27209
27289
  function getClaudePermissionModeFromAgentConfig(config2) {
27210
27290
  if (!config2) return null;
27211
27291
  const raw = config2[AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY];
@@ -27213,6 +27293,10 @@ function getClaudePermissionModeFromAgentConfig(config2) {
27213
27293
  const t = raw.trim();
27214
27294
  return isClaudeCodePermissionMode(t) ? t : null;
27215
27295
  }
27296
+ function getCliPermissionModeFromAgentConfig(config2) {
27297
+ if (!config2) return CLI_PERMISSION_MODE_DEFAULT;
27298
+ return normalizeCliPermissionModeInput(config2[AGENT_CONFIG_CLI_PERMISSION_MODE_KEY]);
27299
+ }
27216
27300
 
27217
27301
  // src/git/session-git-queue.ts
27218
27302
  import { execFile as execFile7 } from "node:child_process";
@@ -32179,6 +32263,96 @@ function augmentPromptResultAuthFields(agentType, errorText) {
32179
32263
  return { agentAuthRequired: true, agentType };
32180
32264
  }
32181
32265
 
32266
+ // src/agents/acp/fetch-session-attachments.ts
32267
+ function metaSaysEncrypted(meta) {
32268
+ if (!meta) return false;
32269
+ const e = meta.encrypted;
32270
+ return e === true || e === "true" || e === 1;
32271
+ }
32272
+ function warnIfDecodedImageMagicUnexpected(buf, mimeType, idShort) {
32273
+ const m = mimeType.toLowerCase();
32274
+ if (!m.startsWith("image/") || buf.length < 4) return;
32275
+ const b0 = buf[0];
32276
+ const b1 = buf[1];
32277
+ const b2 = buf[2];
32278
+ const b3 = buf[3];
32279
+ let looksOk = false;
32280
+ if (m.includes("png") && b0 === 137 && b1 === 80 && b2 === 78 && b3 === 71) looksOk = true;
32281
+ else if ((m.includes("jpeg") || m.includes("jpg")) && b0 === 255 && b1 === 216 && b2 === 255) looksOk = true;
32282
+ else if (m.includes("gif") && b0 === 71 && b1 === 73 && b2 === 70) looksOk = true;
32283
+ else if (m.includes("webp") && buf.length >= 12 && buf.subarray(0, 4).toString("ascii") === "RIFF" && buf.subarray(8, 12).toString("ascii") === "WEBP")
32284
+ looksOk = true;
32285
+ else if (!/(png|jpe?g|gif|webp)/i.test(m)) looksOk = true;
32286
+ if (!looksOk) {
32287
+ logDebug(
32288
+ `[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")}`
32289
+ );
32290
+ }
32291
+ }
32292
+ async function fetchSessionAttachmentPayloadsForAgent(params) {
32293
+ const { attachments, sessionId, cloudApiBaseUrl, getCloudAccessToken, e2ee, log: log2 } = params;
32294
+ const token = getCloudAccessToken();
32295
+ if (!token) {
32296
+ return { ok: false, error: "Missing cloud access token; cannot download attachments." };
32297
+ }
32298
+ const wantedCount = attachments.filter((a) => typeof a.attachmentId === "string" && a.attachmentId.trim() !== "").length;
32299
+ if (wantedCount === 0) {
32300
+ return { ok: false, error: "No valid attachment ids in prompt." };
32301
+ }
32302
+ const base = cloudApiBaseUrl.replace(/\/+$/, "");
32303
+ const out = [];
32304
+ for (const a of attachments) {
32305
+ const id = typeof a.attachmentId === "string" ? a.attachmentId.trim() : "";
32306
+ if (!id) continue;
32307
+ const metaUrl = `${base}/api/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/meta`;
32308
+ const blobUrl = `${base}/api/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/blob`;
32309
+ const metaRes = await fetch(metaUrl, { headers: { Authorization: `Bearer ${token}` } });
32310
+ if (!metaRes.ok) {
32311
+ const t = await metaRes.text().catch(() => "");
32312
+ log2(`[Agent] Attachment meta fetch failed ${metaRes.status}: ${t.slice(0, 200)}`);
32313
+ return { ok: false, error: `Could not load attachment (${id.slice(0, 8)}\u2026): ${metaRes.status}` };
32314
+ }
32315
+ const meta = await metaRes.json().catch(() => null);
32316
+ const mimeType = typeof meta?.mimeType === "string" && meta.mimeType.trim() ? meta.mimeType.trim() : a.mimeType;
32317
+ const blobRes = await fetch(blobUrl, { headers: { Authorization: `Bearer ${token}` } });
32318
+ if (!blobRes.ok) {
32319
+ const t = await blobRes.text().catch(() => "");
32320
+ log2(`[Agent] Attachment blob fetch failed ${blobRes.status}: ${t.slice(0, 200)}`);
32321
+ return { ok: false, error: `Could not load attachment data (${id.slice(0, 8)}\u2026): ${blobRes.status}` };
32322
+ }
32323
+ const buf = Buffer.from(await blobRes.arrayBuffer());
32324
+ let imageBytes;
32325
+ const encrypted = metaSaysEncrypted(meta);
32326
+ if (encrypted) {
32327
+ if (!e2ee) {
32328
+ return { ok: false, error: "Encrypted attachments require E2EE keys on this bridge." };
32329
+ }
32330
+ const k = typeof meta?.k === "string" ? meta.k : "";
32331
+ const n = typeof meta?.n === "string" ? meta.n : "";
32332
+ if (!k || !n) {
32333
+ return { ok: false, error: "Invalid encrypted attachment metadata (missing key id or nonce)." };
32334
+ }
32335
+ const c = base64UrlEncode(buf);
32336
+ imageBytes = e2ee.decryptEnvelopeToBuffer({ k, n, c });
32337
+ } else {
32338
+ imageBytes = buf;
32339
+ }
32340
+ warnIfDecodedImageMagicUnexpected(imageBytes, mimeType, id.slice(0, 8));
32341
+ logDebug(
32342
+ `[Agent] Loaded prompt image ${id.slice(0, 8)}\u2026: ${imageBytes.length} bytes, mime=${mimeType}, ${encrypted ? "E2EE decrypted" : "plaintext from storage"}`
32343
+ );
32344
+ const dataBase64 = imageBytes.toString("base64");
32345
+ out.push({ mimeType, dataBase64 });
32346
+ }
32347
+ if (out.length !== wantedCount) {
32348
+ return {
32349
+ ok: false,
32350
+ error: `Expected ${wantedCount} image attachment(s) but only loaded ${out.length} (check attachment ids and empty rows).`
32351
+ };
32352
+ }
32353
+ return { ok: true, images: out };
32354
+ }
32355
+
32182
32356
  // src/agents/acp/send-prompt-to-agent.ts
32183
32357
  async function sendPromptToAgent(options) {
32184
32358
  const {
@@ -32196,10 +32370,49 @@ async function sendPromptToAgent(options) {
32196
32370
  sessionChangeSummaryFilePaths,
32197
32371
  cloudApiBaseUrl,
32198
32372
  getCloudAccessToken,
32199
- e2ee
32373
+ e2ee,
32374
+ attachments
32200
32375
  } = options;
32201
32376
  try {
32202
- const result = await handle.sendPrompt(promptText, {});
32377
+ let sendOpts = {};
32378
+ if (attachments && attachments.length > 0) {
32379
+ if (!sessionId || !cloudApiBaseUrl || !getCloudAccessToken) {
32380
+ sendResult2({
32381
+ type: "prompt_result",
32382
+ id: promptId,
32383
+ ...sessionId ? { sessionId } : {},
32384
+ ...runId ? { runId } : {},
32385
+ success: false,
32386
+ error: "Prompt includes images but the bridge is not configured with a cloud API URL and token to fetch them.",
32387
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
32388
+ ...augmentPromptResultAuthFields(agentType, "missing cloud for images download")
32389
+ });
32390
+ return;
32391
+ }
32392
+ const resolved = await fetchSessionAttachmentPayloadsForAgent({
32393
+ attachments,
32394
+ sessionId,
32395
+ cloudApiBaseUrl,
32396
+ getCloudAccessToken,
32397
+ e2ee,
32398
+ log: log2
32399
+ });
32400
+ if (!resolved.ok) {
32401
+ sendResult2({
32402
+ type: "prompt_result",
32403
+ id: promptId,
32404
+ ...sessionId ? { sessionId } : {},
32405
+ ...runId ? { runId } : {},
32406
+ success: false,
32407
+ error: resolved.error,
32408
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
32409
+ ...augmentPromptResultAuthFields(agentType, resolved.error)
32410
+ });
32411
+ return;
32412
+ }
32413
+ sendOpts = { images: resolved.images };
32414
+ }
32415
+ const result = await handle.sendPrompt(promptText, sendOpts);
32203
32416
  if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
32204
32417
  await collectTurnGitDiffFromPreTurnSnapshot({
32205
32418
  sessionId,
@@ -32641,13 +32854,16 @@ function parseAcpInitAgentCapabilities(initResult) {
32641
32854
  const canLoad = agentCapabilities?.loadSession === true;
32642
32855
  const sessionCaps = agentCapabilities?.sessionCapabilities;
32643
32856
  const canResume = Boolean(sessionCaps?.resume);
32644
- return { canResume, canLoad };
32857
+ const promptCaps = agentCapabilities?.promptCapabilities;
32858
+ const promptSupportsImage = promptCaps?.image === true;
32859
+ return { canResume, canLoad, promptSupportsImage };
32645
32860
  }
32646
32861
 
32647
32862
  // src/agents/acp/clients/shared/bootstrap-acp-wire-session.ts
32648
32863
  async function bootstrapAcpWireSession(transport, ctx, initializeRequest) {
32649
32864
  const initResult = await transport.initialize(initializeRequest);
32650
- const { canResume, canLoad } = parseAcpInitAgentCapabilities(initResult);
32865
+ const { canResume, canLoad, promptSupportsImage } = parseAcpInitAgentCapabilities(initResult);
32866
+ ctx.agentPromptImageSupported = promptSupportsImage;
32651
32867
  await transport.afterInitialize?.();
32652
32868
  const established = await establishAcpSessionWithTransport(transport, ctx, canResume, canLoad);
32653
32869
  const sessionId = established.sessionId;
@@ -32738,11 +32954,27 @@ function normalizeAcpPromptTurnFailure(err, stderrCaptureText) {
32738
32954
  }
32739
32955
 
32740
32956
  // src/agents/acp/clients/shared/send-acp-prompt-via-transport.ts
32741
- async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText) {
32957
+ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText, images) {
32958
+ if (images && images.length > 0 && !ctx.agentPromptImageSupported) {
32959
+ await new Promise((r) => setImmediate(r));
32960
+ return normalizeAcpPromptTurnFailure(
32961
+ new Error(
32962
+ "This agent does not advertise image support in ACP (missing agentCapabilities.promptCapabilities.image). Remove images or use an agent that supports prompt images."
32963
+ ),
32964
+ ctx.getStderrText()
32965
+ );
32966
+ }
32967
+ const textBlock = promptText.trim() !== "" ? promptText : images?.length ? " " : "";
32968
+ const prompt = [{ type: "text", text: textBlock }];
32969
+ if (images && images.length > 0) {
32970
+ for (const im of images) {
32971
+ prompt.push({ type: "image", mimeType: im.mimeType, data: im.data });
32972
+ }
32973
+ }
32742
32974
  try {
32743
32975
  const response = await transport.prompt({
32744
32976
  sessionId,
32745
- prompt: [{ type: "text", text: promptText }]
32977
+ prompt
32746
32978
  });
32747
32979
  await new Promise((r2) => setImmediate(r2));
32748
32980
  const r = response;
@@ -32758,6 +32990,22 @@ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText)
32758
32990
  }
32759
32991
  }
32760
32992
 
32993
+ // src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
32994
+ function awaitSdkStdioPermissionRequestHandshake(params) {
32995
+ const { requestId, paramsRecord, pending, onRequest } = params;
32996
+ return new Promise((resolve18) => {
32997
+ pending.set(requestId, { resolve: resolve18, params: paramsRecord });
32998
+ try {
32999
+ onRequest?.({
33000
+ requestId,
33001
+ method: "session/request_permission",
33002
+ params: paramsRecord
33003
+ });
33004
+ } catch {
33005
+ }
33006
+ });
33007
+ }
33008
+
32761
33009
  // src/agents/acp/clients/sdk/sdk-acp-session-transport.ts
32762
33010
  function createSdkAcpSessionTransport(connection) {
32763
33011
  const c = connection;
@@ -32868,16 +33116,11 @@ async function createSdkStdioAcpClient(options) {
32868
33116
  async requestPermission(params) {
32869
33117
  const requestId = `perm-${++permissionSeq}`;
32870
33118
  const paramsRecord = params != null && typeof params === "object" ? params : {};
32871
- try {
32872
- onRequest?.({
32873
- requestId,
32874
- method: "session/request_permission",
32875
- params: paramsRecord
32876
- });
32877
- } catch {
32878
- }
32879
- return await new Promise((resolve19) => {
32880
- pendingPermissionReplies.set(requestId, { resolve: resolve19, params: paramsRecord });
33119
+ return await awaitSdkStdioPermissionRequestHandshake({
33120
+ requestId,
33121
+ paramsRecord,
33122
+ pending: pendingPermissionReplies,
33123
+ onRequest
32881
33124
  });
32882
33125
  },
32883
33126
  async readTextFile(params) {
@@ -32907,15 +33150,17 @@ async function createSdkStdioAcpClient(options) {
32907
33150
  const established = await bootstrapAcpWireSession(transport, sessionCtx, {
32908
33151
  protocolVersion: PROTOCOL_VERSION2,
32909
33152
  clientCapabilities: {
32910
- fs: { readTextFile: true, writeTextFile: true }
33153
+ fs: { readTextFile: true, writeTextFile: true },
33154
+ prompt: { image: true }
32911
33155
  },
32912
33156
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
32913
33157
  });
32914
33158
  const sessionId = established.sessionId;
32915
33159
  settleResolve({
32916
33160
  sessionId,
32917
- async sendPrompt(prompt, _options) {
32918
- return sendAcpPromptViaTransport(transport, sessionCtx, sessionId, prompt);
33161
+ async sendPrompt(prompt, options2) {
33162
+ const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
33163
+ return sendAcpPromptViaTransport(transport, sessionCtx, sessionId, prompt, imgs);
32919
33164
  },
32920
33165
  async cancel() {
32921
33166
  for (const [id, entry] of [...pendingPermissionReplies.entries()]) {
@@ -33283,14 +33528,19 @@ async function createCursorAcpClient(options) {
33283
33528
  });
33284
33529
  const established = await bootstrapAcpWireSession(transport, sessionCtx, {
33285
33530
  protocolVersion: 1,
33286
- clientCapabilities: { fs: { readTextFile: true, writeTextFile: true }, terminal: false },
33531
+ clientCapabilities: {
33532
+ fs: { readTextFile: true, writeTextFile: true },
33533
+ terminal: false,
33534
+ prompt: { image: true }
33535
+ },
33287
33536
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
33288
33537
  });
33289
33538
  const sessionId = established.sessionId;
33290
33539
  resolve18({
33291
33540
  sessionId,
33292
- async sendPrompt(prompt, _options) {
33293
- return sendAcpPromptViaTransport(transport, sessionCtx, sessionId, prompt);
33541
+ async sendPrompt(prompt, options2) {
33542
+ const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
33543
+ return sendAcpPromptViaTransport(transport, sessionCtx, sessionId, prompt, imgs);
33294
33544
  },
33295
33545
  async cancel() {
33296
33546
  cancelPendingPermissionRequests2();
@@ -33578,7 +33828,7 @@ function createBridgeOnFileChange(opts) {
33578
33828
 
33579
33829
  // src/agents/acp/hooks/bridge-on-request.ts
33580
33830
  function createBridgeOnRequest(opts) {
33581
- const { routing, getSendRequest, log: log2 } = opts;
33831
+ const { routing, getSendRequest, log: log2, getAutoApproveAcpPermissions, resolveAcpPermissionRequest } = opts;
33582
33832
  return (request) => {
33583
33833
  const runId = routing.runId;
33584
33834
  const sessionId = routing.sessionId;
@@ -33586,6 +33836,26 @@ function createBridgeOnRequest(opts) {
33586
33836
  if (!runId || !sendReq) return;
33587
33837
  const kind = request.method === "cursor/create_plan" ? "plan" : request.method === "session/request_permission" ? "permission" : "question";
33588
33838
  const sessionUpdate = request.method === "cursor/create_plan" ? "plan" : request.method === "session/request_permission" ? "permission" : "question";
33839
+ if (request.method === "session/request_permission" && getAutoApproveAcpPermissions() && resolveAcpPermissionRequest) {
33840
+ const params = request.params != null && typeof request.params === "object" && !Array.isArray(request.params) ? request.params : {};
33841
+ const auto = buildCliAutoApprovedPermissionRpcResult(params);
33842
+ if (auto != null) {
33843
+ try {
33844
+ resolveAcpPermissionRequest(request.requestId, auto);
33845
+ log2(
33846
+ `[Bridge service] CLI dangerous mode: auto-approved permission requestId=${request.requestId} runId=${runId}`
33847
+ );
33848
+ return;
33849
+ } catch (err) {
33850
+ log2(
33851
+ `[Bridge service] CLI dangerous mode: auto-approve failed (${errorMessage(err)}); forwarding permission`
33852
+ );
33853
+ }
33854
+ }
33855
+ log2(
33856
+ `[Bridge service] CLI dangerous mode: no allow option to auto-select; forwarding permission requestId=${request.requestId}`
33857
+ );
33858
+ }
33589
33859
  try {
33590
33860
  sendReq({
33591
33861
  type: "session_update",
@@ -34136,6 +34406,7 @@ async function ensureAcpClient(options) {
34136
34406
  sendRequest,
34137
34407
  log: log2
34138
34408
  } = options;
34409
+ state.latestAgentConfigForBridgeHooks = agentConfig != null && typeof agentConfig === "object" && !Array.isArray(agentConfig) ? agentConfig : null;
34139
34410
  const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
34140
34411
  if (state.acpStartPromise && !state.acpHandle) {
34141
34412
  await state.acpStartPromise;
@@ -34193,7 +34464,11 @@ async function ensureAcpClient(options) {
34193
34464
  sessionParentPath: targetSessionParentPath,
34194
34465
  getSendSessionUpdate: () => sendSessionUpdate,
34195
34466
  getSendRequest: () => sendRequest,
34196
- log: log2
34467
+ log: log2,
34468
+ getAutoApproveAcpPermissions: () => getCliPermissionModeFromAgentConfig(state.latestAgentConfigForBridgeHooks) === CLI_PERMISSION_MODE_DANGEROUS,
34469
+ resolveAcpPermissionRequest: (requestId, result) => {
34470
+ state.acpHandle?.resolveRequest?.(requestId, result);
34471
+ }
34197
34472
  });
34198
34473
  const persisted = cloudSessionId != null && cloudSessionId !== "" && preferredAgentType != null && preferredAgentType !== "" ? readLocalAgentSessionFile(cloudSessionId) : null;
34199
34474
  const persistedAcpSessionId = persisted && persisted.backendAgentType === preferredAgentType && typeof persisted.acpSessionId === "string" && persisted.acpSessionId.trim() !== "" ? persisted.acpSessionId.trim() : null;
@@ -34229,6 +34504,7 @@ async function ensureAcpClient(options) {
34229
34504
  state.acpStartPromise = null;
34230
34505
  state.acpAgentKey = null;
34231
34506
  state.activeSessionConfigOptions = null;
34507
+ state.latestAgentConfigForBridgeHooks = null;
34232
34508
  state.lastAcpStartError = "Agent subprocess exited";
34233
34509
  },
34234
34510
  ...hooks,
@@ -34259,7 +34535,8 @@ async function createAcpManager(options) {
34259
34535
  lastAcpStartError: null,
34260
34536
  lastAcpCwd: null,
34261
34537
  acpAgentKey: null,
34262
- activeSessionConfigOptions: null
34538
+ activeSessionConfigOptions: null,
34539
+ latestAgentConfigForBridgeHooks: null
34263
34540
  };
34264
34541
  let backendFallbackAgentType = null;
34265
34542
  const promptRouting = {};
@@ -34294,7 +34571,8 @@ async function createAcpManager(options) {
34294
34571
  sessionChangeSummaryFilePaths,
34295
34572
  cloudApiBaseUrl,
34296
34573
  getCloudAccessToken,
34297
- e2ee
34574
+ e2ee,
34575
+ attachments
34298
34576
  } = opts;
34299
34577
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
34300
34578
  pendingCancelRunId = void 0;
@@ -34365,7 +34643,8 @@ async function createAcpManager(options) {
34365
34643
  sessionChangeSummaryFilePaths,
34366
34644
  cloudApiBaseUrl,
34367
34645
  getCloudAccessToken,
34368
- e2ee
34646
+ e2ee,
34647
+ attachments
34369
34648
  });
34370
34649
  }
34371
34650
  void run().finally(() => {
@@ -34401,6 +34680,7 @@ async function createAcpManager(options) {
34401
34680
  state.acpStartPromise = null;
34402
34681
  state.acpAgentKey = null;
34403
34682
  state.activeSessionConfigOptions = null;
34683
+ state.latestAgentConfigForBridgeHooks = null;
34404
34684
  }
34405
34685
  return {
34406
34686
  setPreferredAgentType,
@@ -37736,7 +38016,8 @@ function dispatchLocalPrompt(next, deps) {
37736
38016
  ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
37737
38017
  ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
37738
38018
  ...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
37739
- ...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {}
38019
+ ...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
38020
+ ...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
37740
38021
  };
37741
38022
  handleBridgePrompt(msg, deps);
37742
38023
  }
@@ -38003,15 +38284,30 @@ function resolveChangeSummaryPromptForAgent(params) {
38003
38284
  }
38004
38285
 
38005
38286
  // src/agents/acp/from-bridge/handle-bridge-prompt.ts
38287
+ function parseBridgeAttachments(msg) {
38288
+ const raw = msg.attachments;
38289
+ if (!Array.isArray(raw)) return [];
38290
+ const out = [];
38291
+ for (const x of raw) {
38292
+ if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
38293
+ const o = x;
38294
+ const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
38295
+ if (!id) continue;
38296
+ const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
38297
+ out.push({ attachmentId: id, mimeType: mt });
38298
+ }
38299
+ return out;
38300
+ }
38006
38301
  function handleBridgePrompt(msg, deps) {
38007
38302
  const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
38008
38303
  const rawPrompt = msg.prompt;
38009
38304
  const promptText = typeof rawPrompt === "string" ? rawPrompt : rawPrompt != null ? String(rawPrompt) : "";
38305
+ const attachments = parseBridgeAttachments(msg);
38010
38306
  const sessionId = msg.sessionId;
38011
38307
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
38012
38308
  const promptId = typeof msg.id === "string" ? msg.id : void 0;
38013
38309
  const { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate } = createBridgePromptSenders(deps, getWs);
38014
- if (!promptText.trim()) {
38310
+ if (!promptText.trim() && attachments.length === 0) {
38015
38311
  log2(
38016
38312
  `[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).`
38017
38313
  );
@@ -38078,7 +38374,8 @@ function handleBridgePrompt(msg, deps) {
38078
38374
  sessionChangeSummaryFilePaths: pathsForUpload,
38079
38375
  cloudApiBaseUrl: deps.cloudApiBaseUrl,
38080
38376
  getCloudAccessToken: deps.getCloudAccessToken,
38081
- e2ee: deps.e2ee
38377
+ e2ee: deps.e2ee,
38378
+ ...attachments.length > 0 ? { attachments } : {}
38082
38379
  });
38083
38380
  }
38084
38381
  void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {