@buildautomaton/cli 0.1.16 → 0.1.18

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
@@ -28776,6 +28776,498 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
28776
28776
  }
28777
28777
  }
28778
28778
 
28779
+ // ../types/dist/index.js
28780
+ init_zod();
28781
+ init_zod();
28782
+ init_zod();
28783
+ init_zod();
28784
+ init_zod();
28785
+ init_zod();
28786
+ init_zod();
28787
+ init_zod();
28788
+ init_zod();
28789
+ init_zod();
28790
+ init_zod();
28791
+ init_zod();
28792
+ var WorkItemStatusSchema = external_exports.enum(["backlog", "in-progress", "completed"]);
28793
+ var WorkItemProgressSchema = external_exports.object({
28794
+ remainingCriteria: external_exports.array(external_exports.string()).default([]),
28795
+ openQuestions: external_exports.array(external_exports.string()).default([]),
28796
+ assignedTo: external_exports.enum(["agent", "human-product", "human-expert"]).optional()
28797
+ });
28798
+ var ChangeSchema = external_exports.object({
28799
+ id: external_exports.string(),
28800
+ description: external_exports.string(),
28801
+ buildingBlockId: external_exports.string(),
28802
+ buildingBlockType: external_exports.enum(["function", "workflow", "connector", "ui-component", "app-fragment", "application", "project"]),
28803
+ action: external_exports.enum(["create", "update", "split", "combine"])
28804
+ });
28805
+ var CompletionCriterionSchema = external_exports.object({
28806
+ id: external_exports.string(),
28807
+ description: external_exports.string(),
28808
+ type: external_exports.enum(["write-code", "write-tests", "verify-tests", "other"]),
28809
+ verified: external_exports.boolean().default(false)
28810
+ });
28811
+ var WorkItemPrioritySchema = external_exports.enum(["low", "medium", "high", "critical"]);
28812
+ var IterationPhaseSchema = external_exports.enum(["analysis", "implementation", "verify", "reprioritize", "completed"]);
28813
+ var WorkItemDependencySchema = external_exports.object({
28814
+ type: external_exports.enum(["work-item"]),
28815
+ id: external_exports.string()
28816
+ });
28817
+ var WorkItemSchema = external_exports.object({
28818
+ id: external_exports.string(),
28819
+ sessionId: external_exports.string().optional(),
28820
+ summary: external_exports.string().optional(),
28821
+ description: external_exports.string(),
28822
+ status: WorkItemStatusSchema,
28823
+ buildingBlockId: external_exports.string().optional(),
28824
+ buildingBlockType: external_exports.enum(["function", "workflow", "connector", "ui-component", "app-fragment", "application", "project"]),
28825
+ changes: external_exports.array(ChangeSchema).default([]),
28826
+ completionCriteria: external_exports.array(CompletionCriterionSchema).default([]),
28827
+ priority: WorkItemPrioritySchema.default("medium"),
28828
+ dependencies: external_exports.array(WorkItemDependencySchema).default([]),
28829
+ assignedToUserId: external_exports.string().optional()
28830
+ });
28831
+ var UserWorkspaceProfileSchema = external_exports.object({
28832
+ id: external_exports.string(),
28833
+ workspaceId: external_exports.string(),
28834
+ userId: external_exports.string(),
28835
+ roleDescription: external_exports.string().optional(),
28836
+ expertiseAreas: external_exports.array(external_exports.string()),
28837
+ preferences: external_exports.record(external_exports.unknown()).optional(),
28838
+ learnings: external_exports.array(external_exports.string())
28839
+ });
28840
+ var WorkspaceOwnerInfoSchema = external_exports.object({
28841
+ ownerId: external_exports.string(),
28842
+ ownerName: external_exports.string().optional(),
28843
+ ownerEmail: external_exports.string().optional(),
28844
+ ownerProfilePictureUrl: external_exports.string().optional()
28845
+ });
28846
+ var WorkspaceRuntimeEntrySchema = external_exports.object({
28847
+ workspaceId: external_exports.string(),
28848
+ path: external_exports.string(),
28849
+ name: external_exports.string().optional(),
28850
+ owner: WorkspaceOwnerInfoSchema.optional(),
28851
+ isOwner: external_exports.boolean().optional()
28852
+ });
28853
+ var ProjectContextSchema = external_exports.object({
28854
+ projectId: external_exports.string(),
28855
+ context: external_exports.record(external_exports.unknown()).default({}),
28856
+ updatedAt: external_exports.string()
28857
+ });
28858
+ var WebSocketMessageTypeSchema = external_exports.enum([
28859
+ "plan-update",
28860
+ "work-item-update",
28861
+ "work-item-added",
28862
+ "work-item-removed",
28863
+ "project-processing-start",
28864
+ "project-processing-update",
28865
+ "project-processing-complete",
28866
+ "project-processing-error",
28867
+ "file-tool-request",
28868
+ "file-tool-response",
28869
+ "file-generated"
28870
+ ]);
28871
+ var WebSocketMessageSchema = external_exports.object({
28872
+ type: WebSocketMessageTypeSchema,
28873
+ contextId: external_exports.string().optional(),
28874
+ data: external_exports.any().optional(),
28875
+ error: external_exports.string().optional()
28876
+ });
28877
+ var CheckpointKindSchema = external_exports.enum(["daily", "weekly", "overall"]);
28878
+ var CheckpointSummarySchema = external_exports.object({
28879
+ id: external_exports.string(),
28880
+ kind: CheckpointKindSchema,
28881
+ /** ISO date for daily (YYYY-MM-DD), ISO week for weekly, null for overall */
28882
+ periodKey: external_exports.string().nullable(),
28883
+ summary: external_exports.string(),
28884
+ createdAt: external_exports.string(),
28885
+ updatedAt: external_exports.string()
28886
+ });
28887
+ var ThreadMetaSchema = external_exports.object({
28888
+ threadId: external_exports.string(),
28889
+ workspaceId: external_exports.string(),
28890
+ /** External source (e.g. slack, discord); null if internal-only */
28891
+ externalSource: external_exports.string().nullable(),
28892
+ /** Id in the external system (e.g. channel_id + thread_ts) */
28893
+ externalId: external_exports.string().nullable(),
28894
+ title: external_exports.string().optional(),
28895
+ createdAt: external_exports.string(),
28896
+ updatedAt: external_exports.string()
28897
+ });
28898
+ var ThreadMessageSchema = external_exports.object({
28899
+ messageId: external_exports.string(),
28900
+ threadId: external_exports.string(),
28901
+ /** Role: user, assistant, system */
28902
+ role: external_exports.enum(["user", "assistant", "system"]),
28903
+ content: external_exports.string(),
28904
+ /** Optional reference to a ContentItem (e.g. doc, Notion page) */
28905
+ contentItemId: external_exports.string().nullable(),
28906
+ /** External message id if synced from external chat */
28907
+ externalId: external_exports.string().nullable(),
28908
+ createdAt: external_exports.string(),
28909
+ updatedAt: external_exports.string()
28910
+ });
28911
+ var ThreadCheckpointSummarySchema = CheckpointSummarySchema.extend({
28912
+ threadId: external_exports.string()
28913
+ });
28914
+ var ContentSourceSchema = external_exports.enum(["notion", "doc", "slack_thread", "other"]);
28915
+ var ContentItemMetaSchema = external_exports.object({
28916
+ contentId: external_exports.string(),
28917
+ workspaceId: external_exports.string(),
28918
+ source: ContentSourceSchema,
28919
+ /** Id in the external system (e.g. Notion page id, doc url) */
28920
+ externalId: external_exports.string(),
28921
+ /** If source is slack_thread, points to Thread DO id */
28922
+ threadId: external_exports.string().nullable(),
28923
+ title: external_exports.string().optional(),
28924
+ createdAt: external_exports.string(),
28925
+ updatedAt: external_exports.string()
28926
+ });
28927
+ var ContentStorageRefSchema = external_exports.object({
28928
+ storageKey: external_exports.string(),
28929
+ /** Optional: mime type or format hint */
28930
+ contentType: external_exports.string().optional()
28931
+ });
28932
+ var ContentCheckpointSummarySchema = CheckpointSummarySchema.extend({
28933
+ contentId: external_exports.string()
28934
+ });
28935
+ var StoryMetaSchema = external_exports.object({
28936
+ storyId: external_exports.string(),
28937
+ workspaceId: external_exports.string(),
28938
+ title: external_exports.string(),
28939
+ /** feature | bug | epic */
28940
+ kind: external_exports.enum(["feature", "bug", "epic"]).default("feature"),
28941
+ createdAt: external_exports.string(),
28942
+ updatedAt: external_exports.string()
28943
+ });
28944
+ var StoryContentItemRefSchema = external_exports.object({
28945
+ id: external_exports.string(),
28946
+ storyId: external_exports.string(),
28947
+ contentItemId: external_exports.string(),
28948
+ /** Snapshot summary when added to story (or updated) */
28949
+ summary: external_exports.string(),
28950
+ orderIndex: external_exports.number().default(0),
28951
+ createdAt: external_exports.string(),
28952
+ updatedAt: external_exports.string()
28953
+ });
28954
+ var StoryCheckpointSummarySchema = CheckpointSummarySchema.extend({
28955
+ storyId: external_exports.string()
28956
+ });
28957
+ var BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID = "__builtin_change_summary__";
28958
+ var SessionMetaSchema = external_exports.object({
28959
+ sessionId: external_exports.string(),
28960
+ workspaceId: external_exports.string(),
28961
+ title: external_exports.string().optional(),
28962
+ createdAt: external_exports.string(),
28963
+ updatedAt: external_exports.string()
28964
+ });
28965
+ var SessionPromptSchema = external_exports.object({
28966
+ id: external_exports.string(),
28967
+ sessionId: external_exports.string(),
28968
+ /** text | resource */
28969
+ type: external_exports.enum(["text", "resource"]).default("text"),
28970
+ text: external_exports.string().optional(),
28971
+ resourceUri: external_exports.string().optional(),
28972
+ createdAt: external_exports.string()
28973
+ });
28974
+ var SessionResponseSchema = external_exports.object({
28975
+ id: external_exports.string(),
28976
+ sessionId: external_exports.string(),
28977
+ promptId: external_exports.string(),
28978
+ /** message | completion */
28979
+ kind: external_exports.enum(["message", "completion"]),
28980
+ content: external_exports.string().optional(),
28981
+ /** For completion: stopReason etc. */
28982
+ stopReason: external_exports.string().optional(),
28983
+ createdAt: external_exports.string()
28984
+ });
28985
+ var SessionToolCallSchema = external_exports.object({
28986
+ id: external_exports.string(),
28987
+ sessionId: external_exports.string(),
28988
+ promptId: external_exports.string(),
28989
+ name: external_exports.string(),
28990
+ params: external_exports.record(external_exports.unknown()).optional(),
28991
+ result: external_exports.record(external_exports.unknown()).optional(),
28992
+ createdAt: external_exports.string()
28993
+ });
28994
+ var SessionThreadRefSchema = external_exports.object({
28995
+ sessionId: external_exports.string(),
28996
+ threadId: external_exports.string(),
28997
+ addedAt: external_exports.string()
28998
+ });
28999
+ function normalizeRepoRelativePath(p) {
29000
+ let t = p.trim().replace(/\\/g, "/");
29001
+ while (t.startsWith("./")) t = t.slice(2);
29002
+ return t.replace(/\/+/g, "/");
29003
+ }
29004
+ function resolveChangeSummaryPathAgainstAllowed(rawPath, allowed) {
29005
+ const trimmed2 = rawPath.trim();
29006
+ if (!trimmed2) return null;
29007
+ if (allowed.has(trimmed2)) return trimmed2;
29008
+ const n = normalizeRepoRelativePath(trimmed2);
29009
+ if (allowed.has(n)) return n;
29010
+ for (const a of allowed) {
29011
+ if (normalizeRepoRelativePath(a) === n) return a;
29012
+ }
29013
+ return null;
29014
+ }
29015
+ function clampSummaryToAtMostTwoLines(summary) {
29016
+ const lines = summary.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
29017
+ return lines.slice(0, 2).join("\n");
29018
+ }
29019
+ function parseChangeSummaryJson(raw, allowedPaths, options) {
29020
+ if (raw == null || raw.trim() === "") return [];
29021
+ let text = raw.trim();
29022
+ const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
29023
+ if (fence?.[1]) text = fence[1].trim();
29024
+ let parsed;
29025
+ try {
29026
+ parsed = JSON.parse(text);
29027
+ } catch {
29028
+ const start = text.indexOf("[");
29029
+ const end = text.lastIndexOf("]");
29030
+ if (start < 0 || end <= start) return [];
29031
+ try {
29032
+ parsed = JSON.parse(text.slice(start, end + 1));
29033
+ } catch {
29034
+ return [];
29035
+ }
29036
+ }
29037
+ const rows = [];
29038
+ let arr = [];
29039
+ if (Array.isArray(parsed)) {
29040
+ arr = parsed;
29041
+ } else if (parsed && typeof parsed === "object" && Array.isArray(parsed.files)) {
29042
+ arr = parsed.files;
29043
+ }
29044
+ const skip = options?.skipPathAllowlist === true;
29045
+ for (const item of arr) {
29046
+ if (!item || typeof item !== "object") continue;
29047
+ const o = item;
29048
+ const rawPath = typeof o.path === "string" ? o.path.trim() : "";
29049
+ const summary = typeof o.summary === "string" ? o.summary.trim() : "";
29050
+ if (!rawPath || !summary) continue;
29051
+ const path32 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
29052
+ if (!path32) continue;
29053
+ rows.push({ path: path32, summary: clampSummaryToAtMostTwoLines(summary) });
29054
+ }
29055
+ return rows;
29056
+ }
29057
+ var PATCH_PREVIEW_MAX = 12e3;
29058
+ function clip(s, max) {
29059
+ if (s.length <= max) return s;
29060
+ return `${s.slice(0, max)}
29061
+
29062
+ \u2026(truncated, ${s.length - max} more characters)`;
29063
+ }
29064
+ function buildSessionChangeSummaryPrompt(files) {
29065
+ const lines = [
29066
+ "You are the same agent that produced the changes below. Summarize **your own** edits so a reader can scan them quickly.",
29067
+ "",
29068
+ "Write in second person (you / your): what you changed in each path and why it matters.",
29069
+ "",
29070
+ "Each summary must be **very concise**: **one line** of plain text, or **at most two short lines** (use a single line break between the two if needed). No bullets, no paragraphs.",
29071
+ "",
29072
+ "## How to format your reply (machine parsing)",
29073
+ "",
29074
+ "- Put the machine-readable part **only** inside a **markdown fenced code block** whose opening fence is exactly ```json on its own line. Close the block with ``` on its own line after the JSON.",
29075
+ "- Inside that fence: **nothing except** one valid JSON value \u2014 the array described below. No trailing commentary inside the fence.",
29076
+ "- **Do not** attach prose to the JSON on the same line (wrong: `Only one file\u2026page.tsx.[{\u2026}]`). Wrong: any sentence that ends with `.` immediately before `[`. Put a blank line before the ```json line.",
29077
+ "- If you add optional plain English before the fence (e.g. one short sentence), keep it **separate**: end that sentence, blank line, then ```json.",
29078
+ '- In each `"summary"` string, avoid raw double-quote characters, or escape them as `\\"` so the JSON parses.',
29079
+ "",
29080
+ "JSON shape **inside the fence** (array only):",
29081
+ '[{"path":"<file path exactly as given>","summary":"<one line, or two short lines separated by \\n>"}]',
29082
+ "",
29083
+ "Rules:",
29084
+ "- Include **exactly one** object per file path listed below (same path strings).",
29085
+ "- If a path is a removed directory, state briefly what you removed.",
29086
+ "- Do not invent paths; use only the paths provided.",
29087
+ "",
29088
+ "## Files you changed",
29089
+ ""
29090
+ ];
29091
+ for (const f of files) {
29092
+ lines.push(`### ${f.path}`);
29093
+ if (f.directoryRemoved) {
29094
+ lines.push("(directory removed)");
29095
+ lines.push("");
29096
+ continue;
29097
+ }
29098
+ if (f.patchContent && f.patchContent.trim() !== "") {
29099
+ lines.push("```diff");
29100
+ lines.push(clip(f.patchContent.trim(), PATCH_PREVIEW_MAX));
29101
+ lines.push("```");
29102
+ } else if (f.oldText != null || f.newText != null) {
29103
+ const oldT = (f.oldText ?? "").trim();
29104
+ const newT = (f.newText ?? "").trim();
29105
+ lines.push("Previous snippet:", clip(oldT, 6e3));
29106
+ lines.push("New snippet:", clip(newT, 6e3));
29107
+ } else {
29108
+ lines.push("(no diff body stored for this path)");
29109
+ }
29110
+ lines.push("");
29111
+ }
29112
+ return lines.join("\n");
29113
+ }
29114
+ function defaultRichness(c) {
29115
+ const patch = typeof c.patchContent === "string" ? c.patchContent.length : 0;
29116
+ const nt = typeof c.newText === "string" ? c.newText.length : 0;
29117
+ const ot = typeof c.oldText === "string" ? c.oldText.length : 0;
29118
+ const dir = c.directoryRemoved === true ? 8 : 0;
29119
+ return (patch > 0 ? 4 : 0) + (nt > 0 ? 2 : 0) + (ot > 0 ? 1 : 0) + dir;
29120
+ }
29121
+ function dedupeSessionFileChangesByPath(items, richness = (item) => defaultRichness(item)) {
29122
+ const byPath = /* @__PURE__ */ new Map();
29123
+ for (const item of items) {
29124
+ const p = typeof item.path === "string" ? item.path.trim() : "";
29125
+ if (!p) continue;
29126
+ const prev = byPath.get(p);
29127
+ if (!prev || richness(item) >= richness(prev)) byPath.set(p, item);
29128
+ }
29129
+ return Array.from(byPath.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v);
29130
+ }
29131
+ var ArtifactMetaSchema = external_exports.object({
29132
+ artifactId: external_exports.string(),
29133
+ workspaceId: external_exports.string(),
29134
+ /** Slug for permalink: /workspaces/:wid/artifacts/:slug */
29135
+ permalinkSlug: external_exports.string(),
29136
+ title: external_exports.string(),
29137
+ /** e.g. summary_report, build_log */
29138
+ type: external_exports.string().default("report"),
29139
+ /** Optional session that produced this artifact */
29140
+ sessionId: external_exports.string().nullable(),
29141
+ createdAt: external_exports.string(),
29142
+ updatedAt: external_exports.string()
29143
+ });
29144
+ var TemplateMetaSchema = external_exports.object({
29145
+ templateId: external_exports.string(),
29146
+ workspaceId: external_exports.string(),
29147
+ name: external_exports.string(),
29148
+ /** e.g. summary_report, build_log */
29149
+ artifactType: external_exports.string().optional(),
29150
+ createdAt: external_exports.string(),
29151
+ updatedAt: external_exports.string()
29152
+ });
29153
+ var GitRepoMetaSchema = external_exports.object({
29154
+ /** Stable id for the repo (e.g. hash of normalized canonical URL). Used for DO idFromName. */
29155
+ repoId: external_exports.string(),
29156
+ /** Canonical external URL (e.g. https://github.com/org/repo). Normalize before storing. */
29157
+ canonicalUrl: external_exports.string().url(),
29158
+ /** Optional workspace this repo was first linked in. */
29159
+ workspaceId: external_exports.string().nullable(),
29160
+ displayName: external_exports.string().optional(),
29161
+ createdAt: external_exports.string(),
29162
+ updatedAt: external_exports.string()
29163
+ });
29164
+
29165
+ // src/agents/acp/put-summarize-change-summaries.ts
29166
+ async function putEncryptedChangeSummaryRows(params) {
29167
+ const base = params.apiBaseUrl.replace(/\/+$/, "");
29168
+ const entries = params.rows.map(({ path: path32, summary }) => {
29169
+ const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
29170
+ return { path: path32, summary: JSON.stringify(enc) };
29171
+ });
29172
+ const res = await fetch(
29173
+ `${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
29174
+ {
29175
+ method: "PUT",
29176
+ headers: {
29177
+ Authorization: `Bearer ${params.authToken}`,
29178
+ "Content-Type": "application/json"
29179
+ },
29180
+ body: JSON.stringify({ entries })
29181
+ }
29182
+ );
29183
+ if (!res.ok) {
29184
+ const t = await res.text();
29185
+ throw new Error(`PUT summarize-changes summaries failed ${res.status}: ${t.slice(0, 500)}`);
29186
+ }
29187
+ }
29188
+
29189
+ // src/agents/acp/maybe-upload-e2ee-session-change-summaries.ts
29190
+ async function maybeUploadE2eeSessionChangeSummariesAfterAgentSuccess(params) {
29191
+ const {
29192
+ sessionId,
29193
+ runId,
29194
+ resultSuccess,
29195
+ output,
29196
+ e2ee,
29197
+ cloudApiBaseUrl,
29198
+ getCloudAccessToken,
29199
+ followUpCatalogPromptId,
29200
+ sessionChangeSummaryFilePaths,
29201
+ log: log2
29202
+ } = params;
29203
+ const outputStr = typeof output === "string" ? output : "";
29204
+ if (!sessionId) {
29205
+ return;
29206
+ }
29207
+ if (!runId) {
29208
+ return;
29209
+ }
29210
+ if (!resultSuccess) {
29211
+ return;
29212
+ }
29213
+ if (!e2ee) {
29214
+ return;
29215
+ }
29216
+ if (!cloudApiBaseUrl) {
29217
+ return;
29218
+ }
29219
+ if (!getCloudAccessToken) {
29220
+ return;
29221
+ }
29222
+ if (followUpCatalogPromptId !== BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID) {
29223
+ return;
29224
+ }
29225
+ if (!sessionChangeSummaryFilePaths || sessionChangeSummaryFilePaths.length === 0) {
29226
+ return;
29227
+ }
29228
+ if (outputStr.trim() === "") {
29229
+ return;
29230
+ }
29231
+ const allowed = /* @__PURE__ */ new Set();
29232
+ for (const p of sessionChangeSummaryFilePaths) {
29233
+ const t = p.trim();
29234
+ if (!t) continue;
29235
+ allowed.add(t);
29236
+ allowed.add(normalizeRepoRelativePath(t));
29237
+ }
29238
+ const rows = parseChangeSummaryJson(outputStr, allowed);
29239
+ if (rows.length === 0) {
29240
+ return;
29241
+ }
29242
+ const token = getCloudAccessToken();
29243
+ if (!token) {
29244
+ return;
29245
+ }
29246
+ try {
29247
+ await putEncryptedChangeSummaryRows({
29248
+ apiBaseUrl: cloudApiBaseUrl,
29249
+ authToken: token,
29250
+ sessionId,
29251
+ e2ee,
29252
+ rows
29253
+ });
29254
+ } catch (uploadErr) {
29255
+ log2(
29256
+ `[Agent] Encrypted change summary upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
29257
+ );
29258
+ }
29259
+ }
29260
+
29261
+ // src/agents/acp/send-prompt-result-augment.ts
29262
+ function augmentPromptResultAuthFields(agentType, errorText) {
29263
+ const err = errorText ?? "";
29264
+ const at = agentType ?? null;
29265
+ const evaluated = Boolean(at && err.trim());
29266
+ const suggestsAuth = evaluated && at ? localAgentErrorSuggestsAuth(at, err) : false;
29267
+ if (!suggestsAuth || !agentType) return {};
29268
+ return { agentAuthRequired: true, agentType };
29269
+ }
29270
+
28779
29271
  // src/agents/acp/send-prompt-to-agent.ts
