@buildautomaton/cli 0.1.26 → 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
@@ -23128,13 +23128,16 @@ function parseAcpInitAgentCapabilities(initResult) {
23128
23128
  const canLoad = agentCapabilities?.loadSession === true;
23129
23129
  const sessionCaps = agentCapabilities?.sessionCapabilities;
23130
23130
  const canResume = Boolean(sessionCaps?.resume);
23131
- return { canResume, canLoad };
23131
+ const promptCaps = agentCapabilities?.promptCapabilities;
23132
+ const promptSupportsImage = promptCaps?.image === true;
23133
+ return { canResume, canLoad, promptSupportsImage };
23132
23134
  }
23133
23135
 
23134
23136
  // src/agents/acp/clients/shared/bootstrap-acp-wire-session.ts
23135
23137
  async function bootstrapAcpWireSession(transport, ctx, initializeRequest) {
23136
23138
  const initResult = await transport.initialize(initializeRequest);
23137
- const { canResume, canLoad } = parseAcpInitAgentCapabilities(initResult);
23139
+ const { canResume, canLoad, promptSupportsImage } = parseAcpInitAgentCapabilities(initResult);
23140
+ ctx.agentPromptImageSupported = promptSupportsImage;
23138
23141
  await transport.afterInitialize?.();
23139
23142
  const established = await establishAcpSessionWithTransport(transport, ctx, canResume, canLoad);
23140
23143
  const sessionId = established.sessionId;
@@ -23225,11 +23228,27 @@ function normalizeAcpPromptTurnFailure(err, stderrCaptureText) {
23225
23228
  }
23226
23229
 
23227
23230
  // src/agents/acp/clients/shared/send-acp-prompt-via-transport.ts
23228
- 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
+ }
23229
23248
  try {
23230
23249
  const response = await transport.prompt({
23231
23250
  sessionId,
23232
- prompt: [{ type: "text", text: promptText }]
23251
+ prompt
23233
23252
  });
23234
23253
  await new Promise((r2) => setImmediate(r2));
23235
23254
  const r = response;
@@ -23405,15 +23424,17 @@ async function createSdkStdioAcpClient(options) {
23405
23424
  const established = await bootstrapAcpWireSession(transport, sessionCtx, {
23406
23425
  protocolVersion: PROTOCOL_VERSION2,
23407
23426
  clientCapabilities: {
23408
- fs: { readTextFile: true, writeTextFile: true }
23427
+ fs: { readTextFile: true, writeTextFile: true },
23428
+ prompt: { image: true }
23409
23429
  },
23410
23430
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
23411
23431
  });
23412
23432
  const sessionId = established.sessionId;
23413
23433
  settleResolve({
23414
23434
  sessionId,
23415
- async sendPrompt(prompt, _options) {
23416
- 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);
23417
23438
  },
23418
23439
  async cancel() {
23419
23440
  for (const [id, entry] of [...pendingPermissionReplies.entries()]) {
@@ -23875,7 +23896,7 @@ function installBridgeProcessResilience() {
23875
23896
  }
23876
23897
 
23877
23898
  // src/cli-version.ts
23878
- var CLI_VERSION = "0.1.26".length > 0 ? "0.1.26" : "0.0.0-dev";
23899
+ var CLI_VERSION = "0.1.27".length > 0 ? "0.1.27" : "0.0.0-dev";
23879
23900
 
23880
23901
  // ../../node_modules/.pnpm/open@10.2.0/node_modules/open/index.js
23881
23902
  import process7 from "node:process";
@@ -29951,6 +29972,122 @@ function augmentPromptResultAuthFields(agentType, errorText) {
29951
29972
  return { agentAuthRequired: true, agentType };
29952
29973
  }
29953
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
+
29954
30091
  // src/agents/acp/send-prompt-to-agent.ts
29955
30092
  async function sendPromptToAgent(options) {
29956
30093
  const {
@@ -29968,10 +30105,49 @@ async function sendPromptToAgent(options) {
29968
30105
  sessionChangeSummaryFilePaths,
29969
30106
  cloudApiBaseUrl,
29970
30107
  getCloudAccessToken,
29971
- e2ee
30108
+ e2ee,
30109
+ attachments
29972
30110
  } = options;
29973
30111
  try {
29974
- 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);
29975
30151
  if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
29976
30152
  await collectTurnGitDiffFromPreTurnSnapshot({
29977
30153
  sessionId,
@@ -30392,14 +30568,19 @@ async function createCursorAcpClient(options) {
30392
30568
  });
30393
30569
  const established = await bootstrapAcpWireSession(transport, sessionCtx, {
30394
30570
  protocolVersion: 1,
30395
- clientCapabilities: { fs: { readTextFile: true, writeTextFile: true }, terminal: false },
30571
+ clientCapabilities: {
30572
+ fs: { readTextFile: true, writeTextFile: true },
30573
+ terminal: false,
30574
+ prompt: { image: true }
30575
+ },
30396
30576
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
30397
30577
  });
30398
30578
  const sessionId = established.sessionId;
30399
30579
  resolve16({
30400
30580
  sessionId,
30401
- async sendPrompt(prompt, _options) {
30402
- 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);
30403
30584
  },
30404
30585
  async cancel() {
30405
30586
  cancelPendingPermissionRequests2();
@@ -31430,7 +31611,8 @@ async function createAcpManager(options) {
31430
31611
  sessionChangeSummaryFilePaths,
31431
31612
  cloudApiBaseUrl,
31432
31613
  getCloudAccessToken,
31433
- e2ee
31614
+ e2ee,
31615
+ attachments
31434
31616
  } = opts;
31435
31617
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
31436
31618
  pendingCancelRunId = void 0;
@@ -31501,7 +31683,8 @@ async function createAcpManager(options) {
31501
31683
  sessionChangeSummaryFilePaths,
31502
31684
  cloudApiBaseUrl,
31503
31685
  getCloudAccessToken,
31504
- e2ee
31686
+ e2ee,
31687
+ attachments
31505
31688
  });
31506
31689
  }
31507
31690
  void run().finally(() => {
@@ -34705,7 +34888,8 @@ function dispatchLocalPrompt(next, deps) {
34705
34888
  ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
34706
34889
  ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
34707
34890
  ...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
34708
- ...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 } : {}
34709
34893
  };
34710
34894
  handleBridgePrompt(msg, deps);
34711
34895
  }
@@ -34916,32 +35100,6 @@ function parseFollowUpFieldsFromPromptMessage(msg) {
34916
35100
  return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
34917
35101
  }
34918
35102
 
34919
- // ../e2ee/src/constants.ts
34920
- var E2EE_NONCE_BYTES = 12;
34921
-
34922
- // ../e2ee/src/types.ts
34923
- function isE2eeEnvelope(value) {
34924
- if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
34925
- const o = value;
34926
- return typeof o.k === "string" && typeof o.n === "string" && typeof o.c === "string";
34927
- }
34928
-
34929
- // ../e2ee/src/encoding.ts
34930
- function base64UrlEncode(bytes) {
34931
- let binary = "";
34932
- for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
34933
- const b64 = btoa(binary);
34934
- return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
34935
- }
34936
- function base64UrlDecode(value) {
34937
- const b64 = value.replace(/-/g, "+").replace(/_/g, "/");
34938
- const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
34939
- const binary = atob(padded);
34940
- const out = new Uint8Array(binary.length);
34941
- for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
34942
- return out;
34943
- }
34944
-
34945
35103
  // src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
34946
35104
  function decryptChangeSummaryFileInput(row, e2ee) {
34947
35105
  if (!e2ee) return row;
@@ -34998,15 +35156,30 @@ function resolveChangeSummaryPromptForAgent(params) {
34998
35156
  }
34999
35157
 
35000
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
+ }
35001
35173
  function handleBridgePrompt(msg, deps) {
35002
35174
  const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
35003
35175
  const rawPrompt = msg.prompt;
35004
35176
  const promptText = typeof rawPrompt === "string" ? rawPrompt : rawPrompt != null ? String(rawPrompt) : "";
35177
+ const attachments = parseBridgeAttachments(msg);
35005
35178
  const sessionId = msg.sessionId;
35006
35179
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
35007
35180
  const promptId = typeof msg.id === "string" ? msg.id : void 0;
35008
35181
  const { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate } = createBridgePromptSenders(deps, getWs);
35009
- if (!promptText.trim()) {
35182
+ if (!promptText.trim() && attachments.length === 0) {
35010
35183
  log2(
35011
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).`
35012
35185
  );
@@ -35073,7 +35246,8 @@ function handleBridgePrompt(msg, deps) {
35073
35246
  sessionChangeSummaryFilePaths: pathsForUpload,
35074
35247
  cloudApiBaseUrl: deps.cloudApiBaseUrl,
35075
35248
  getCloudAccessToken: deps.getCloudAccessToken,
35076
- e2ee: deps.e2ee
35249
+ e2ee: deps.e2ee,
35250
+ ...attachments.length > 0 ? { attachments } : {}
35077
35251
  });
35078
35252
  }
35079
35253
  void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
@@ -36043,6 +36217,17 @@ function createCliE2eeRuntime(key) {
36043
36217
  const merged = { ...message, ...parsed };
36044
36218
  delete merged.ee;
36045
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()]);
36046
36231
  }
36047
36232
  };
36048
36233
  }