@buildautomaton/cli 0.1.16 → 0.1.17

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
@@ -31364,6 +31364,498 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
31364
31364
  }
31365
31365
  }
31366
31366
 
31367
+ // ../types/dist/index.js
31368
+ init_zod();
31369
+ init_zod();
31370
+ init_zod();
31371
+ init_zod();
31372
+ init_zod();
31373
+ init_zod();
31374
+ init_zod();
31375
+ init_zod();
31376
+ init_zod();
31377
+ init_zod();
31378
+ init_zod();
31379
+ init_zod();
31380
+ var WorkItemStatusSchema = external_exports.enum(["backlog", "in-progress", "completed"]);
31381
+ var WorkItemProgressSchema = external_exports.object({
31382
+ remainingCriteria: external_exports.array(external_exports.string()).default([]),
31383
+ openQuestions: external_exports.array(external_exports.string()).default([]),
31384
+ assignedTo: external_exports.enum(["agent", "human-product", "human-expert"]).optional()
31385
+ });
31386
+ var ChangeSchema = external_exports.object({
31387
+ id: external_exports.string(),
31388
+ description: external_exports.string(),
31389
+ buildingBlockId: external_exports.string(),
31390
+ buildingBlockType: external_exports.enum(["function", "workflow", "connector", "ui-component", "app-fragment", "application", "project"]),
31391
+ action: external_exports.enum(["create", "update", "split", "combine"])
31392
+ });
31393
+ var CompletionCriterionSchema = external_exports.object({
31394
+ id: external_exports.string(),
31395
+ description: external_exports.string(),
31396
+ type: external_exports.enum(["write-code", "write-tests", "verify-tests", "other"]),
31397
+ verified: external_exports.boolean().default(false)
31398
+ });
31399
+ var WorkItemPrioritySchema = external_exports.enum(["low", "medium", "high", "critical"]);
31400
+ var IterationPhaseSchema = external_exports.enum(["analysis", "implementation", "verify", "reprioritize", "completed"]);
31401
+ var WorkItemDependencySchema = external_exports.object({
31402
+ type: external_exports.enum(["work-item"]),
31403
+ id: external_exports.string()
31404
+ });
31405
+ var WorkItemSchema = external_exports.object({
31406
+ id: external_exports.string(),
31407
+ sessionId: external_exports.string().optional(),
31408
+ summary: external_exports.string().optional(),
31409
+ description: external_exports.string(),
31410
+ status: WorkItemStatusSchema,
31411
+ buildingBlockId: external_exports.string().optional(),
31412
+ buildingBlockType: external_exports.enum(["function", "workflow", "connector", "ui-component", "app-fragment", "application", "project"]),
31413
+ changes: external_exports.array(ChangeSchema).default([]),
31414
+ completionCriteria: external_exports.array(CompletionCriterionSchema).default([]),
31415
+ priority: WorkItemPrioritySchema.default("medium"),
31416
+ dependencies: external_exports.array(WorkItemDependencySchema).default([]),
31417
+ assignedToUserId: external_exports.string().optional()
31418
+ });
31419
+ var UserWorkspaceProfileSchema = external_exports.object({
31420
+ id: external_exports.string(),
31421
+ workspaceId: external_exports.string(),
31422
+ userId: external_exports.string(),
31423
+ roleDescription: external_exports.string().optional(),
31424
+ expertiseAreas: external_exports.array(external_exports.string()),
31425
+ preferences: external_exports.record(external_exports.unknown()).optional(),
31426
+ learnings: external_exports.array(external_exports.string())
31427
+ });
31428
+ var WorkspaceOwnerInfoSchema = external_exports.object({
31429
+ ownerId: external_exports.string(),
31430
+ ownerName: external_exports.string().optional(),
31431
+ ownerEmail: external_exports.string().optional(),
31432
+ ownerProfilePictureUrl: external_exports.string().optional()
31433
+ });
31434
+ var WorkspaceRuntimeEntrySchema = external_exports.object({
31435
+ workspaceId: external_exports.string(),
31436
+ path: external_exports.string(),
31437
+ name: external_exports.string().optional(),
31438
+ owner: WorkspaceOwnerInfoSchema.optional(),
31439
+ isOwner: external_exports.boolean().optional()
31440
+ });
31441
+ var ProjectContextSchema = external_exports.object({
31442
+ projectId: external_exports.string(),
31443
+ context: external_exports.record(external_exports.unknown()).default({}),
31444
+ updatedAt: external_exports.string()
31445
+ });
31446
+ var WebSocketMessageTypeSchema = external_exports.enum([
31447
+ "plan-update",
31448
+ "work-item-update",
31449
+ "work-item-added",
31450
+ "work-item-removed",
31451
+ "project-processing-start",
31452
+ "project-processing-update",
31453
+ "project-processing-complete",
31454
+ "project-processing-error",
31455
+ "file-tool-request",
31456
+ "file-tool-response",
31457
+ "file-generated"
31458
+ ]);
31459
+ var WebSocketMessageSchema = external_exports.object({
31460
+ type: WebSocketMessageTypeSchema,
31461
+ contextId: external_exports.string().optional(),
31462
+ data: external_exports.any().optional(),
31463
+ error: external_exports.string().optional()
31464
+ });
31465
+ var CheckpointKindSchema = external_exports.enum(["daily", "weekly", "overall"]);
31466
+ var CheckpointSummarySchema = external_exports.object({
31467
+ id: external_exports.string(),
31468
+ kind: CheckpointKindSchema,
31469
+ /** ISO date for daily (YYYY-MM-DD), ISO week for weekly, null for overall */
31470
+ periodKey: external_exports.string().nullable(),
31471
+ summary: external_exports.string(),
31472
+ createdAt: external_exports.string(),
31473
+ updatedAt: external_exports.string()
31474
+ });
31475
+ var ThreadMetaSchema = external_exports.object({
31476
+ threadId: external_exports.string(),
31477
+ workspaceId: external_exports.string(),
31478
+ /** External source (e.g. slack, discord); null if internal-only */
31479
+ externalSource: external_exports.string().nullable(),
31480
+ /** Id in the external system (e.g. channel_id + thread_ts) */
31481
+ externalId: external_exports.string().nullable(),
31482
+ title: external_exports.string().optional(),
31483
+ createdAt: external_exports.string(),
31484
+ updatedAt: external_exports.string()
31485
+ });
31486
+ var ThreadMessageSchema = external_exports.object({
31487
+ messageId: external_exports.string(),
31488
+ threadId: external_exports.string(),
31489
+ /** Role: user, assistant, system */
31490
+ role: external_exports.enum(["user", "assistant", "system"]),
31491
+ content: external_exports.string(),
31492
+ /** Optional reference to a ContentItem (e.g. doc, Notion page) */
31493
+ contentItemId: external_exports.string().nullable(),
31494
+ /** External message id if synced from external chat */
31495
+ externalId: external_exports.string().nullable(),
31496
+ createdAt: external_exports.string(),
31497
+ updatedAt: external_exports.string()
31498
+ });
31499
+ var ThreadCheckpointSummarySchema = CheckpointSummarySchema.extend({
31500
+ threadId: external_exports.string()
31501
+ });
31502
+ var ContentSourceSchema = external_exports.enum(["notion", "doc", "slack_thread", "other"]);
31503
+ var ContentItemMetaSchema = external_exports.object({
31504
+ contentId: external_exports.string(),
31505
+ workspaceId: external_exports.string(),
31506
+ source: ContentSourceSchema,
31507
+ /** Id in the external system (e.g. Notion page id, doc url) */
31508
+ externalId: external_exports.string(),
31509
+ /** If source is slack_thread, points to Thread DO id */
31510
+ threadId: external_exports.string().nullable(),
31511
+ title: external_exports.string().optional(),
31512
+ createdAt: external_exports.string(),
31513
+ updatedAt: external_exports.string()
31514
+ });
31515
+ var ContentStorageRefSchema = external_exports.object({
31516
+ storageKey: external_exports.string(),
31517
+ /** Optional: mime type or format hint */
31518
+ contentType: external_exports.string().optional()
31519
+ });
31520
+ var ContentCheckpointSummarySchema = CheckpointSummarySchema.extend({
31521
+ contentId: external_exports.string()
31522
+ });
31523
+ var StoryMetaSchema = external_exports.object({
31524
+ storyId: external_exports.string(),
31525
+ workspaceId: external_exports.string(),
31526
+ title: external_exports.string(),
31527
+ /** feature | bug | epic */
31528
+ kind: external_exports.enum(["feature", "bug", "epic"]).default("feature"),
31529
+ createdAt: external_exports.string(),
31530
+ updatedAt: external_exports.string()
31531
+ });
31532
+ var StoryContentItemRefSchema = external_exports.object({
31533
+ id: external_exports.string(),
31534
+ storyId: external_exports.string(),
31535
+ contentItemId: external_exports.string(),
31536
+ /** Snapshot summary when added to story (or updated) */
31537
+ summary: external_exports.string(),
31538
+ orderIndex: external_exports.number().default(0),
31539
+ createdAt: external_exports.string(),
31540
+ updatedAt: external_exports.string()
31541
+ });
31542
+ var StoryCheckpointSummarySchema = CheckpointSummarySchema.extend({
31543
+ storyId: external_exports.string()
31544
+ });
31545
+ var BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID = "__builtin_change_summary__";
31546
+ var SessionMetaSchema = external_exports.object({
31547
+ sessionId: external_exports.string(),
31548
+ workspaceId: external_exports.string(),
31549
+ title: external_exports.string().optional(),
31550
+ createdAt: external_exports.string(),
31551
+ updatedAt: external_exports.string()
31552
+ });
31553
+ var SessionPromptSchema = external_exports.object({
31554
+ id: external_exports.string(),
31555
+ sessionId: external_exports.string(),
31556
+ /** text | resource */
31557
+ type: external_exports.enum(["text", "resource"]).default("text"),
31558
+ text: external_exports.string().optional(),
31559
+ resourceUri: external_exports.string().optional(),
31560
+ createdAt: external_exports.string()
31561
+ });
31562
+ var SessionResponseSchema = external_exports.object({
31563
+ id: external_exports.string(),
31564
+ sessionId: external_exports.string(),
31565
+ promptId: external_exports.string(),
31566
+ /** message | completion */
31567
+ kind: external_exports.enum(["message", "completion"]),
31568
+ content: external_exports.string().optional(),
31569
+ /** For completion: stopReason etc. */
31570
+ stopReason: external_exports.string().optional(),
31571
+ createdAt: external_exports.string()
31572
+ });
31573
+ var SessionToolCallSchema = external_exports.object({
31574
+ id: external_exports.string(),
31575
+ sessionId: external_exports.string(),
31576
+ promptId: external_exports.string(),
31577
+ name: external_exports.string(),
31578
+ params: external_exports.record(external_exports.unknown()).optional(),
31579
+ result: external_exports.record(external_exports.unknown()).optional(),
31580
+ createdAt: external_exports.string()
31581
+ });
31582
+ var SessionThreadRefSchema = external_exports.object({
31583
+ sessionId: external_exports.string(),
31584
+ threadId: external_exports.string(),
31585
+ addedAt: external_exports.string()
31586
+ });
31587
+ function normalizeRepoRelativePath(p) {
31588
+ let t = p.trim().replace(/\\/g, "/");
31589
+ while (t.startsWith("./")) t = t.slice(2);
31590
+ return t.replace(/\/+/g, "/");
31591
+ }
31592
+ function resolveChangeSummaryPathAgainstAllowed(rawPath, allowed) {
31593
+ const trimmed2 = rawPath.trim();
31594
+ if (!trimmed2) return null;
31595
+ if (allowed.has(trimmed2)) return trimmed2;
31596
+ const n = normalizeRepoRelativePath(trimmed2);
31597
+ if (allowed.has(n)) return n;
31598
+ for (const a of allowed) {
31599
+ if (normalizeRepoRelativePath(a) === n) return a;
31600
+ }
31601
+ return null;
31602
+ }
31603
+ function clampSummaryToAtMostTwoLines(summary) {
31604
+ const lines = summary.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
31605
+ return lines.slice(0, 2).join("\n");
31606
+ }
31607
+ function parseChangeSummaryJson(raw, allowedPaths, options) {
31608
+ if (raw == null || raw.trim() === "") return [];
31609
+ let text = raw.trim();
31610
+ const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
31611
+ if (fence?.[1]) text = fence[1].trim();
31612
+ let parsed;
31613
+ try {
31614
+ parsed = JSON.parse(text);
31615
+ } catch {
31616
+ const start = text.indexOf("[");
31617
+ const end = text.lastIndexOf("]");
31618
+ if (start < 0 || end <= start) return [];
31619
+ try {
31620
+ parsed = JSON.parse(text.slice(start, end + 1));
31621
+ } catch {
31622
+ return [];
31623
+ }
31624
+ }
31625
+ const rows = [];
31626
+ let arr = [];
31627
+ if (Array.isArray(parsed)) {
31628
+ arr = parsed;
31629
+ } else if (parsed && typeof parsed === "object" && Array.isArray(parsed.files)) {
31630
+ arr = parsed.files;
31631
+ }
31632
+ const skip = options?.skipPathAllowlist === true;
31633
+ for (const item of arr) {
31634
+ if (!item || typeof item !== "object") continue;
31635
+ const o = item;
31636
+ const rawPath = typeof o.path === "string" ? o.path.trim() : "";
31637
+ const summary = typeof o.summary === "string" ? o.summary.trim() : "";
31638
+ if (!rawPath || !summary) continue;
31639
+ const path34 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
31640
+ if (!path34) continue;
31641
+ rows.push({ path: path34, summary: clampSummaryToAtMostTwoLines(summary) });
31642
+ }
31643
+ return rows;
31644
+ }
31645
+ var PATCH_PREVIEW_MAX = 12e3;
31646
+ function clip(s, max) {
31647
+ if (s.length <= max) return s;
31648
+ return `${s.slice(0, max)}
31649
+
31650
+ \u2026(truncated, ${s.length - max} more characters)`;
31651
+ }
31652
+ function buildSessionChangeSummaryPrompt(files) {
31653
+ const lines = [
31654
+ "You are the same agent that produced the changes below. Summarize **your own** edits so a reader can scan them quickly.",
31655
+ "",
31656
+ "Write in second person (you / your): what you changed in each path and why it matters.",
31657
+ "",
31658
+ "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.",
31659
+ "",
31660
+ "## How to format your reply (machine parsing)",
31661
+ "",
31662
+ "- 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.",
31663
+ "- Inside that fence: **nothing except** one valid JSON value \u2014 the array described below. No trailing commentary inside the fence.",
31664
+ "- **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.",
31665
+ "- If you add optional plain English before the fence (e.g. one short sentence), keep it **separate**: end that sentence, blank line, then ```json.",
31666
+ '- In each `"summary"` string, avoid raw double-quote characters, or escape them as `\\"` so the JSON parses.',
31667
+ "",
31668
+ "JSON shape **inside the fence** (array only):",
31669
+ '[{"path":"<file path exactly as given>","summary":"<one line, or two short lines separated by \\n>"}]',
31670
+ "",
31671
+ "Rules:",
31672
+ "- Include **exactly one** object per file path listed below (same path strings).",
31673
+ "- If a path is a removed directory, state briefly what you removed.",
31674
+ "- Do not invent paths; use only the paths provided.",
31675
+ "",
31676
+ "## Files you changed",
31677
+ ""
31678
+ ];
31679
+ for (const f of files) {
31680
+ lines.push(`### ${f.path}`);
31681
+ if (f.directoryRemoved) {
31682
+ lines.push("(directory removed)");
31683
+ lines.push("");
31684
+ continue;
31685
+ }
31686
+ if (f.patchContent && f.patchContent.trim() !== "") {
31687
+ lines.push("```diff");
31688
+ lines.push(clip(f.patchContent.trim(), PATCH_PREVIEW_MAX));
31689
+ lines.push("```");
31690
+ } else if (f.oldText != null || f.newText != null) {
31691
+ const oldT = (f.oldText ?? "").trim();
31692
+ const newT = (f.newText ?? "").trim();
31693
+ lines.push("Previous snippet:", clip(oldT, 6e3));
31694
+ lines.push("New snippet:", clip(newT, 6e3));
31695
+ } else {
31696
+ lines.push("(no diff body stored for this path)");
31697
+ }
31698
+ lines.push("");
31699
+ }
31700
+ return lines.join("\n");
31701
+ }
31702
+ function defaultRichness(c) {
31703
+ const patch = typeof c.patchContent === "string" ? c.patchContent.length : 0;
31704
+ const nt = typeof c.newText === "string" ? c.newText.length : 0;
31705
+ const ot = typeof c.oldText === "string" ? c.oldText.length : 0;
31706
+ const dir = c.directoryRemoved === true ? 8 : 0;
31707
+ return (patch > 0 ? 4 : 0) + (nt > 0 ? 2 : 0) + (ot > 0 ? 1 : 0) + dir;
31708
+ }
31709
+ function dedupeSessionFileChangesByPath(items, richness = (item) => defaultRichness(item)) {
31710
+ const byPath = /* @__PURE__ */ new Map();
31711
+ for (const item of items) {
31712
+ const p = typeof item.path === "string" ? item.path.trim() : "";
31713
+ if (!p) continue;
31714
+ const prev = byPath.get(p);
31715
+ if (!prev || richness(item) >= richness(prev)) byPath.set(p, item);
31716
+ }
31717
+ return Array.from(byPath.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v);
31718
+ }
31719
+ var ArtifactMetaSchema = external_exports.object({
31720
+ artifactId: external_exports.string(),
31721
+ workspaceId: external_exports.string(),
31722
+ /** Slug for permalink: /workspaces/:wid/artifacts/:slug */
31723
+ permalinkSlug: external_exports.string(),
31724
+ title: external_exports.string(),
31725
+ /** e.g. summary_report, build_log */
31726
+ type: external_exports.string().default("report"),
31727
+ /** Optional session that produced this artifact */
31728
+ sessionId: external_exports.string().nullable(),
31729
+ createdAt: external_exports.string(),
31730
+ updatedAt: external_exports.string()
31731
+ });
31732
+ var TemplateMetaSchema = external_exports.object({
31733
+ templateId: external_exports.string(),
31734
+ workspaceId: external_exports.string(),
31735
+ name: external_exports.string(),
31736
+ /** e.g. summary_report, build_log */
31737
+ artifactType: external_exports.string().optional(),
31738
+ createdAt: external_exports.string(),
31739
+ updatedAt: external_exports.string()
31740
+ });
31741
+ var GitRepoMetaSchema = external_exports.object({
31742
+ /** Stable id for the repo (e.g. hash of normalized canonical URL). Used for DO idFromName. */
31743
+ repoId: external_exports.string(),
31744
+ /** Canonical external URL (e.g. https://github.com/org/repo). Normalize before storing. */
31745
+ canonicalUrl: external_exports.string().url(),
31746
+ /** Optional workspace this repo was first linked in. */
31747
+ workspaceId: external_exports.string().nullable(),
31748
+ displayName: external_exports.string().optional(),
31749
+ createdAt: external_exports.string(),
31750
+ updatedAt: external_exports.string()
31751
+ });
31752
+
31753
+ // src/agents/acp/put-summarize-change-summaries.ts
31754
+ async function putEncryptedChangeSummaryRows(params) {
31755
+ const base = params.apiBaseUrl.replace(/\/+$/, "");
31756
+ const entries = params.rows.map(({ path: path34, summary }) => {
31757
+ const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
31758
+ return { path: path34, summary: JSON.stringify(enc) };
31759
+ });
31760
+ const res = await fetch(
31761
+ `${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
31762
+ {
31763
+ method: "PUT",
31764
+ headers: {
31765
+ Authorization: `Bearer ${params.authToken}`,
31766
+ "Content-Type": "application/json"
31767
+ },
31768
+ body: JSON.stringify({ entries })
31769
+ }
31770
+ );
31771
+ if (!res.ok) {
31772
+ const t = await res.text();
31773
+ throw new Error(`PUT summarize-changes summaries failed ${res.status}: ${t.slice(0, 500)}`);
31774
+ }
31775
+ }
31776
+
31777
+ // src/agents/acp/maybe-upload-e2ee-session-change-summaries.ts
31778
+ async function maybeUploadE2eeSessionChangeSummariesAfterAgentSuccess(params) {
31779
+ const {
31780
+ sessionId,
31781
+ runId,
31782
+ resultSuccess,
31783
+ output,
31784
+ e2ee,
31785
+ cloudApiBaseUrl,
31786
+ getCloudAccessToken,
31787
+ followUpCatalogPromptId,
31788
+ sessionChangeSummaryFilePaths,
31789
+ log: log2
31790
+ } = params;
31791
+ const outputStr = typeof output === "string" ? output : "";
31792
+ if (!sessionId) {
31793
+ return;
31794
+ }
31795
+ if (!runId) {
31796
+ return;
31797
+ }
31798
+ if (!resultSuccess) {
31799
+ return;
31800
+ }
31801
+ if (!e2ee) {
31802
+ return;
31803
+ }
31804
+ if (!cloudApiBaseUrl) {
31805
+ return;
31806
+ }
31807
+ if (!getCloudAccessToken) {
31808
+ return;
31809
+ }
31810
+ if (followUpCatalogPromptId !== BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID) {
31811
+ return;
31812
+ }
31813
+ if (!sessionChangeSummaryFilePaths || sessionChangeSummaryFilePaths.length === 0) {
31814
+ return;
31815
+ }
31816
+ if (outputStr.trim() === "") {
31817
+ return;
31818
+ }
31819
+ const allowed = /* @__PURE__ */ new Set();
31820
+ for (const p of sessionChangeSummaryFilePaths) {
31821
+ const t = p.trim();
31822
+ if (!t) continue;
31823
+ allowed.add(t);
31824
+ allowed.add(normalizeRepoRelativePath(t));
31825
+ }
31826
+ const rows = parseChangeSummaryJson(outputStr, allowed);
31827
+ if (rows.length === 0) {
31828
+ return;
31829
+ }
31830
+ const token = getCloudAccessToken();
31831
+ if (!token) {
31832
+ return;
31833
+ }
31834
+ try {
31835
+ await putEncryptedChangeSummaryRows({
31836
+ apiBaseUrl: cloudApiBaseUrl,
31837
+ authToken: token,
31838
+ sessionId,
31839
+ e2ee,
31840
+ rows
31841
+ });
31842
+ } catch (uploadErr) {
31843
+ log2(
31844
+ `[Agent] Encrypted change summary upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
31845
+ );
31846
+ }
31847
+ }
31848
+
31849
+ // src/agents/acp/send-prompt-result-augment.ts
31850
+ function augmentPromptResultAuthFields(agentType, errorText) {
31851
+ const err = errorText ?? "";
31852
+ const at = agentType ?? null;
31853
+ const evaluated = Boolean(at && err.trim());
31854
+ const suggestsAuth = evaluated && at ? localAgentErrorSuggestsAuth(at, err) : false;
31855
+ if (!suggestsAuth || !agentType) return {};
31856
+ return { agentAuthRequired: true, agentType };
31857
+ }
31858
+
31367
31859
  // src/agents/acp/send-prompt-to-agent.ts