28780
29272
  async function sendPromptToAgent(options) {
28781
29273
  const {
@@ -28788,16 +29280,13 @@ async function sendPromptToAgent(options) {
28788
29280
  agentCwd,
28789
29281
  sendResult: sendResult2,
28790
29282
  sendSessionUpdate,
28791
- log: log2
29283
+ log: log2,
29284
+ followUpCatalogPromptId,
29285
+ sessionChangeSummaryFilePaths,
29286
+ cloudApiBaseUrl,
29287
+ getCloudAccessToken,
29288
+ e2ee
28792
29289
  } = options;
28793
- function augmentAuthFields(errorText) {
28794
- const err = errorText ?? "";
28795
- const at = agentType ?? null;
28796
- const evaluated = Boolean(at && err.trim());
28797
- const suggestsAuth = evaluated && at ? localAgentErrorSuggestsAuth(at, err) : false;
28798
- if (!suggestsAuth || !agentType) return {};
28799
- return { agentAuthRequired: true, agentType };
28800
- }
28801
29290
  try {
28802
29291
  const result = await handle.sendPrompt(promptText, {});
28803
29292
  if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
@@ -28809,6 +29298,18 @@ async function sendPromptToAgent(options) {
28809
29298
  log: log2
28810
29299
  });
28811
29300
  }
29301
+ await maybeUploadE2eeSessionChangeSummariesAfterAgentSuccess({
29302
+ sessionId,
29303
+ runId,
29304
+ resultSuccess: result.success === true,
29305
+ output: result.output,
29306
+ e2ee,
29307
+ cloudApiBaseUrl,
29308
+ getCloudAccessToken,
29309
+ followUpCatalogPromptId,
29310
+ sessionChangeSummaryFilePaths,
29311
+ log: log2
29312
+ });
28812
29313
  const errStr = typeof result.error === "string" ? result.error : void 0;
28813
29314
  sendResult2({
28814
29315
  type: "prompt_result",
@@ -28816,7 +29317,8 @@ async function sendPromptToAgent(options) {
28816
29317
  ...sessionId ? { sessionId } : {},
28817
29318
  ...runId ? { runId } : {},
28818
29319
  ...result,
28819
- ...augmentAuthFields(errStr)
29320
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
29321
+ ...augmentPromptResultAuthFields(agentType, errStr)
28820
29322
  });
28821
29323
  if (!result.success) {
28822
29324
  log2(
@@ -28833,7 +29335,8 @@ async function sendPromptToAgent(options) {
28833
29335
  ...runId ? { runId } : {},
28834
29336
  success: false,
28835
29337
  error: errMsg,
28836
- ...augmentAuthFields(errMsg)
29338
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
29339
+ ...augmentPromptResultAuthFields(agentType, errMsg)
28837
29340
  });
28838
29341
  }
28839
29342
  }
@@ -30141,7 +30644,12 @@ async function createAcpManager(options) {
30141
30644
  agentType,
30142
30645
  cwd,
30143
30646
  sendResult: sendResult2,
30144
- sendSessionUpdate
30647
+ sendSessionUpdate,
30648
+ followUpCatalogPromptId,
30649
+ sessionChangeSummaryFilePaths,
30650
+ cloudApiBaseUrl,
30651
+ getCloudAccessToken,
30652
+ e2ee
30145
30653
  } = opts;
30146
30654
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
30147
30655
  pendingCancelRunId = void 0;
@@ -30205,7 +30713,12 @@ async function createAcpManager(options) {
30205
30713
  agentCwd: cwd,
30206
30714
  sendResult: sendResult2,
30207
30715
  sendSessionUpdate,
30208
- log: log2
30716
+ log: log2,
30717
+ followUpCatalogPromptId,
30718
+ sessionChangeSummaryFilePaths,
30719
+ cloudApiBaseUrl,
30720
+ getCloudAccessToken,
30721
+ e2ee
30209
30722
  });