31368
31860
  async function sendPromptToAgent(options) {
31369
31861
  const {
@@ -31376,16 +31868,13 @@ async function sendPromptToAgent(options) {
31376
31868
  agentCwd,
31377
31869
  sendResult: sendResult2,
31378
31870
  sendSessionUpdate,
31379
- log: log2
31871
+ log: log2,
31872
+ followUpCatalogPromptId,
31873
+ sessionChangeSummaryFilePaths,
31874
+ cloudApiBaseUrl,
31875
+ getCloudAccessToken,
31876
+ e2ee
31380
31877
  } = options;
31381
- function augmentAuthFields(errorText) {
31382
- const err = errorText ?? "";
31383
- const at = agentType ?? null;
31384
- const evaluated = Boolean(at && err.trim());
31385
- const suggestsAuth = evaluated && at ? localAgentErrorSuggestsAuth(at, err) : false;
31386
- if (!suggestsAuth || !agentType) return {};
31387
- return { agentAuthRequired: true, agentType };
31388
- }
31389
31878
  try {
31390
31879
  const result = await handle.sendPrompt(promptText, {});
31391
31880
  if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
@@ -31397,6 +31886,18 @@ async function sendPromptToAgent(options) {
31397
31886
  log: log2
31398
31887
  });
31399
31888
  }
31889
+ await maybeUploadE2eeSessionChangeSummariesAfterAgentSuccess({
31890
+ sessionId,
31891
+ runId,
31892
+ resultSuccess: result.success === true,
31893
+ output: result.output,
31894
+ e2ee,
31895
+ cloudApiBaseUrl,
31896
+ getCloudAccessToken,
31897
+ followUpCatalogPromptId,
31898
+ sessionChangeSummaryFilePaths,
31899
+ log: log2
31900
+ });
31400
31901
  const errStr = typeof result.error === "string" ? result.error : void 0;
31401
31902
  sendResult2({
31402
31903
  type: "prompt_result",
@@ -31404,7 +31905,8 @@ async function sendPromptToAgent(options) {
31404
31905
  ...sessionId ? { sessionId } : {},
31405
31906
  ...runId ? { runId } : {},
31406
31907
  ...result,
31407
- ...augmentAuthFields(errStr)
31908
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
31909
+ ...augmentPromptResultAuthFields(agentType, errStr)
31408
31910
  });
31409
31911
  if (!result.success) {
31410
31912
  log2(
@@ -31421,7 +31923,8 @@ async function sendPromptToAgent(options) {
31421
31923
  ...runId ? { runId } : {},
31422
31924
  success: false,
31423
31925
  error: errMsg,
31424
- ...augmentAuthFields(errMsg)
31926
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
31927
+ ...augmentPromptResultAuthFields(agentType, errMsg)
31425
31928
  });
31426
31929
  }
31427
31930
  }
@@ -33116,7 +33619,12 @@ async function createAcpManager(options) {
33116
33619
  agentType,
33117
33620
  cwd,
33118
33621
  sendResult: sendResult2,
33119
- sendSessionUpdate
33622
+ sendSessionUpdate,
33623
+ followUpCatalogPromptId,
33624
+ sessionChangeSummaryFilePaths,
33625
+ cloudApiBaseUrl,
33626
+ getCloudAccessToken,
33627
+ e2ee
33120
33628
  } = opts;
33121
33629
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
33122
33630
  pendingCancelRunId = void 0;
@@ -33180,7 +33688,12 @@ async function createAcpManager(options) {
33180
33688
  agentCwd: cwd,
33181
33689
  sendResult: sendResult2,
33182
33690
  sendSessionUpdate,
33183
- log: log2
33691
+ log: log2,
33692
+ followUpCatalogPromptId,
33693
+ sessionChangeSummaryFilePaths,
33694
+ cloudApiBaseUrl,
33695
+ getCloudAccessToken,
33696
+ e2ee
33184
33697
  });
33185
33698
  }
33186
33699
  void run().finally(() => {
@@ -36007,6 +36520,42 @@ var handleAgentConfigMessage = (msg, deps) => {
36007
36520
 
36008
36521
  // src/agents/acp/from-bridge/handle-bridge-prompt.ts
36009
36522
  import * as path29 from "node:path";
36523
+
36524
+ // src/agents/acp/from-bridge/bridge-prompt-wiring.ts
36525
+ function createBridgePromptSenders(deps, getWs) {
36526
+ const sendBridgeMessage = (message, encryptedFields = []) => {
36527
+ const s = getWs();
36528
+ if (!s) return false;
36529
+ const wire = deps.e2ee && encryptedFields.length > 0 ? deps.e2ee.encryptFields(message, encryptedFields) : message;
36530
+ sendWsMessage(s, wire);
36531
+ return true;
36532
+ };
36533
+ const sendResult2 = (result) => {
36534
+ const skipEncryptForChangeSummaryFollowUp = result.type === "prompt_result" && result.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
36535
+ const encryptedFields = result.type === "prompt_result" && !skipEncryptForChangeSummaryFollowUp ? ["output", "error"] : [];
36536
+ sendBridgeMessage(result, encryptedFields);
36537
+ };
36538
+ const sendSessionUpdate = (payload) => {
36539
+ const s = getWs();
36540
+ if (!s) {
36541
+ deps.log("[Bridge service] Session update not sent: not connected to the bridge.");
36542
+ return;
36543
+ }
36544
+ const p = payload;
36545
+ const wire = p.type === "session_update" && deps.e2ee ? deps.e2ee.encryptFields(payload, ["payload"]) : p.type === "session_file_change" && deps.e2ee ? deps.e2ee.encryptFields(payload, [
36546
+ "path",
36547
+ "oldText",
36548
+ "newText",
36549
+ "patchContent",
36550
+ "isDirectory",
36551
+ "directoryRemoved"
36552
+ ]) : payload;
36553
+ sendWsMessage(s, wire);
36554
+ };
36555
+ return { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate };
36556
+ }
36557
+
36558
+ // src/agents/acp/from-bridge/bridge-prompt-preamble.ts
36010
36559
  import { execFile as execFile10 } from "node:child_process";
36011
36560
  import { promisify as promisify10 } from "node:util";
36012
36561
 
@@ -36065,7 +36614,7 @@ async function resolveBridgeQueueBindFields(options) {
36065
36614
  return { canonicalQueueKey, repoId, cwdAbs };
36066
36615
  }
36067
36616
 
36068
- // src/agents/acp/from-bridge/handle-bridge-prompt.ts
36617
+ // src/agents/acp/from-bridge/bridge-prompt-preamble.ts
36069
36618
  var execFileAsync9 = promisify10(execFile10);
36070
36619
  async function readGitBranch(cwd) {
36071
36620
  try {
@@ -36076,6 +36625,132 @@ async function readGitBranch(cwd) {
36076
36625
  return null;
36077
36626
  }
36078
36627
  }
36628
+ async function runBridgePromptPreamble(params) {
36629
+ const { getWs, log: log2, sessionWorktreeManager, sessionId, runId, effectiveCwd } = params;
36630
+ const s = getWs();
36631
+ const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
36632
+ const repoRoots = await resolveSnapshotRepoRoots({
36633
+ worktreePaths,
36634
+ fallbackCwd: effectiveCwd,
36635
+ log: log2
36636
+ });
36637
+ if (s && sessionId) {
36638
+ const bind = await resolveBridgeQueueBindFields({
36639
+ effectiveCwd,
36640
+ worktreePaths,
36641
+ primaryRepoRoots: repoRoots,
36642
+ log: log2
36643
+ });
36644
+ if (bind) {
36645
+ sendWsMessage(s, {
36646
+ type: "bridge_queue_bind",
36647
+ sessionId,
36648
+ canonicalQueueKey: bind.canonicalQueueKey,
36649
+ repoId: bind.repoId,
36650
+ cwdAbs: bind.cwdAbs
36651
+ });
36652
+ }
36653
+ }
36654
+ if (s && sessionId) {
36655
+ const cliGitBranch = await readGitBranch(effectiveCwd);
36656
+ sendWsMessage(s, {
36657
+ type: "session_git_context_report",
36658
+ sessionId,
36659
+ cliGitBranch,
36660
+ agentUsesWorktree: sessionWorktreeManager.usesWorktreeSession(sessionId)
36661
+ });
36662
+ }
36663
+ if (s && sessionId && runId) {
36664
+ const cap = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false, error: "No git repos" };
36665
+ sendWsMessage(s, {
36666
+ type: "pre_turn_snapshot_report",
36667
+ sessionId,
36668
+ turnId: runId,
36669
+ captured: cap.ok
36670
+ });
36671
+ }
36672
+ }
36673
+ function parseChangeSummarySnapshots(raw) {
36674
+ if (!Array.isArray(raw) || raw.length === 0) return void 0;
36675
+ const out = [];
36676
+ for (const item of raw) {
36677
+ if (!item || typeof item !== "object") continue;
36678
+ const o = item;
36679
+ const path34 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
36680
+ if (!path34) continue;
36681
+ const row = { path: path34 };
36682
+ if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
36683
+ if (typeof o.oldText === "string") row.oldText = o.oldText;
36684
+ if (typeof o.newText === "string") row.newText = o.newText;
36685
+ if (o.directoryRemoved === true) row.directoryRemoved = true;
36686
+ out.push(row);
36687
+ }
36688
+ return out.length > 0 ? out : void 0;
36689
+ }
36690
+ function parseFollowUpFieldsFromPromptMessage(msg) {
36691
+ const followUpCatalogPromptId = typeof msg.followUpCatalogPromptId === "string" && msg.followUpCatalogPromptId.trim() !== "" ? msg.followUpCatalogPromptId.trim() : null;
36692
+ const rawPaths = msg.sessionChangeSummaryFilePaths;
36693
+ const sessionChangeSummaryFilePaths = Array.isArray(rawPaths) ? rawPaths.filter((p) => typeof p === "string" && p.trim() !== "").map((p) => p.trim()) : void 0;
36694
+ const sessionChangeSummaryFileSnapshots = parseChangeSummarySnapshots(msg.sessionChangeSummaryFileSnapshots);
36695
+ return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
36696
+ }
36697
+
36698
+ // src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
36699
+ function decryptChangeSummaryFileInput(row, e2ee) {
36700
+ if (!e2ee) return row;
36701
+ for (const field of ["path", "patchContent", "oldText", "newText"]) {
36702
+ const raw = row[field];
36703
+ if (typeof raw !== "string" || raw.trim() === "") continue;
36704
+ let o;
36705
+ try {
36706
+ o = JSON.parse(raw);
36707
+ } catch {
36708
+ continue;
36709
+ }
36710
+ if (!isE2eeEnvelope(o.ee)) continue;
36711
+ try {
36712
+ const d = e2ee.decryptMessage(o);
36713
+ const out = {
36714
+ path: typeof d.path === "string" ? d.path : row.path
36715
+ };
36716
+ if (d.directoryRemoved === true) out.directoryRemoved = true;
36717
+ else if (row.directoryRemoved === true) out.directoryRemoved = true;
36718
+ if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
36719
+ else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
36720
+ if (typeof d.oldText === "string") out.oldText = d.oldText;
36721
+ else if (typeof row.oldText === "string") out.oldText = row.oldText;
36722
+ if (typeof d.newText === "string") out.newText = d.newText;
36723
+ else if (typeof row.newText === "string") out.newText = row.newText;
36724
+ return out;
36725
+ } catch {
36726
+ return row;
36727
+ }
36728
+ }
36729
+ return row;
36730
+ }
36731
+
36732
+ // src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
36733
+ function hasSummarizePayload(f) {
36734
+ return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
36735
+ }
36736
+ function resolveChangeSummaryPromptForAgent(params) {
36737
+ const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
36738
+ const snaps = params.sessionChangeSummaryFileSnapshots;
36739
+ if (!isBuiltin || !snaps || snaps.length === 0) {
36740
+ return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
36741
+ }
36742
+ const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
36743
+ const withPayload = decrypted.filter(hasSummarizePayload);
36744
+ if (withPayload.length === 0) {
36745
+ return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
36746
+ }
36747
+ return {
36748
+ promptText: buildSessionChangeSummaryPrompt(withPayload),
36749
+ sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
36750
+ };
36751
+ }
36752
+
36753
+ // src/agents/acp/from-bridge/handle-bridge-prompt.ts
36079
36754
  function handleBridgePrompt(msg, deps) {
36080
36755
  const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
36081
36756
  const rawPrompt = msg.prompt;
@@ -36083,13 +36758,7 @@ function handleBridgePrompt(msg, deps) {
36083
36758
  const sessionId = msg.sessionId;
36084
36759
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
36085
36760
  const promptId = typeof msg.id === "string" ? msg.id : void 0;
36086
- const sendBridgeMessage = (message, encryptedFields = []) => {
36087
- const s = getWs();
36088
- if (!s) return false;
36089
- const wire = deps.e2ee && encryptedFields.length > 0 ? deps.e2ee.encryptFields(message, encryptedFields) : message;
36090
- sendWsMessage(s, wire);
36091
- return true;
36092
- };
36761
+ const { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate } = createBridgePromptSenders(deps, getWs);
36093
36762
  if (!promptText.trim()) {
36094
36763
  log2(
36095
36764
  `[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).`
@@ -36112,72 +36781,35 @@ function handleBridgePrompt(msg, deps) {
36112
36781
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
36113
36782
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
36114
36783
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
36115
- const sendResult2 = (result) => {
36116
- sendBridgeMessage(result, result.type === "prompt_result" ? ["output", "error"] : []);
36117
- };
36118
- const sendSessionUpdate = (payload) => {
36119
- const s = getWs();
36120
- if (!s) {
36121
- log2("[Bridge service] Session update not sent: not connected to the bridge.");
36122
- return;
36123
- }
36124
- const p = payload;
36125
- const wire = p.type === "session_update" && deps.e2ee ? deps.e2ee.encryptFields(payload, ["payload"]) : p.type === "session_file_change" && deps.e2ee ? deps.e2ee.encryptFields(payload, [
36126
- "path",
36127
- "oldText",
36128
- "newText",
36129
- "patchContent",
36130
- "isDirectory",
36131
- "directoryRemoved"
36132
- ]) : payload;
36133
- sendWsMessage(s, wire);
36134
- };
36135
36784
  async function preambleAndPrompt(resolvedCwd) {
36136
- const s = getWs();
36137
36785
  const effectiveCwd = path29.resolve(resolvedCwd ?? getBridgeWorkspaceDirectory());
36138
- const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
36139
- const repoRoots = await resolveSnapshotRepoRoots({
36140
- worktreePaths,
36141
- fallbackCwd: effectiveCwd,
36142
- log: log2
36786
+ await runBridgePromptPreamble({
36787
+ getWs,
36788
+ log: log2,
36789
+ sessionWorktreeManager,
36790
+ sessionId,
36791
+ runId,
36792
+ effectiveCwd
36143
36793
  });
36144
- if (s && sessionId) {
36145
- const bind = await resolveBridgeQueueBindFields({
36146
- effectiveCwd,
36147
- worktreePaths,
36148
- primaryRepoRoots: repoRoots,
36149
- log: log2
36150
- });
36151
- if (bind) {
36152
- sendWsMessage(s, {
36153
- type: "bridge_queue_bind",
36154
- sessionId,
36155
- canonicalQueueKey: bind.canonicalQueueKey,
36156
- repoId: bind.repoId,
36157
- cwdAbs: bind.cwdAbs
36158
- });
36159
- }
36160
- }
36161
- if (s && sessionId) {
36162
- const cliGitBranch = await readGitBranch(effectiveCwd);
36163
- sendWsMessage(s, {
36164
- type: "session_git_context_report",
36165
- sessionId,
36166
- cliGitBranch,
36167
- agentUsesWorktree: sessionWorktreeManager.usesWorktreeSession(sessionId)
36168
- });
36169
- }
36170
- if (s && sessionId && runId) {
36171
- const cap = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false, error: "No git repos" };
36172
- sendWsMessage(s, {
36173
- type: "pre_turn_snapshot_report",
36174
- sessionId,
36175
- turnId: runId,
36176
- captured: cap.ok
36177
- });
36794
+ const {
36795
+ followUpCatalogPromptId,
36796
+ sessionChangeSummaryFilePaths: pathsFromBridge,
36797
+ sessionChangeSummaryFileSnapshots
36798
+ } = parseFollowUpFieldsFromPromptMessage(msg);
36799
+ const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
36800
+ followUpCatalogPromptId,
36801
+ sessionChangeSummaryFileSnapshots,
36802
+ bridgePromptText: promptText,
36803
+ e2ee: deps.e2ee
36804
+ });
36805
+ if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
36806
+ deps.log(
36807
+ "[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
36808
+ );
36178
36809
  }
36810
+ const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
36179
36811
  acpManager.handlePrompt({
36180
- promptText,
36812
+ promptText: resolvedPromptText,
36181
36813
  promptId: msg.id,
36182
36814
  sessionId,
36183
36815
  runId,
@@ -36185,7 +36817,12 @@ function handleBridgePrompt(msg, deps) {
36185
36817
  agentType,
36186
36818
  cwd: effectiveCwd,
36187
36819
  sendResult: sendResult2,
36188
- sendSessionUpdate
36820
+ sendSessionUpdate,
36821
+ followUpCatalogPromptId,
36822
+ sessionChangeSummaryFilePaths: pathsForUpload,
36823
+ cloudApiBaseUrl: deps.cloudApiBaseUrl,
36824
+ getCloudAccessToken: deps.getCloudAccessToken,
36825
+ e2ee: deps.e2ee
36189
36826
  });
36190
36827
  }
36191
36828
  void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
@@ -37278,7 +37915,9 @@ async function createBridgeConnection(options) {
37278
37915
  sendLocalSkillsReport,
37279
37916
  reportAutoDetectedAgents,
37280
37917
  devServerManager,
37281
- e2ee
37918
+ e2ee,
37919
+ cloudApiBaseUrl: apiUrl,
37920
+ getCloudAccessToken: () => tokens.accessToken
37282
37921
  };
37283
37922
  const { connect } = createMainBridgeWebSocketLifecycle({
37284
37923
  state,