30210
30723
  }
30211
30724
  void run().finally(() => {
@@ -32864,6 +33377,42 @@ var handleAgentConfigMessage = (msg, deps) => {
32864
33377
 
32865
33378
  // src/agents/acp/from-bridge/handle-bridge-prompt.ts
32866
33379
  import * as path28 from "node:path";
33380
+
33381
+ // src/agents/acp/from-bridge/bridge-prompt-wiring.ts
33382
+ function createBridgePromptSenders(deps, getWs) {
33383
+ const sendBridgeMessage = (message, encryptedFields = []) => {
33384
+ const s = getWs();
33385
+ if (!s) return false;
33386
+ const wire = deps.e2ee && encryptedFields.length > 0 ? deps.e2ee.encryptFields(message, encryptedFields) : message;
33387
+ sendWsMessage(s, wire);
33388
+ return true;
33389
+ };
33390
+ const sendResult2 = (result) => {
33391
+ const skipEncryptForChangeSummaryFollowUp = result.type === "prompt_result" && result.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
33392
+ const encryptedFields = result.type === "prompt_result" && !skipEncryptForChangeSummaryFollowUp ? ["output", "error"] : [];
33393
+ sendBridgeMessage(result, encryptedFields);
33394
+ };
33395
+ const sendSessionUpdate = (payload) => {
33396
+ const s = getWs();
33397
+ if (!s) {
33398
+ deps.log("[Bridge service] Session update not sent: not connected to the bridge.");
33399
+ return;
33400
+ }
33401
+ const p = payload;
33402
+ const wire = p.type === "session_update" && deps.e2ee ? deps.e2ee.encryptFields(payload, ["payload"]) : p.type === "session_file_change" && deps.e2ee ? deps.e2ee.encryptFields(payload, [
33403
+ "path",
33404
+ "oldText",
33405
+ "newText",
33406
+ "patchContent",
33407
+ "isDirectory",
33408
+ "directoryRemoved"
33409
+ ]) : payload;
33410
+ sendWsMessage(s, wire);
33411
+ };
33412
+ return { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate };
33413
+ }
33414
+
33415
+ // src/agents/acp/from-bridge/bridge-prompt-preamble.ts
32867
33416
  import { execFile as execFile10 } from "node:child_process";
32868
33417
  import { promisify as promisify10 } from "node:util";
32869
33418
 
@@ -32922,7 +33471,7 @@ async function resolveBridgeQueueBindFields(options) {
32922
33471
  return { canonicalQueueKey, repoId, cwdAbs };
32923
33472
  }
32924
33473
 
32925
- // src/agents/acp/from-bridge/handle-bridge-prompt.ts
33474
+ // src/agents/acp/from-bridge/bridge-prompt-preamble.ts
32926
33475
  var execFileAsync9 = promisify10(execFile10);
32927
33476
  async function readGitBranch(cwd) {
32928
33477
  try {
@@ -32933,6 +33482,158 @@ async function readGitBranch(cwd) {
32933
33482
  return null;
32934
33483
  }
32935
33484
  }
33485
+ async function runBridgePromptPreamble(params) {
33486
+ const { getWs, log: log2, sessionWorktreeManager, sessionId, runId, effectiveCwd } = params;
33487
+ const s = getWs();
33488
+ const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
33489
+ const repoRoots = await resolveSnapshotRepoRoots({
33490
+ worktreePaths,
33491
+ fallbackCwd: effectiveCwd,
33492
+ log: log2
33493
+ });
33494
+ if (s && sessionId) {
33495
+ const bind = await resolveBridgeQueueBindFields({
33496
+ effectiveCwd,
33497
+ worktreePaths,
33498
+ primaryRepoRoots: repoRoots,
33499
+ log: log2
33500
+ });
33501
+ if (bind) {
33502
+ sendWsMessage(s, {
33503
+ type: "bridge_queue_bind",
33504
+ sessionId,
33505
+ canonicalQueueKey: bind.canonicalQueueKey,
33506
+ repoId: bind.repoId,
33507
+ cwdAbs: bind.cwdAbs
33508
+ });
33509
+ }
33510
+ }
33511
+ if (s && sessionId) {
33512
+ const cliGitBranch = await readGitBranch(effectiveCwd);
33513
+ sendWsMessage(s, {
33514
+ type: "session_git_context_report",
33515
+ sessionId,
33516
+ cliGitBranch,
33517
+ agentUsesWorktree: sessionWorktreeManager.usesWorktreeSession(sessionId)
33518
+ });
33519
+ }
33520
+ if (s && sessionId && runId) {
33521
+ const cap = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false, error: "No git repos" };
33522
+ sendWsMessage(s, {
33523
+ type: "pre_turn_snapshot_report",
33524
+ sessionId,
33525
+ turnId: runId,
33526
+ captured: cap.ok
33527
+ });
33528
+ }
33529
+ }
33530
+ function parseChangeSummarySnapshots(raw) {
33531
+ if (!Array.isArray(raw) || raw.length === 0) return void 0;
33532
+ const out = [];
33533
+ for (const item of raw) {
33534
+ if (!item || typeof item !== "object") continue;
33535
+ const o = item;
33536
+ const path32 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
33537
+ if (!path32) continue;
33538
+ const row = { path: path32 };
33539
+ if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
33540
+ if (typeof o.oldText === "string") row.oldText = o.oldText;
33541
+ if (typeof o.newText === "string") row.newText = o.newText;
33542
+ if (o.directoryRemoved === true) row.directoryRemoved = true;
33543
+ out.push(row);
33544
+ }
33545
+ return out.length > 0 ? out : void 0;
33546
+ }
33547
+ function parseFollowUpFieldsFromPromptMessage(msg) {
33548
+ const followUpCatalogPromptId = typeof msg.followUpCatalogPromptId === "string" && msg.followUpCatalogPromptId.trim() !== "" ? msg.followUpCatalogPromptId.trim() : null;
33549
+ const rawPaths = msg.sessionChangeSummaryFilePaths;
33550
+ const sessionChangeSummaryFilePaths = Array.isArray(rawPaths) ? rawPaths.filter((p) => typeof p === "string" && p.trim() !== "").map((p) => p.trim()) : void 0;
33551
+ const sessionChangeSummaryFileSnapshots = parseChangeSummarySnapshots(msg.sessionChangeSummaryFileSnapshots);
33552
+ return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
33553
+ }
33554
+
33555
+ // ../e2ee/src/constants.ts
33556
+ var E2EE_NONCE_BYTES = 12;
33557
+
33558
+ // ../e2ee/src/types.ts
33559
+ function isE2eeEnvelope(value) {
33560
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
33561
+ const o = value;
33562
+ return typeof o.k === "string" && typeof o.n === "string" && typeof o.c === "string";
33563
+ }
33564
+
33565
+ // ../e2ee/src/encoding.ts
33566
+ function base64UrlEncode(bytes) {
33567
+ let binary = "";
33568
+ for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
33569
+ const b64 = btoa(binary);
33570
+ return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
33571
+ }
33572
+ function base64UrlDecode(value) {
33573
+ const b64 = value.replace(/-/g, "+").replace(/_/g, "/");
33574
+ const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
33575
+ const binary = atob(padded);
33576
+ const out = new Uint8Array(binary.length);
33577
+ for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
33578
+ return out;
33579
+ }
33580
+
33581
+ // src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
33582
+ function decryptChangeSummaryFileInput(row, e2ee) {
33583
+ if (!e2ee) return row;
33584
+ for (const field of ["path", "patchContent", "oldText", "newText"]) {
33585
+ const raw = row[field];
33586
+ if (typeof raw !== "string" || raw.trim() === "") continue;
33587
+ let o;
33588
+ try {
33589
+ o = JSON.parse(raw);
33590
+ } catch {
33591
+ continue;
33592
+ }
33593
+ if (!isE2eeEnvelope(o.ee)) continue;
33594
+ try {
33595
+ const d = e2ee.decryptMessage(o);
33596
+ const out = {
33597
+ path: typeof d.path === "string" ? d.path : row.path
33598
+ };
33599
+ if (d.directoryRemoved === true) out.directoryRemoved = true;
33600
+ else if (row.directoryRemoved === true) out.directoryRemoved = true;
33601
+ if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
33602
+ else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
33603
+ if (typeof d.oldText === "string") out.oldText = d.oldText;
33604
+ else if (typeof row.oldText === "string") out.oldText = row.oldText;
33605
+ if (typeof d.newText === "string") out.newText = d.newText;
33606
+ else if (typeof row.newText === "string") out.newText = row.newText;
33607
+ return out;
33608
+ } catch {
33609
+ return row;
33610
+ }
33611
+ }
33612
+ return row;
33613
+ }
33614
+
33615
+ // src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
33616
+ function hasSummarizePayload(f) {
33617
+ return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
33618
+ }
33619
+ function resolveChangeSummaryPromptForAgent(params) {
33620
+ const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
33621
+ const snaps = params.sessionChangeSummaryFileSnapshots;
33622
+ if (!isBuiltin || !snaps || snaps.length === 0) {
33623
+ return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
33624
+ }
33625
+ const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
33626
+ const withPayload = decrypted.filter(hasSummarizePayload);
33627
+ if (withPayload.length === 0) {
33628
+ return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
33629
+ }
33630
+ return {
33631
+ promptText: buildSessionChangeSummaryPrompt(withPayload),
33632
+ sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
33633
+ };
33634
+ }
33635
+
33636
+ // src/agents/acp/from-bridge/handle-bridge-prompt.ts
32936
33637
  function handleBridgePrompt(msg, deps) {
32937
33638
  const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
32938
33639
  const rawPrompt = msg.prompt;
@@ -32940,13 +33641,7 @@ function handleBridgePrompt(msg, deps) {
32940
33641
  const sessionId = msg.sessionId;
32941
33642
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
32942
33643
  const promptId = typeof msg.id === "string" ? msg.id : void 0;
32943
- const sendBridgeMessage = (message, encryptedFields = []) => {
32944
- const s = getWs();
32945
- if (!s) return false;
32946
- const wire = deps.e2ee && encryptedFields.length > 0 ? deps.e2ee.encryptFields(message, encryptedFields) : message;
32947
- sendWsMessage(s, wire);
32948
- return true;
32949
- };
33644
+ const { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate } = createBridgePromptSenders(deps, getWs);
32950
33645
  if (!promptText.trim()) {
32951
33646
  log2(
32952
33647
  `[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).`
@@ -32969,72 +33664,35 @@ function handleBridgePrompt(msg, deps) {
32969
33664
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
32970
33665
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
32971
33666
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
32972
- const sendResult2 = (result) => {
32973
- sendBridgeMessage(result, result.type === "prompt_result" ? ["output", "error"] : []);
32974
- };
32975
- const sendSessionUpdate = (payload) => {
32976
- const s = getWs();
32977
- if (!s) {
32978
- log2("[Bridge service] Session update not sent: not connected to the bridge.");
32979
- return;
32980
- }
32981
- const p = payload;
32982
- const wire = p.type === "session_update" && deps.e2ee ? deps.e2ee.encryptFields(payload, ["payload"]) : p.type === "session_file_change" && deps.e2ee ? deps.e2ee.encryptFields(payload, [
32983
- "path",
32984
- "oldText",
32985
- "newText",
32986
- "patchContent",
32987
- "isDirectory",
32988
- "directoryRemoved"
32989
- ]) : payload;
32990
- sendWsMessage(s, wire);
32991
- };
32992
33667
  async function preambleAndPrompt(resolvedCwd) {
32993
- const s = getWs();
32994
33668
  const effectiveCwd = path28.resolve(resolvedCwd ?? getBridgeWorkspaceDirectory());
32995
- const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
32996
- const repoRoots = await resolveSnapshotRepoRoots({
32997
- worktreePaths,
32998
- fallbackCwd: effectiveCwd,
32999
- log: log2
33669
+ await runBridgePromptPreamble({
33670
+ getWs,
33671
+ log: log2,
33672
+ sessionWorktreeManager,
33673
+ sessionId,
33674
+ runId,
33675
+ effectiveCwd
33000
33676
  });
33001
- if (s && sessionId) {
33002
- const bind = await resolveBridgeQueueBindFields({
33003
- effectiveCwd,
33004
- worktreePaths,
33005
- primaryRepoRoots: repoRoots,
33006
- log: log2
33007
- });
33008
- if (bind) {
33009
- sendWsMessage(s, {
33010
- type: "bridge_queue_bind",
33011
- sessionId,
33012
- canonicalQueueKey: bind.canonicalQueueKey,
33013
- repoId: bind.repoId,
33014
- cwdAbs: bind.cwdAbs
33015
- });
33016
- }
33017
- }
33018
- if (s && sessionId) {
33019
- const cliGitBranch = await readGitBranch(effectiveCwd);
33020
- sendWsMessage(s, {
33021
- type: "session_git_context_report",
33022
- sessionId,
33023
- cliGitBranch,
33024
- agentUsesWorktree: sessionWorktreeManager.usesWorktreeSession(sessionId)
33025
- });
33026
- }
33027
- if (s && sessionId && runId) {
33028
- const cap = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false, error: "No git repos" };
33029
- sendWsMessage(s, {
33030
- type: "pre_turn_snapshot_report",
33031
- sessionId,
33032
- turnId: runId,
33033
- captured: cap.ok
33034
- });
33677
+ const {
33678
+ followUpCatalogPromptId,
33679
+ sessionChangeSummaryFilePaths: pathsFromBridge,
33680
+ sessionChangeSummaryFileSnapshots
33681
+ } = parseFollowUpFieldsFromPromptMessage(msg);
33682
+ const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
33683
+ followUpCatalogPromptId,
33684
+ sessionChangeSummaryFileSnapshots,
33685
+ bridgePromptText: promptText,
33686
+ e2ee: deps.e2ee
33687
+ });
33688
+ if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
33689
+ deps.log(
33690
+ "[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
33691
+ );
33035
33692
  }
33693
+ const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
33036
33694
  acpManager.handlePrompt({
33037
- promptText,
33695
+ promptText: resolvedPromptText,
33038
33696
  promptId: msg.id,
33039
33697
  sessionId,
33040
33698
  runId,
@@ -33042,7 +33700,12 @@ function handleBridgePrompt(msg, deps) {
33042
33700
  agentType,
33043
33701
  cwd: effectiveCwd,
33044
33702
  sendResult: sendResult2,
33045
- sendSessionUpdate
33703
+ sendSessionUpdate,
33704
+ followUpCatalogPromptId,
33705
+ sessionChangeSummaryFilePaths: pathsForUpload,
33706
+ cloudApiBaseUrl: deps.cloudApiBaseUrl,
33707
+ getCloudAccessToken: deps.getCloudAccessToken,
33708
+ e2ee: deps.e2ee
33046
33709
  });
33047
33710
  }
33048
33711
  void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
@@ -33907,32 +34570,6 @@ function createMainBridgeWebSocketLifecycle(params) {
33907
34570
  return { connect };
33908
34571
  }
33909
34572
 
33910
- // ../e2ee/src/constants.ts
33911
- var E2EE_NONCE_BYTES = 12;
33912
-
33913
- // ../e2ee/src/types.ts
33914
- function isE2eeEnvelope(value) {
33915
- if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
33916
- const o = value;
33917
- return typeof o.k === "string" && typeof o.n === "string" && typeof o.c === "string";
33918
- }
33919
-
33920
- // ../e2ee/src/encoding.ts
33921
- function base64UrlEncode(bytes) {
33922
- let binary = "";
33923
- for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
33924
- const b64 = btoa(binary);
33925
- return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
33926
- }
33927
- function base64UrlDecode(value) {
33928
- const b64 = value.replace(/-/g, "+").replace(/_/g, "/");
33929
- const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
33930
- const binary = atob(padded);
33931
- const out = new Uint8Array(binary.length);
33932
- for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
33933
- return out;
33934
- }
33935
-
33936
34573
  // src/lib/e2ee/cli-e2ee-runtime.ts
33937
34574
  import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
33938
34575
  function nonceFromCounter(prefix, counter) {
@@ -34055,7 +34692,9 @@ async function createBridgeConnection(options) {
34055
34692
  sendLocalSkillsReport,
34056
34693
  reportAutoDetectedAgents,
34057
34694
  devServerManager,
34058
- e2ee
34695
+ e2ee,
34696
+ cloudApiBaseUrl: apiUrl,
34697
+ getCloudAccessToken: () => tokens.accessToken
34059
34698
  };
34060
34699
  const { connect } = createMainBridgeWebSocketLifecycle({
34061
34700
  state,