@buildautomaton/cli 0.1.21 → 0.1.23

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
@@ -23088,7 +23088,7 @@ function installBridgeProcessResilience() {
23088
23088
  }
23089
23089
 
23090
23090
  // src/cli-version.ts
23091
- var CLI_VERSION = "0.1.21".length > 0 ? "0.1.21" : "0.0.0-dev";
23091
+ var CLI_VERSION = "0.1.23".length > 0 ? "0.1.23" : "0.0.0-dev";
23092
23092
 
23093
23093
  // ../../node_modules/.pnpm/open@10.2.0/node_modules/open/index.js
23094
23094
  import process7 from "node:process";
@@ -24006,330 +24006,757 @@ function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
24006
24006
  return getBridgeRoot();
24007
24007
  }
24008
24008
 
24009
- // src/git/session-git-queue.ts
24010
- import { execFile as execFile7 } from "node:child_process";
24011
- import { readFile, stat } from "node:fs/promises";
24012
- import { promisify as promisify7 } from "node:util";
24013
- import * as path8 from "node:path";
24009
+ // ../types/src/work-items.ts
24010
+ init_zod();
24011
+ var WorkItemStatusSchema = external_exports.enum(["backlog", "in-progress", "completed"]);
24012
+ var WorkItemProgressSchema = external_exports.object({
24013
+ remainingCriteria: external_exports.array(external_exports.string()).default([]),
24014
+ openQuestions: external_exports.array(external_exports.string()).default([]),
24015
+ assignedTo: external_exports.enum(["agent", "human-product", "human-expert"]).optional()
24016
+ });
24017
+ var ChangeSchema = external_exports.object({
24018
+ id: external_exports.string(),
24019
+ description: external_exports.string(),
24020
+ buildingBlockId: external_exports.string(),
24021
+ buildingBlockType: external_exports.enum(["function", "workflow", "connector", "ui-component", "app-fragment", "application", "project"]),
24022
+ action: external_exports.enum(["create", "update", "split", "combine"])
24023
+ });
24024
+ var CompletionCriterionSchema = external_exports.object({
24025
+ id: external_exports.string(),
24026
+ description: external_exports.string(),
24027
+ type: external_exports.enum(["write-code", "write-tests", "verify-tests", "other"]),
24028
+ verified: external_exports.boolean().default(false)
24029
+ });
24030
+ var WorkItemPrioritySchema = external_exports.enum(["low", "medium", "high", "critical"]);
24031
+ var IterationPhaseSchema = external_exports.enum(["analysis", "implementation", "verify", "reprioritize", "completed"]);
24032
+ var WorkItemDependencySchema = external_exports.object({
24033
+ type: external_exports.enum(["work-item"]),
24034
+ id: external_exports.string()
24035
+ });
24036
+ var WorkItemSchema = external_exports.object({
24037
+ id: external_exports.string(),
24038
+ sessionId: external_exports.string().optional(),
24039
+ summary: external_exports.string().optional(),
24040
+ description: external_exports.string(),
24041
+ status: WorkItemStatusSchema,
24042
+ buildingBlockId: external_exports.string().optional(),
24043
+ buildingBlockType: external_exports.enum(["function", "workflow", "connector", "ui-component", "app-fragment", "application", "project"]),
24044
+ changes: external_exports.array(ChangeSchema).default([]),
24045
+ completionCriteria: external_exports.array(CompletionCriterionSchema).default([]),
24046
+ priority: WorkItemPrioritySchema.default("medium"),
24047
+ dependencies: external_exports.array(WorkItemDependencySchema).default([]),
24048
+ assignedToUserId: external_exports.string().optional()
24049
+ });
24014
24050
 
24015
- // src/git/pre-turn-snapshot.ts
24016
- import * as fs8 from "node:fs";
24017
- import * as path7 from "node:path";
24018
- import { execFile as execFile6 } from "node:child_process";
24019
- import { promisify as promisify6 } from "node:util";
24051
+ // ../types/src/user-profiles.ts
24052
+ init_zod();
24053
+ var UserWorkspaceProfileSchema = external_exports.object({
24054
+ id: external_exports.string(),
24055
+ workspaceId: external_exports.string(),
24056
+ userId: external_exports.string(),
24057
+ roleDescription: external_exports.string().optional(),
24058
+ expertiseAreas: external_exports.array(external_exports.string()),
24059
+ preferences: external_exports.record(external_exports.unknown()).optional(),
24060
+ learnings: external_exports.array(external_exports.string())
24061
+ });
24020
24062
 
24021
- // src/git/discover-repos.ts
24022
- import * as fs7 from "node:fs";
24023
- import * as path6 from "node:path";
24063
+ // ../types/src/runtime.ts
24064
+ init_zod();
24065
+ var WorkspaceOwnerInfoSchema = external_exports.object({
24066
+ ownerId: external_exports.string(),
24067
+ ownerName: external_exports.string().optional(),
24068
+ ownerEmail: external_exports.string().optional(),
24069
+ ownerProfilePictureUrl: external_exports.string().optional()
24070
+ });
24071
+ var WorkspaceRuntimeEntrySchema = external_exports.object({
24072
+ workspaceId: external_exports.string(),
24073
+ path: external_exports.string(),
24074
+ name: external_exports.string().optional(),
24075
+ owner: WorkspaceOwnerInfoSchema.optional(),
24076
+ isOwner: external_exports.boolean().optional()
24077
+ });
24024
24078
 
24025
- // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
24026
- var import_file_exists = __toESM(require_dist(), 1);
24027
- var import_debug = __toESM(require_src(), 1);
24028
- var import_promise_deferred = __toESM(require_dist2(), 1);
24029
- var import_promise_deferred2 = __toESM(require_dist2(), 1);
24030
- import { Buffer as Buffer22 } from "node:buffer";
24031
- import { spawn as spawn3 } from "child_process";
24032
- import { normalize as normalize2 } from "node:path";
24033
- import { EventEmitter } from "node:events";
24034
- var __defProp2 = Object.defineProperty;
24035
- var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
24036
- var __getOwnPropNames2 = Object.getOwnPropertyNames;
24037
- var __hasOwnProp2 = Object.prototype.hasOwnProperty;
24038
- var __esm2 = (fn, res) => function __init() {
24039
- return fn && (res = (0, fn[__getOwnPropNames2(fn)[0]])(fn = 0)), res;
24040
- };
24041
- var __commonJS2 = (cb, mod) => function __require2() {
24042
- return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
24043
- };
24044
- var __export2 = (target, all) => {
24045
- for (var name in all)
24046
- __defProp2(target, name, { get: all[name], enumerable: true });
24047
- };
24048
- var __copyProps2 = (to, from, except, desc) => {
24049
- if (from && typeof from === "object" || typeof from === "function") {
24050
- for (let key of __getOwnPropNames2(from))
24051
- if (!__hasOwnProp2.call(to, key) && key !== except)
24052
- __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
24053
- }
24054
- return to;
24055
- };
24056
- var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
24057
- function pathspec(...paths) {
24058
- const key = new String(paths);
24059
- cache.set(key, paths);
24060
- return key;
24061
- }
24062
- function isPathSpec(path34) {
24063
- return path34 instanceof String && cache.has(path34);
24064
- }
24065
- function toPaths(pathSpec) {
24066
- return cache.get(pathSpec) || [];
24067
- }
24068
- var cache;
24069
- var init_pathspec = __esm2({
24070
- "src/lib/args/pathspec.ts"() {
24071
- "use strict";
24072
- cache = /* @__PURE__ */ new WeakMap();
24073
- }
24079
+ // ../types/src/agent.ts
24080
+ init_zod();
24081
+ var ProjectContextSchema = external_exports.object({
24082
+ projectId: external_exports.string(),
24083
+ context: external_exports.record(external_exports.unknown()).default({}),
24084
+ updatedAt: external_exports.string()
24074
24085
  });
24075
- var GitError;
24076
- var init_git_error = __esm2({
24077
- "src/lib/errors/git-error.ts"() {
24078
- "use strict";
24079
- GitError = class extends Error {
24080
- constructor(task, message) {
24081
- super(message);
24082
- this.task = task;
24083
- Object.setPrototypeOf(this, new.target.prototype);
24084
- }
24085
- };
24086
- }
24086
+ var WebSocketMessageTypeSchema = external_exports.enum([
24087
+ "plan-update",
24088
+ "work-item-update",
24089
+ "work-item-added",
24090
+ "work-item-removed",
24091
+ "project-processing-start",
24092
+ "project-processing-update",
24093
+ "project-processing-complete",
24094
+ "project-processing-error",
24095
+ "file-tool-request",
24096
+ "file-tool-response",
24097
+ "file-generated"
24098
+ ]);
24099
+ var WebSocketMessageSchema = external_exports.object({
24100
+ type: WebSocketMessageTypeSchema,
24101
+ contextId: external_exports.string().optional(),
24102
+ data: external_exports.any().optional(),
24103
+ error: external_exports.string().optional()
24087
24104
  });
24088
- var GitResponseError;
24089
- var init_git_response_error = __esm2({
24090
- "src/lib/errors/git-response-error.ts"() {
24091
- "use strict";
24092
- init_git_error();
24093
- GitResponseError = class extends GitError {
24094
- constructor(git, message) {
24095
- super(void 0, message || String(git));
24096
- this.git = git;
24097
- }
24098
- };
24099
- }
24105
+
24106
+ // ../types/src/checkpoints.ts
24107
+ init_zod();
24108
+ var CheckpointKindSchema = external_exports.enum(["daily", "weekly", "overall"]);
24109
+ var CheckpointSummarySchema = external_exports.object({
24110
+ id: external_exports.string(),
24111
+ kind: CheckpointKindSchema,
24112
+ /** ISO date for daily (YYYY-MM-DD), ISO week for weekly, null for overall */
24113
+ periodKey: external_exports.string().nullable(),
24114
+ summary: external_exports.string(),
24115
+ createdAt: external_exports.string(),
24116
+ updatedAt: external_exports.string()
24100
24117
  });
24101
- var TaskConfigurationError;
24102
- var init_task_configuration_error = __esm2({
24103
- "src/lib/errors/task-configuration-error.ts"() {
24104
- "use strict";
24105
- init_git_error();
24106
- TaskConfigurationError = class extends GitError {
24107
- constructor(message) {
24108
- super(void 0, message);
24109
- }
24110
- };
24111
- }
24118
+
24119
+ // ../types/src/threads.ts
24120
+ init_zod();
24121
+ var ThreadMetaSchema = external_exports.object({
24122
+ threadId: external_exports.string(),
24123
+ workspaceId: external_exports.string(),
24124
+ /** External source (e.g. slack, discord); null if internal-only */
24125
+ externalSource: external_exports.string().nullable(),
24126
+ /** Id in the external system (e.g. channel_id + thread_ts) */
24127
+ externalId: external_exports.string().nullable(),
24128
+ title: external_exports.string().optional(),
24129
+ createdAt: external_exports.string(),
24130
+ updatedAt: external_exports.string()
24112
24131
  });
24113
- function asFunction(source) {
24114
- if (typeof source !== "function") {
24115
- return NOOP;
24116
- }
24117
- return source;
24118
- }
24119
- function isUserFunction(source) {
24120
- return typeof source === "function" && source !== NOOP;
24121
- }
24122
- function splitOn(input, char) {
24123
- const index = input.indexOf(char);
24124
- if (index <= 0) {
24125
- return [input, ""];
24126
- }
24127
- return [input.substr(0, index), input.substr(index + 1)];
24128
- }
24129
- function first(input, offset = 0) {
24130
- return isArrayLike(input) && input.length > offset ? input[offset] : void 0;
24131
- }
24132
- function last(input, offset = 0) {
24133
- if (isArrayLike(input) && input.length > offset) {
24134
- return input[input.length - 1 - offset];
24135
- }
24136
- }
24137
- function isArrayLike(input) {
24138
- return filterHasLength(input);
24139
- }
24140
- function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
24141
- return input.split(separator).reduce((output, line) => {
24142
- const lineContent = trimmed2 ? line.trim() : line;
24143
- if (lineContent) {
24144
- output.push(lineContent);
24145
- }
24146
- return output;
24147
- }, []);
24148
- }
24149
- function forEachLineWithContent(input, callback) {
24150
- return toLinesWithContent(input, true).map((line) => callback(line));
24151
- }
24152
- function folderExists(path34) {
24153
- return (0, import_file_exists.exists)(path34, import_file_exists.FOLDER);
24154
- }
24155
- function append(target, item) {
24156
- if (Array.isArray(target)) {
24157
- if (!target.includes(item)) {
24158
- target.push(item);
24159
- }
24160
- } else {
24161
- target.add(item);
24162
- }
24163
- return item;
24164
- }
24165
- function including(target, item) {
24166
- if (Array.isArray(target) && !target.includes(item)) {
24167
- target.push(item);
24168
- }
24169
- return target;
24170
- }
24171
- function remove(target, item) {
24172
- if (Array.isArray(target)) {
24173
- const index = target.indexOf(item);
24174
- if (index >= 0) {
24175
- target.splice(index, 1);
24176
- }
24177
- } else {
24178
- target.delete(item);
24179
- }
24180
- return item;
24181
- }
24182
- function asArray(source) {
24183
- return Array.isArray(source) ? source : [source];
24184
- }
24185
- function asCamelCase(str) {
24186
- return str.replace(/[\s-]+(.)/g, (_all, chr) => {
24187
- return chr.toUpperCase();
24188
- });
24189
- }
24190
- function asStringArray(source) {
24191
- return asArray(source).map((item) => {
24192
- return item instanceof String ? item : String(item);
24193
- });
24132
+ var ThreadMessageSchema = external_exports.object({
24133
+ messageId: external_exports.string(),
24134
+ threadId: external_exports.string(),
24135
+ /** Role: user, assistant, system */
24136
+ role: external_exports.enum(["user", "assistant", "system"]),
24137
+ content: external_exports.string(),
24138
+ /** Optional reference to a ContentItem (e.g. doc, Notion page) */
24139
+ contentItemId: external_exports.string().nullable(),
24140
+ /** External message id if synced from external chat */
24141
+ externalId: external_exports.string().nullable(),
24142
+ createdAt: external_exports.string(),
24143
+ updatedAt: external_exports.string()
24144
+ });
24145
+ var ThreadCheckpointSummarySchema = CheckpointSummarySchema.extend({
24146
+ threadId: external_exports.string()
24147
+ });
24148
+
24149
+ // ../types/src/content-items.ts
24150
+ init_zod();
24151
+ var ContentSourceSchema = external_exports.enum(["notion", "doc", "slack_thread", "other"]);
24152
+ var ContentItemMetaSchema = external_exports.object({
24153
+ contentId: external_exports.string(),
24154
+ workspaceId: external_exports.string(),
24155
+ source: ContentSourceSchema,
24156
+ /** Id in the external system (e.g. Notion page id, doc url) */
24157
+ externalId: external_exports.string(),
24158
+ /** If source is slack_thread, points to Thread DO id */
24159
+ threadId: external_exports.string().nullable(),
24160
+ title: external_exports.string().optional(),
24161
+ createdAt: external_exports.string(),
24162
+ updatedAt: external_exports.string()
24163
+ });
24164
+ var ContentStorageRefSchema = external_exports.object({
24165
+ storageKey: external_exports.string(),
24166
+ /** Optional: mime type or format hint */
24167
+ contentType: external_exports.string().optional()
24168
+ });
24169
+ var ContentCheckpointSummarySchema = CheckpointSummarySchema.extend({
24170
+ contentId: external_exports.string()
24171
+ });
24172
+
24173
+ // ../types/src/stories.ts
24174
+ init_zod();
24175
+ var StoryMetaSchema = external_exports.object({
24176
+ storyId: external_exports.string(),
24177
+ workspaceId: external_exports.string(),
24178
+ title: external_exports.string(),
24179
+ /** feature | bug | epic */
24180
+ kind: external_exports.enum(["feature", "bug", "epic"]).default("feature"),
24181
+ createdAt: external_exports.string(),
24182
+ updatedAt: external_exports.string()
24183
+ });
24184
+ var StoryContentItemRefSchema = external_exports.object({
24185
+ id: external_exports.string(),
24186
+ storyId: external_exports.string(),
24187
+ contentItemId: external_exports.string(),
24188
+ /** Snapshot summary when added to story (or updated) */
24189
+ summary: external_exports.string(),
24190
+ orderIndex: external_exports.number().default(0),
24191
+ createdAt: external_exports.string(),
24192
+ updatedAt: external_exports.string()
24193
+ });
24194
+ var StoryCheckpointSummarySchema = CheckpointSummarySchema.extend({
24195
+ storyId: external_exports.string()
24196
+ });
24197
+
24198
+ // ../types/src/sessions.ts
24199
+ init_zod();
24200
+ var BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID = "__builtin_change_summary__";
24201
+ var SessionMetaSchema = external_exports.object({
24202
+ sessionId: external_exports.string(),
24203
+ workspaceId: external_exports.string(),
24204
+ title: external_exports.string().optional(),
24205
+ createdAt: external_exports.string(),
24206
+ updatedAt: external_exports.string()
24207
+ });
24208
+ var SessionPromptSchema = external_exports.object({
24209
+ id: external_exports.string(),
24210
+ sessionId: external_exports.string(),
24211
+ /** text | resource */
24212
+ type: external_exports.enum(["text", "resource"]).default("text"),
24213
+ text: external_exports.string().optional(),
24214
+ resourceUri: external_exports.string().optional(),
24215
+ createdAt: external_exports.string()
24216
+ });
24217
+ var SessionResponseSchema = external_exports.object({
24218
+ id: external_exports.string(),
24219
+ sessionId: external_exports.string(),
24220
+ promptId: external_exports.string(),
24221
+ /** message | completion */
24222
+ kind: external_exports.enum(["message", "completion"]),
24223
+ content: external_exports.string().optional(),
24224
+ /** For completion: stopReason etc. */
24225
+ stopReason: external_exports.string().optional(),
24226
+ createdAt: external_exports.string()
24227
+ });
24228
+ var SessionToolCallSchema = external_exports.object({
24229
+ id: external_exports.string(),
24230
+ sessionId: external_exports.string(),
24231
+ promptId: external_exports.string(),
24232
+ name: external_exports.string(),
24233
+ params: external_exports.record(external_exports.unknown()).optional(),
24234
+ result: external_exports.record(external_exports.unknown()).optional(),
24235
+ createdAt: external_exports.string()
24236
+ });
24237
+ var SessionThreadRefSchema = external_exports.object({
24238
+ sessionId: external_exports.string(),
24239
+ threadId: external_exports.string(),
24240
+ addedAt: external_exports.string()
24241
+ });
24242
+ var USER_ENDED_SESSION_TURN_ERROR_TEXT = "Stopped by user";
24243
+ var LEGACY_USER_ENDED_SESSION_TURN_ERRORS = ["Removed from queue", "Cancelled before start"];
24244
+ function isUserEndedSessionTurnErrorText(errorText) {
24245
+ const raw = (errorText ?? "").trim();
24246
+ if (!raw) return false;
24247
+ const firstLine = raw.split(/\r?\n/)[0]?.trim() ?? "";
24248
+ if (firstLine === USER_ENDED_SESSION_TURN_ERROR_TEXT) return true;
24249
+ if (LEGACY_USER_ENDED_SESSION_TURN_ERRORS.includes(firstLine)) return true;
24250
+ if (raw === USER_ENDED_SESSION_TURN_ERROR_TEXT) return true;
24251
+ return LEGACY_USER_ENDED_SESSION_TURN_ERRORS.includes(raw);
24194
24252
  }
24195
- function asNumber(source, onNaN = 0) {
24196
- if (source == null) {
24197
- return onNaN;
24198
- }
24199
- const num = parseInt(source, 10);
24200
- return Number.isNaN(num) ? onNaN : num;
24253
+
24254
+ // ../types/src/change-summary-path.ts
24255
+ function normalizeRepoRelativePath(p) {
24256
+ let t = p.trim().replace(/\\/g, "/");
24257
+ while (t.startsWith("./")) t = t.slice(2);
24258
+ return t.replace(/\/+/g, "/");
24201
24259
  }
24202
- function prefixedArray(input, prefix) {
24203
- const output = [];
24204
- for (let i = 0, max = input.length; i < max; i++) {
24205
- output.push(prefix, input[i]);
24260
+ function resolveChangeSummaryPathAgainstAllowed(rawPath, allowed) {
24261
+ const trimmed2 = rawPath.trim();
24262
+ if (!trimmed2) return null;
24263
+ if (allowed.has(trimmed2)) return trimmed2;
24264
+ const n = normalizeRepoRelativePath(trimmed2);
24265
+ if (allowed.has(n)) return n;
24266
+ for (const a of allowed) {
24267
+ if (normalizeRepoRelativePath(a) === n) return a;
24206
24268
  }
24207
- return output;
24269
+ return null;
24208
24270
  }
24209
- function bufferToString(input) {
24210
- return (Array.isArray(input) ? Buffer22.concat(input) : input).toString("utf-8");
24271
+
24272
+ // ../types/src/parse-change-summary-json.ts
24273
+ function clampSummaryToAtMostTwoLines(summary) {
24274
+ const lines = summary.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
24275
+ return lines.slice(0, 2).join("\n");
24211
24276
  }
24212
- function pick2(source, properties) {
24213
- const out = {};
24214
- properties.forEach((key) => {
24215
- if (source[key] !== void 0) {
24216
- out[key] = source[key];
24277
+ function parseChangeSummaryJson(raw, allowedPaths, options) {
24278
+ if (raw == null || raw.trim() === "") return [];
24279
+ let text = raw.trim();
24280
+ const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
24281
+ if (fence?.[1]) text = fence[1].trim();
24282
+ let parsed;
24283
+ try {
24284
+ parsed = JSON.parse(text);
24285
+ } catch {
24286
+ const start = text.indexOf("[");
24287
+ const end = text.lastIndexOf("]");
24288
+ if (start < 0 || end <= start) return [];
24289
+ try {
24290
+ parsed = JSON.parse(text.slice(start, end + 1));
24291
+ } catch {
24292
+ return [];
24217
24293
  }
24218
- });
24219
- return out;
24220
- }
24221
- function delay(duration3 = 0) {
24222
- return new Promise((done) => setTimeout(done, duration3));
24223
- }
24224
- function orVoid(input) {
24225
- if (input === false) {
24226
- return void 0;
24227
24294
  }
24228
- return input;
24229
- }
24230
- var NULL;
24231
- var NOOP;
24232
- var objectToString;
24233
- var init_util3 = __esm2({
24234
- "src/lib/utils/util.ts"() {
24235
- "use strict";
24236
- init_argument_filters();
24237
- NULL = "\0";
24238
- NOOP = () => {
24239
- };
24240
- objectToString = Object.prototype.toString.call.bind(Object.prototype.toString);
24295
+ const rows = [];
24296
+ let arr = [];
24297
+ if (Array.isArray(parsed)) {
24298
+ arr = parsed;
24299
+ } else if (parsed && typeof parsed === "object" && Array.isArray(parsed.files)) {
24300
+ arr = parsed.files;
24241
24301
  }
24242
- });
24243
- function filterType(input, filter, def) {
24244
- if (filter(input)) {
24245
- return input;
24302
+ const skip = options?.skipPathAllowlist === true;
24303
+ for (const item of arr) {
24304
+ if (!item || typeof item !== "object") continue;
24305
+ const o = item;
24306
+ const rawPath = typeof o.path === "string" ? o.path.trim() : "";
24307
+ const summary = typeof o.summary === "string" ? o.summary.trim() : "";
24308
+ if (!rawPath || !summary) continue;
24309
+ const path34 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
24310
+ if (!path34) continue;
24311
+ rows.push({ path: path34, summary: clampSummaryToAtMostTwoLines(summary) });
24246
24312
  }
24247
- return arguments.length > 2 ? def : void 0;
24248
- }
24249
- function filterPrimitives(input, omit2) {
24250
- const type = isPathSpec(input) ? "string" : typeof input;
24251
- return /number|string|boolean/.test(type) && (!omit2 || !omit2.includes(type));
24252
- }
24253
- function filterPlainObject(input) {
24254
- return !!input && objectToString(input) === "[object Object]";
24255
- }
24256
- function filterFunction(input) {
24257
- return typeof input === "function";
24313
+ return rows;
24258
24314
  }
24259
- var filterArray;
24260
- var filterNumber;
24261
- var filterString;
24262
- var filterStringOrStringArray;
24263
- var filterHasLength;
24264
- var init_argument_filters = __esm2({
24265
- "src/lib/utils/argument-filters.ts"() {
24266
- "use strict";
24267
- init_pathspec();
24268
- init_util3();
24269
- filterArray = (input) => {
24270
- return Array.isArray(input);
24271
- };
24272
- filterNumber = (input) => {
24273
- return typeof input === "number";
24274
- };
24275
- filterString = (input) => {
24276
- return typeof input === "string";
24277
- };
24278
- filterStringOrStringArray = (input) => {
24279
- return filterString(input) || Array.isArray(input) && input.every(filterString);
24280
- };
24281
- filterHasLength = (input) => {
24282
- if (input == null || "number|boolean|function".includes(typeof input)) {
24283
- return false;
24284
- }
24285
- return typeof input.length === "number";
24286
- };
24315
+
24316
+ // ../types/src/build-change-summary-prompt.ts
24317
+ var PATCH_PREVIEW_MAX = 12e3;
24318
+ function clip(s, max) {
24319
+ if (s.length <= max) return s;
24320
+ return `${s.slice(0, max)}
24321
+
24322
+ \u2026(truncated, ${s.length - max} more characters)`;
24323
+ }
24324
+ function buildSessionChangeSummaryPrompt(files) {
24325
+ const lines = [
24326
+ "You are the same agent that produced the changes below. Summarize **your own** edits so a reader can scan them quickly.",
24327
+ "",
24328
+ "Write in second person (you / your): what you changed in each path and why it matters.",
24329
+ "",
24330
+ "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.",
24331
+ "",
24332
+ "## How to format your reply (machine parsing)",
24333
+ "",
24334
+ "- 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.",
24335
+ "- Inside that fence: **nothing except** one valid JSON value \u2014 the array described below. No trailing commentary inside the fence.",
24336
+ "- **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.",
24337
+ "- If you add optional plain English before the fence (e.g. one short sentence), keep it **separate**: end that sentence, blank line, then ```json.",
24338
+ '- In each `"summary"` string, avoid raw double-quote characters, or escape them as `\\"` so the JSON parses.',
24339
+ "",
24340
+ "JSON shape **inside the fence** (array only):",
24341
+ '[{"path":"<file path exactly as given>","summary":"<one line, or two short lines separated by \\n>"}]',
24342
+ "",
24343
+ "Rules:",
24344
+ "- Include **exactly one** object per file path listed below (same path strings).",
24345
+ "- If a path is a removed directory, state briefly what you removed.",
24346
+ "- Do not invent paths; use only the paths provided.",
24347
+ "",
24348
+ "## Files you changed",
24349
+ ""
24350
+ ];
24351
+ for (const f of files) {
24352
+ lines.push(`### ${f.path}`);
24353
+ if (f.directoryRemoved) {
24354
+ lines.push("(directory removed)");
24355
+ lines.push("");
24356
+ continue;
24357
+ }
24358
+ if (f.patchContent && f.patchContent.trim() !== "") {
24359
+ lines.push("```diff");
24360
+ lines.push(clip(f.patchContent.trim(), PATCH_PREVIEW_MAX));
24361
+ lines.push("```");
24362
+ } else if (f.oldText != null || f.newText != null) {
24363
+ const oldT = (f.oldText ?? "").trim();
24364
+ const newT = (f.newText ?? "").trim();
24365
+ lines.push("Previous snippet:", clip(oldT, 6e3));
24366
+ lines.push("New snippet:", clip(newT, 6e3));
24367
+ } else {
24368
+ lines.push("(no diff body stored for this path)");
24369
+ }
24370
+ lines.push("");
24371
+ }
24372
+ return lines.join("\n");
24373
+ }
24374
+
24375
+ // ../types/src/dedupe-session-file-changes-by-path.ts
24376
+ function defaultRichness(c) {
24377
+ const patch = typeof c.patchContent === "string" ? c.patchContent.length : 0;
24378
+ const nt = typeof c.newText === "string" ? c.newText.length : 0;
24379
+ const ot = typeof c.oldText === "string" ? c.oldText.length : 0;
24380
+ const dir = c.directoryRemoved === true ? 8 : 0;
24381
+ return (patch > 0 ? 4 : 0) + (nt > 0 ? 2 : 0) + (ot > 0 ? 1 : 0) + dir;
24382
+ }
24383
+ function dedupeSessionFileChangesByPath(items, richness = (item) => defaultRichness(item)) {
24384
+ const byPath = /* @__PURE__ */ new Map();
24385
+ for (const item of items) {
24386
+ const p = typeof item.path === "string" ? item.path.trim() : "";
24387
+ if (!p) continue;
24388
+ const prev = byPath.get(p);
24389
+ if (!prev || richness(item) >= richness(prev)) byPath.set(p, item);
24287
24390
  }
24391
+ return Array.from(byPath.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v);
24392
+ }
24393
+
24394
+ // ../types/src/artifacts.ts
24395
+ init_zod();
24396
+ var ArtifactMetaSchema = external_exports.object({
24397
+ artifactId: external_exports.string(),
24398
+ workspaceId: external_exports.string(),
24399
+ /** Slug for permalink: /workspaces/:wid/artifacts/:slug */
24400
+ permalinkSlug: external_exports.string(),
24401
+ title: external_exports.string(),
24402
+ /** e.g. summary_report, build_log */
24403
+ type: external_exports.string().default("report"),
24404
+ /** Optional session that produced this artifact */
24405
+ sessionId: external_exports.string().nullable(),
24406
+ createdAt: external_exports.string(),
24407
+ updatedAt: external_exports.string()
24288
24408
  });
24289
- var ExitCodes;
24290
- var init_exit_codes = __esm2({
24291
- "src/lib/utils/exit-codes.ts"() {
24409
+
24410
+ // ../types/src/templates.ts
24411
+ init_zod();
24412
+ var TemplateMetaSchema = external_exports.object({
24413
+ templateId: external_exports.string(),
24414
+ workspaceId: external_exports.string(),
24415
+ name: external_exports.string(),
24416
+ /** e.g. summary_report, build_log */
24417
+ artifactType: external_exports.string().optional(),
24418
+ createdAt: external_exports.string(),
24419
+ updatedAt: external_exports.string()
24420
+ });
24421
+
24422
+ // ../types/src/git-repos.ts
24423
+ init_zod();
24424
+ var GitRepoMetaSchema = external_exports.object({
24425
+ /** Stable id for the repo (e.g. hash of normalized canonical URL). Used for DO idFromName. */
24426
+ repoId: external_exports.string(),
24427
+ /** Canonical external URL (e.g. https://github.com/org/repo). Normalize before storing. */
24428
+ canonicalUrl: external_exports.string().url(),
24429
+ /** Optional workspace this repo was first linked in. */
24430
+ workspaceId: external_exports.string().nullable(),
24431
+ displayName: external_exports.string().optional(),
24432
+ createdAt: external_exports.string(),
24433
+ updatedAt: external_exports.string()
24434
+ });
24435
+
24436
+ // src/git/session-git-queue.ts
24437
+ import { execFile as execFile7 } from "node:child_process";
24438
+ import { readFile, stat } from "node:fs/promises";
24439
+ import { promisify as promisify7 } from "node:util";
24440
+ import * as path8 from "node:path";
24441
+
24442
+ // src/git/pre-turn-snapshot.ts
24443
+ import * as fs8 from "node:fs";
24444
+ import * as path7 from "node:path";
24445
+ import { execFile as execFile6 } from "node:child_process";
24446
+ import { promisify as promisify6 } from "node:util";
24447
+
24448
+ // src/git/discover-repos.ts
24449
+ import * as fs7 from "node:fs";
24450
+ import * as path6 from "node:path";
24451
+
24452
+ // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
24453
+ var import_file_exists = __toESM(require_dist(), 1);
24454
+ var import_debug = __toESM(require_src(), 1);
24455
+ var import_promise_deferred = __toESM(require_dist2(), 1);
24456
+ var import_promise_deferred2 = __toESM(require_dist2(), 1);
24457
+ import { Buffer as Buffer22 } from "node:buffer";
24458
+ import { spawn as spawn3 } from "child_process";
24459
+ import { normalize as normalize2 } from "node:path";
24460
+ import { EventEmitter } from "node:events";
24461
+ var __defProp2 = Object.defineProperty;
24462
+ var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
24463
+ var __getOwnPropNames2 = Object.getOwnPropertyNames;
24464
+ var __hasOwnProp2 = Object.prototype.hasOwnProperty;
24465
+ var __esm2 = (fn, res) => function __init() {
24466
+ return fn && (res = (0, fn[__getOwnPropNames2(fn)[0]])(fn = 0)), res;
24467
+ };
24468
+ var __commonJS2 = (cb, mod) => function __require2() {
24469
+ return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
24470
+ };
24471
+ var __export2 = (target, all) => {
24472
+ for (var name in all)
24473
+ __defProp2(target, name, { get: all[name], enumerable: true });
24474
+ };
24475
+ var __copyProps2 = (to, from, except, desc) => {
24476
+ if (from && typeof from === "object" || typeof from === "function") {
24477
+ for (let key of __getOwnPropNames2(from))
24478
+ if (!__hasOwnProp2.call(to, key) && key !== except)
24479
+ __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
24480
+ }
24481
+ return to;
24482
+ };
24483
+ var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
24484
+ function pathspec(...paths) {
24485
+ const key = new String(paths);
24486
+ cache.set(key, paths);
24487
+ return key;
24488
+ }
24489
+ function isPathSpec(path34) {
24490
+ return path34 instanceof String && cache.has(path34);
24491
+ }
24492
+ function toPaths(pathSpec) {
24493
+ return cache.get(pathSpec) || [];
24494
+ }
24495
+ var cache;
24496
+ var init_pathspec = __esm2({
24497
+ "src/lib/args/pathspec.ts"() {
24292
24498
  "use strict";
24293
- ExitCodes = /* @__PURE__ */ ((ExitCodes2) => {
24294
- ExitCodes2[ExitCodes2["SUCCESS"] = 0] = "SUCCESS";
24295
- ExitCodes2[ExitCodes2["ERROR"] = 1] = "ERROR";
24296
- ExitCodes2[ExitCodes2["NOT_FOUND"] = -2] = "NOT_FOUND";
24297
- ExitCodes2[ExitCodes2["UNCLEAN"] = 128] = "UNCLEAN";
24298
- return ExitCodes2;
24299
- })(ExitCodes || {});
24499
+ cache = /* @__PURE__ */ new WeakMap();
24300
24500
  }
24301
24501
  });
24302
- var GitOutputStreams;
24303
- var init_git_output_streams = __esm2({
24304
- "src/lib/utils/git-output-streams.ts"() {
24502
+ var GitError;
24503
+ var init_git_error = __esm2({
24504
+ "src/lib/errors/git-error.ts"() {
24305
24505
  "use strict";
24306
- GitOutputStreams = class _GitOutputStreams {
24307
- constructor(stdOut, stdErr) {
24308
- this.stdOut = stdOut;
24309
- this.stdErr = stdErr;
24310
- }
24311
- asStrings() {
24312
- return new _GitOutputStreams(this.stdOut.toString("utf8"), this.stdErr.toString("utf8"));
24506
+ GitError = class extends Error {
24507
+ constructor(task, message) {
24508
+ super(message);
24509
+ this.task = task;
24510
+ Object.setPrototypeOf(this, new.target.prototype);
24313
24511
  }
24314
24512
  };
24315
24513
  }
24316
24514
  });
24317
- function useMatchesDefault() {
24318
- throw new Error(`LineParser:useMatches not implemented`);
24319
- }
24320
- var LineParser;
24321
- var RemoteLineParser;
24322
- var init_line_parser = __esm2({
24323
- "src/lib/utils/line-parser.ts"() {
24515
+ var GitResponseError;
24516
+ var init_git_response_error = __esm2({
24517
+ "src/lib/errors/git-response-error.ts"() {
24324
24518
  "use strict";
24325
- LineParser = class {
24326
- constructor(regExp, useMatches) {
24327
- this.matches = [];
24328
- this.useMatches = useMatchesDefault;
24329
- this.parse = (line, target) => {
24330
- this.resetMatches();
24331
- if (!this._regExp.every((reg, index) => this.addMatch(reg, index, line(index)))) {
24332
- return false;
24519
+ init_git_error();
24520
+ GitResponseError = class extends GitError {
24521
+ constructor(git, message) {
24522
+ super(void 0, message || String(git));
24523
+ this.git = git;
24524
+ }
24525
+ };
24526
+ }
24527
+ });
24528
+ var TaskConfigurationError;
24529
+ var init_task_configuration_error = __esm2({
24530
+ "src/lib/errors/task-configuration-error.ts"() {
24531
+ "use strict";
24532
+ init_git_error();
24533
+ TaskConfigurationError = class extends GitError {
24534
+ constructor(message) {
24535
+ super(void 0, message);
24536
+ }
24537
+ };
24538
+ }
24539
+ });
24540
+ function asFunction(source) {
24541
+ if (typeof source !== "function") {
24542
+ return NOOP;
24543
+ }
24544
+ return source;
24545
+ }
24546
+ function isUserFunction(source) {
24547
+ return typeof source === "function" && source !== NOOP;
24548
+ }
24549
+ function splitOn(input, char) {
24550
+ const index = input.indexOf(char);
24551
+ if (index <= 0) {
24552
+ return [input, ""];
24553
+ }
24554
+ return [input.substr(0, index), input.substr(index + 1)];
24555
+ }
24556
+ function first(input, offset = 0) {
24557
+ return isArrayLike(input) && input.length > offset ? input[offset] : void 0;
24558
+ }
24559
+ function last(input, offset = 0) {
24560
+ if (isArrayLike(input) && input.length > offset) {
24561
+ return input[input.length - 1 - offset];
24562
+ }
24563
+ }
24564
+ function isArrayLike(input) {
24565
+ return filterHasLength(input);
24566
+ }
24567
+ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
24568
+ return input.split(separator).reduce((output, line) => {
24569
+ const lineContent = trimmed2 ? line.trim() : line;
24570
+ if (lineContent) {
24571
+ output.push(lineContent);
24572
+ }
24573
+ return output;
24574
+ }, []);
24575
+ }
24576
+ function forEachLineWithContent(input, callback) {
24577
+ return toLinesWithContent(input, true).map((line) => callback(line));
24578
+ }
24579
+ function folderExists(path34) {
24580
+ return (0, import_file_exists.exists)(path34, import_file_exists.FOLDER);
24581
+ }
24582
+ function append(target, item) {
24583
+ if (Array.isArray(target)) {
24584
+ if (!target.includes(item)) {
24585
+ target.push(item);
24586
+ }
24587
+ } else {
24588
+ target.add(item);
24589
+ }
24590
+ return item;
24591
+ }
24592
+ function including(target, item) {
24593
+ if (Array.isArray(target) && !target.includes(item)) {
24594
+ target.push(item);
24595
+ }
24596
+ return target;
24597
+ }
24598
+ function remove(target, item) {
24599
+ if (Array.isArray(target)) {
24600
+ const index = target.indexOf(item);
24601
+ if (index >= 0) {
24602
+ target.splice(index, 1);
24603
+ }
24604
+ } else {
24605
+ target.delete(item);
24606
+ }
24607
+ return item;
24608
+ }
24609
+ function asArray(source) {
24610
+ return Array.isArray(source) ? source : [source];
24611
+ }
24612
+ function asCamelCase(str) {
24613
+ return str.replace(/[\s-]+(.)/g, (_all, chr) => {
24614
+ return chr.toUpperCase();
24615
+ });
24616
+ }
24617
+ function asStringArray(source) {
24618
+ return asArray(source).map((item) => {
24619
+ return item instanceof String ? item : String(item);
24620
+ });
24621
+ }
24622
+ function asNumber(source, onNaN = 0) {
24623
+ if (source == null) {
24624
+ return onNaN;
24625
+ }
24626
+ const num = parseInt(source, 10);
24627
+ return Number.isNaN(num) ? onNaN : num;
24628
+ }
24629
+ function prefixedArray(input, prefix) {
24630
+ const output = [];
24631
+ for (let i = 0, max = input.length; i < max; i++) {
24632
+ output.push(prefix, input[i]);
24633
+ }
24634
+ return output;
24635
+ }
24636
+ function bufferToString(input) {
24637
+ return (Array.isArray(input) ? Buffer22.concat(input) : input).toString("utf-8");
24638
+ }
24639
+ function pick2(source, properties) {
24640
+ const out = {};
24641
+ properties.forEach((key) => {
24642
+ if (source[key] !== void 0) {
24643
+ out[key] = source[key];
24644
+ }
24645
+ });
24646
+ return out;
24647
+ }
24648
+ function delay(duration3 = 0) {
24649
+ return new Promise((done) => setTimeout(done, duration3));
24650
+ }
24651
+ function orVoid(input) {
24652
+ if (input === false) {
24653
+ return void 0;
24654
+ }
24655
+ return input;
24656
+ }
24657
+ var NULL;
24658
+ var NOOP;
24659
+ var objectToString;
24660
+ var init_util3 = __esm2({
24661
+ "src/lib/utils/util.ts"() {
24662
+ "use strict";
24663
+ init_argument_filters();
24664
+ NULL = "\0";
24665
+ NOOP = () => {
24666
+ };
24667
+ objectToString = Object.prototype.toString.call.bind(Object.prototype.toString);
24668
+ }
24669
+ });
24670
+ function filterType(input, filter, def) {
24671
+ if (filter(input)) {
24672
+ return input;
24673
+ }
24674
+ return arguments.length > 2 ? def : void 0;
24675
+ }
24676
+ function filterPrimitives(input, omit2) {
24677
+ const type = isPathSpec(input) ? "string" : typeof input;
24678
+ return /number|string|boolean/.test(type) && (!omit2 || !omit2.includes(type));
24679
+ }
24680
+ function filterPlainObject(input) {
24681
+ return !!input && objectToString(input) === "[object Object]";
24682
+ }
24683
+ function filterFunction(input) {
24684
+ return typeof input === "function";
24685
+ }
24686
+ var filterArray;
24687
+ var filterNumber;
24688
+ var filterString;
24689
+ var filterStringOrStringArray;
24690
+ var filterHasLength;
24691
+ var init_argument_filters = __esm2({
24692
+ "src/lib/utils/argument-filters.ts"() {
24693
+ "use strict";
24694
+ init_pathspec();
24695
+ init_util3();
24696
+ filterArray = (input) => {
24697
+ return Array.isArray(input);
24698
+ };
24699
+ filterNumber = (input) => {
24700
+ return typeof input === "number";
24701
+ };
24702
+ filterString = (input) => {
24703
+ return typeof input === "string";
24704
+ };
24705
+ filterStringOrStringArray = (input) => {
24706
+ return filterString(input) || Array.isArray(input) && input.every(filterString);
24707
+ };
24708
+ filterHasLength = (input) => {
24709
+ if (input == null || "number|boolean|function".includes(typeof input)) {
24710
+ return false;
24711
+ }
24712
+ return typeof input.length === "number";
24713
+ };
24714
+ }
24715
+ });
24716
+ var ExitCodes;
24717
+ var init_exit_codes = __esm2({
24718
+ "src/lib/utils/exit-codes.ts"() {
24719
+ "use strict";
24720
+ ExitCodes = /* @__PURE__ */ ((ExitCodes2) => {
24721
+ ExitCodes2[ExitCodes2["SUCCESS"] = 0] = "SUCCESS";
24722
+ ExitCodes2[ExitCodes2["ERROR"] = 1] = "ERROR";
24723
+ ExitCodes2[ExitCodes2["NOT_FOUND"] = -2] = "NOT_FOUND";
24724
+ ExitCodes2[ExitCodes2["UNCLEAN"] = 128] = "UNCLEAN";
24725
+ return ExitCodes2;
24726
+ })(ExitCodes || {});
24727
+ }
24728
+ });
24729
+ var GitOutputStreams;
24730
+ var init_git_output_streams = __esm2({
24731
+ "src/lib/utils/git-output-streams.ts"() {
24732
+ "use strict";
24733
+ GitOutputStreams = class _GitOutputStreams {
24734
+ constructor(stdOut, stdErr) {
24735
+ this.stdOut = stdOut;
24736
+ this.stdErr = stdErr;
24737
+ }
24738
+ asStrings() {
24739
+ return new _GitOutputStreams(this.stdOut.toString("utf8"), this.stdErr.toString("utf8"));
24740
+ }
24741
+ };
24742
+ }
24743
+ });
24744
+ function useMatchesDefault() {
24745
+ throw new Error(`LineParser:useMatches not implemented`);
24746
+ }
24747
+ var LineParser;
24748
+ var RemoteLineParser;
24749
+ var init_line_parser = __esm2({
24750
+ "src/lib/utils/line-parser.ts"() {
24751
+ "use strict";
24752
+ LineParser = class {
24753
+ constructor(regExp, useMatches) {
24754
+ this.matches = [];
24755
+ this.useMatches = useMatchesDefault;
24756
+ this.parse = (line, target) => {
24757
+ this.resetMatches();
24758
+ if (!this._regExp.every((reg, index) => this.addMatch(reg, index, line(index)))) {
24759
+ return false;
24333
24760
  }
24334
24761
  return this.useMatches(target, this.prepareMatches()) !== false;
24335
24762
  };
@@ -28521,748 +28948,332 @@ function timeoutPlugin({
28521
28948
  };
28522
28949
  }
28523
28950
  }
28524
- init_pathspec();
28525
- function suffixPathsPlugin() {
28526
- return {
28527
- type: "spawn.args",
28528
- action(data) {
28529
- const prefix = [];
28530
- let suffix;
28531
- function append2(args) {
28532
- (suffix = suffix || []).push(...args);
28533
- }
28534
- for (let i = 0; i < data.length; i++) {
28535
- const param = data[i];
28536
- if (isPathSpec(param)) {
28537
- append2(toPaths(param));
28538
- continue;
28539
- }
28540
- if (param === "--") {
28541
- append2(
28542
- data.slice(i + 1).flatMap((item) => isPathSpec(item) && toPaths(item) || item)
28543
- );
28544
- break;
28545
- }
28546
- prefix.push(param);
28547
- }
28548
- return !suffix ? prefix : [...prefix, "--", ...suffix.map(String)];
28549
- }
28550
- };
28551
- }
28552
- init_utils();
28553
- var Git = require_git();
28554
- function gitInstanceFactory(baseDir, options) {
28555
- const plugins = new PluginStore();
28556
- const config2 = createInstanceConfig(
28557
- baseDir && (typeof baseDir === "string" ? { baseDir } : baseDir) || {},
28558
- options
28559
- );
28560
- if (!folderExists(config2.baseDir)) {
28561
- throw new GitConstructError(
28562
- config2,
28563
- `Cannot use simple-git on a directory that does not exist`
28564
- );
28565
- }
28566
- if (Array.isArray(config2.config)) {
28567
- plugins.add(commandConfigPrefixingPlugin(config2.config));
28568
- }
28569
- plugins.add(blockUnsafeOperationsPlugin(config2.unsafe));
28570
- plugins.add(suffixPathsPlugin());
28571
- plugins.add(completionDetectionPlugin(config2.completion));
28572
- config2.abort && plugins.add(abortPlugin(config2.abort));
28573
- config2.progress && plugins.add(progressMonitorPlugin(config2.progress));
28574
- config2.timeout && plugins.add(timeoutPlugin(config2.timeout));
28575
- config2.spawnOptions && plugins.add(spawnOptionsPlugin(config2.spawnOptions));
28576
- plugins.add(errorDetectionPlugin(errorDetectionHandler(true)));
28577
- config2.errors && plugins.add(errorDetectionPlugin(config2.errors));
28578
- customBinaryPlugin(plugins, config2.binary, config2.unsafe?.allowUnsafeCustomBinary);
28579
- return new Git(config2, plugins);
28580
- }
28581
- init_git_response_error();
28582
- var simpleGit = gitInstanceFactory;
28583
-
28584
- // src/git/remote-origin-url.ts
28585
- async function getRemoteOriginUrl(gitDir) {
28586
- try {
28587
- const git = simpleGit(gitDir);
28588
- const remotes = await git.getRemotes(true);
28589
- const list = Array.isArray(remotes) ? remotes : [];
28590
- const origin = list.find((r) => r.name === "origin");
28591
- if (!origin?.refs?.fetch && !origin?.refs?.push) return "";
28592
- return (origin.refs.fetch ?? origin.refs.push ?? "").trim();
28593
- } catch {
28594
- return "";
28595
- }
28596
- }
28597
-
28598
- // src/git/is-git-repo.ts
28599
- async function isGitRepoDirectory(dirPath) {
28600
- try {
28601
- return await simpleGit(dirPath).checkIsRepo();
28602
- } catch {
28603
- return false;
28604
- }
28605
- }
28606
-
28607
- // src/git/discover-repos.ts
28608
- async function discoverGitRepos(cwd = getBridgeRoot()) {
28609
- const result = [];
28610
- const cwdResolved = path6.resolve(cwd);
28611
- if (await isGitRepoDirectory(cwdResolved)) {
28612
- const remoteUrl = await getRemoteOriginUrl(cwdResolved);
28613
- result.push({ absolutePath: cwdResolved, remoteUrl });
28614
- }
28615
- let entries;
28616
- try {
28617
- entries = fs7.readdirSync(cwdResolved, { withFileTypes: true });
28618
- } catch {
28619
- return result;
28620
- }
28621
- for (const ent of entries) {
28622
- if (!ent.isDirectory()) continue;
28623
- const childPath = path6.join(cwdResolved, ent.name);
28624
- if (await isGitRepoDirectory(childPath)) {
28625
- const remoteUrl = await getRemoteOriginUrl(childPath);
28626
- result.push({ absolutePath: childPath, remoteUrl });
28627
- }
28628
- }
28629
- return result;
28630
- }
28631
- async function discoverGitReposUnderRoot(rootPath) {
28632
- const root = path6.resolve(rootPath);
28633
- const roots = [];
28634
- async function walk(dir) {
28635
- if (await isGitRepoDirectory(dir)) {
28636
- roots.push(path6.resolve(dir));
28637
- return;
28638
- }
28639
- let entries;
28640
- try {
28641
- entries = fs7.readdirSync(dir, { withFileTypes: true });
28642
- } catch {
28643
- return;
28644
- }
28645
- for (const ent of entries) {
28646
- if (!ent.isDirectory() || ent.name === ".git") continue;
28647
- await walk(path6.join(dir, ent.name));
28648
- }
28649
- }
28650
- await walk(root);
28651
- const uniq = [...new Set(roots)];
28652
- const out = [];
28653
- for (const p of uniq) {
28654
- const remoteUrl = await getRemoteOriginUrl(p);
28655
- out.push({ absolutePath: p, remoteUrl });
28656
- }
28657
- return out;
28658
- }
28659
-
28660
- // src/git/pre-turn-snapshot.ts
28661
- var execFileAsync5 = promisify6(execFile6);
28662
- function snapshotsDirForCwd(agentCwd) {
28663
- return path7.join(agentCwd, ".buildautomaton", "snapshots");
28664
- }
28665
- async function gitStashCreate(repoRoot, log2) {
28666
- try {
28667
- const { stdout } = await execFileAsync5("git", ["stash", "create"], {
28668
- cwd: repoRoot,
28669
- maxBuffer: 10 * 1024 * 1024
28670
- });
28671
- return stdout.trim();
28672
- } catch (e) {
28673
- log2(
28674
- `[snapshot] Git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`
28675
- );
28676
- return "";
28677
- }
28678
- }
28679
- async function gitRun(repoRoot, args, log2, label) {
28680
- try {
28681
- await execFileAsync5("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
28682
- return { ok: true };
28683
- } catch (e) {
28684
- const msg = e instanceof Error ? e.message : String(e);
28685
- log2(`[snapshot] Git ${label} failed in ${repoRoot}: ${msg}`);
28686
- return { ok: false, error: msg };
28687
- }
28688
- }
28689
- async function resolveSnapshotRepoRoots(options) {
28690
- const { worktreePaths, fallbackCwd, sessionId, log: log2 } = options;
28691
- if (worktreePaths?.length) {
28692
- const uniq = [...new Set(worktreePaths.map((p) => path7.resolve(p)))];
28693
- return uniq;
28694
- }
28695
- try {
28696
- const repos = await discoverGitReposUnderRoot(fallbackCwd);
28697
- const mapped = repos.map((r) => r.absolutePath);
28698
- const sid = sessionId?.trim();
28699
- if (sid) {
28700
- const filtered = mapped.filter((root) => path7.basename(root) === sid);
28701
- if (filtered.length > 0) return filtered;
28702
- }
28703
- return mapped;
28704
- } catch (e) {
28705
- log2(`[snapshot] Discover repositories failed: ${e instanceof Error ? e.message : String(e)}`);
28706
- return [];
28707
- }
28708
- }
28709
- async function capturePreTurnSnapshot(options) {
28710
- const { runId, repoRoots, agentCwd, log: log2 } = options;
28711
- if (!runId || !repoRoots.length) {
28712
- return { ok: false, error: "No git repos to snapshot" };
28713
- }
28714
- const repos = [];
28715
- for (const root of repoRoots) {
28716
- const stashSha = await gitStashCreate(root, log2);
28717
- repos.push({ path: root, stashSha });
28718
- }
28719
- const dir = snapshotsDirForCwd(agentCwd);
28720
- try {
28721
- fs8.mkdirSync(dir, { recursive: true });
28722
- } catch (e) {
28723
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
28724
- }
28725
- const payload = {
28726
- runId,
28727
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
28728
- repos
28729
- };
28730
- const filePath = path7.join(dir, `${runId}.json`);
28731
- try {
28732
- fs8.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
28733
- } catch (e) {
28734
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
28735
- }
28736
- const repoList = repos.map((r) => r.path).join(", ");
28737
- log2(
28738
- `[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)): ${repoList}`
28739
- );
28740
- return { ok: true, filePath, repos };
28741
- }
28742
- async function applyPreTurnSnapshot(filePath, log2) {
28743
- let data;
28744
- try {
28745
- const raw = fs8.readFileSync(filePath, "utf8");
28746
- data = JSON.parse(raw);
28747
- } catch (e) {
28748
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
28749
- }
28750
- if (!Array.isArray(data.repos)) {
28751
- return { ok: false, error: "Invalid snapshot file" };
28752
- }
28753
- for (const r of data.repos) {
28754
- if (!r.path) continue;
28755
- const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
28756
- if (!reset.ok) return reset;
28757
- const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
28758
- if (!clean.ok) return clean;
28759
- if (r.stashSha) {
28760
- const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
28761
- if (!ap.ok) return ap;
28762
- }
28763
- }
28764
- log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
28765
- return { ok: true };
28766
- }
28767
- function snapshotFilePath(agentCwd, runId) {
28768
- return path7.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
28769
- }
28770
-
28771
- // src/git/session-git-queue.ts
28772
- var execFileAsync6 = promisify7(execFile7);
28773
- var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
28774
- async function readWorkspaceFileAsUtf8(absPath) {
28775
- try {
28776
- const st = await stat(absPath);
28777
- if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
28778
- return await readFile(absPath, "utf8");
28779
- } catch {
28780
- return void 0;
28781
- }
28782
- }
28783
- async function collectTurnGitDiffFromPreTurnSnapshot(options) {
28784
- const { sessionId, runId, agentCwd, sendSessionUpdate, log: log2 } = options;
28785
- const filePath = snapshotFilePath(agentCwd, runId);
28786
- let data;
28787
- try {
28788
- const raw = await readFile(filePath, "utf8");
28789
- data = JSON.parse(raw);
28790
- } catch (e) {
28791
- log2(
28792
- `[session-git-queue] No pre-turn snapshot for run ${runId.slice(0, 8)}\u2026: ${e instanceof Error ? e.message : String(e)}`
28793
- );
28794
- return;
28795
- }
28796
- if (!Array.isArray(data.repos) || !data.repos.length) {
28797
- log2(`[session-git-queue] Empty repos in snapshot ${runId.slice(0, 8)}\u2026; skipping aggregate diff.`);
28798
- return;
28799
- }
28800
- const multiRepo = data.repos.length > 1;
28801
- for (const repo of data.repos) {
28802
- if (!repo.stashSha) continue;
28803
- let namesRaw;
28804
- try {
28805
- const { stdout } = await execFileAsync6("git", ["diff", "--name-only", repo.stashSha], {
28806
- cwd: repo.path,
28807
- maxBuffer: 10 * 1024 * 1024
28808
- });
28809
- namesRaw = stdout;
28810
- } catch (e) {
28811
- log2(
28812
- `[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
28813
- );
28814
- continue;
28815
- }
28816
- const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
28817
- const slug = path8.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
28818
- for (const rel of lines) {
28819
- if (rel.includes("..")) continue;
28820
- try {
28821
- const { stdout: patchContent } = await execFileAsync6(
28822
- "git",
28823
- ["diff", "--no-color", repo.stashSha, "--", rel],
28824
- {
28825
- cwd: repo.path,
28826
- maxBuffer: 50 * 1024 * 1024
28827
- }
28828
- );
28829
- if (!patchContent.trim()) continue;
28830
- const displayPath = multiRepo ? `${slug}/${rel}` : rel;
28831
- const workspaceFilePath = path8.join(repo.path, rel);
28832
- const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
28833
- sendSessionUpdate({
28834
- type: "session_file_change",
28835
- sessionId,
28836
- runId,
28837
- path: displayPath,
28838
- patchContent,
28839
- ...newText !== void 0 ? { newText } : {}
28840
- });
28841
- } catch (e) {
28842
- log2(
28843
- `[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
28844
- );
28845
- }
28846
- }
28847
- }
28848
- }
28849
-
28850
- // ../types/src/work-items.ts
28851
- init_zod();
28852
- var WorkItemStatusSchema = external_exports.enum(["backlog", "in-progress", "completed"]);
28853
- var WorkItemProgressSchema = external_exports.object({
28854
- remainingCriteria: external_exports.array(external_exports.string()).default([]),
28855
- openQuestions: external_exports.array(external_exports.string()).default([]),
28856
- assignedTo: external_exports.enum(["agent", "human-product", "human-expert"]).optional()
28857
- });
28858
- var ChangeSchema = external_exports.object({
28859
- id: external_exports.string(),
28860
- description: external_exports.string(),
28861
- buildingBlockId: external_exports.string(),
28862
- buildingBlockType: external_exports.enum(["function", "workflow", "connector", "ui-component", "app-fragment", "application", "project"]),
28863
- action: external_exports.enum(["create", "update", "split", "combine"])
28864
- });
28865
- var CompletionCriterionSchema = external_exports.object({
28866
- id: external_exports.string(),
28867
- description: external_exports.string(),
28868
- type: external_exports.enum(["write-code", "write-tests", "verify-tests", "other"]),
28869
- verified: external_exports.boolean().default(false)
28870
- });
28871
- var WorkItemPrioritySchema = external_exports.enum(["low", "medium", "high", "critical"]);
28872
- var IterationPhaseSchema = external_exports.enum(["analysis", "implementation", "verify", "reprioritize", "completed"]);
28873
- var WorkItemDependencySchema = external_exports.object({
28874
- type: external_exports.enum(["work-item"]),
28875
- id: external_exports.string()
28876
- });
28877
- var WorkItemSchema = external_exports.object({
28878
- id: external_exports.string(),
28879
- sessionId: external_exports.string().optional(),
28880
- summary: external_exports.string().optional(),
28881
- description: external_exports.string(),
28882
- status: WorkItemStatusSchema,
28883
- buildingBlockId: external_exports.string().optional(),
28884
- buildingBlockType: external_exports.enum(["function", "workflow", "connector", "ui-component", "app-fragment", "application", "project"]),
28885
- changes: external_exports.array(ChangeSchema).default([]),
28886
- completionCriteria: external_exports.array(CompletionCriterionSchema).default([]),
28887
- priority: WorkItemPrioritySchema.default("medium"),
28888
- dependencies: external_exports.array(WorkItemDependencySchema).default([]),
28889
- assignedToUserId: external_exports.string().optional()
28890
- });
28891
-
28892
- // ../types/src/user-profiles.ts
28893
- init_zod();
28894
- var UserWorkspaceProfileSchema = external_exports.object({
28895
- id: external_exports.string(),
28896
- workspaceId: external_exports.string(),
28897
- userId: external_exports.string(),
28898
- roleDescription: external_exports.string().optional(),
28899
- expertiseAreas: external_exports.array(external_exports.string()),
28900
- preferences: external_exports.record(external_exports.unknown()).optional(),
28901
- learnings: external_exports.array(external_exports.string())
28902
- });
28903
-
28904
- // ../types/src/runtime.ts
28905
- init_zod();
28906
- var WorkspaceOwnerInfoSchema = external_exports.object({
28907
- ownerId: external_exports.string(),
28908
- ownerName: external_exports.string().optional(),
28909
- ownerEmail: external_exports.string().optional(),
28910
- ownerProfilePictureUrl: external_exports.string().optional()
28911
- });
28912
- var WorkspaceRuntimeEntrySchema = external_exports.object({
28913
- workspaceId: external_exports.string(),
28914
- path: external_exports.string(),
28915
- name: external_exports.string().optional(),
28916
- owner: WorkspaceOwnerInfoSchema.optional(),
28917
- isOwner: external_exports.boolean().optional()
28918
- });
28919
-
28920
- // ../types/src/agent.ts
28921
- init_zod();
28922
- var ProjectContextSchema = external_exports.object({
28923
- projectId: external_exports.string(),
28924
- context: external_exports.record(external_exports.unknown()).default({}),
28925
- updatedAt: external_exports.string()
28926
- });
28927
- var WebSocketMessageTypeSchema = external_exports.enum([
28928
- "plan-update",
28929
- "work-item-update",
28930
- "work-item-added",
28931
- "work-item-removed",
28932
- "project-processing-start",
28933
- "project-processing-update",
28934
- "project-processing-complete",
28935
- "project-processing-error",
28936
- "file-tool-request",
28937
- "file-tool-response",
28938
- "file-generated"
28939
- ]);
28940
- var WebSocketMessageSchema = external_exports.object({
28941
- type: WebSocketMessageTypeSchema,
28942
- contextId: external_exports.string().optional(),
28943
- data: external_exports.any().optional(),
28944
- error: external_exports.string().optional()
28945
- });
28946
-
28947
- // ../types/src/checkpoints.ts
28948
- init_zod();
28949
- var CheckpointKindSchema = external_exports.enum(["daily", "weekly", "overall"]);
28950
- var CheckpointSummarySchema = external_exports.object({
28951
- id: external_exports.string(),
28952
- kind: CheckpointKindSchema,
28953
- /** ISO date for daily (YYYY-MM-DD), ISO week for weekly, null for overall */
28954
- periodKey: external_exports.string().nullable(),
28955
- summary: external_exports.string(),
28956
- createdAt: external_exports.string(),
28957
- updatedAt: external_exports.string()
28958
- });
28959
-
28960
- // ../types/src/threads.ts
28961
- init_zod();
28962
- var ThreadMetaSchema = external_exports.object({
28963
- threadId: external_exports.string(),
28964
- workspaceId: external_exports.string(),
28965
- /** External source (e.g. slack, discord); null if internal-only */
28966
- externalSource: external_exports.string().nullable(),
28967
- /** Id in the external system (e.g. channel_id + thread_ts) */
28968
- externalId: external_exports.string().nullable(),
28969
- title: external_exports.string().optional(),
28970
- createdAt: external_exports.string(),
28971
- updatedAt: external_exports.string()
28972
- });
28973
- var ThreadMessageSchema = external_exports.object({
28974
- messageId: external_exports.string(),
28975
- threadId: external_exports.string(),
28976
- /** Role: user, assistant, system */
28977
- role: external_exports.enum(["user", "assistant", "system"]),
28978
- content: external_exports.string(),
28979
- /** Optional reference to a ContentItem (e.g. doc, Notion page) */
28980
- contentItemId: external_exports.string().nullable(),
28981
- /** External message id if synced from external chat */
28982
- externalId: external_exports.string().nullable(),
28983
- createdAt: external_exports.string(),
28984
- updatedAt: external_exports.string()
28985
- });
28986
- var ThreadCheckpointSummarySchema = CheckpointSummarySchema.extend({
28987
- threadId: external_exports.string()
28988
- });
28989
-
28990
- // ../types/src/content-items.ts
28991
- init_zod();
28992
- var ContentSourceSchema = external_exports.enum(["notion", "doc", "slack_thread", "other"]);
28993
- var ContentItemMetaSchema = external_exports.object({
28994
- contentId: external_exports.string(),
28995
- workspaceId: external_exports.string(),
28996
- source: ContentSourceSchema,
28997
- /** Id in the external system (e.g. Notion page id, doc url) */
28998
- externalId: external_exports.string(),
28999
- /** If source is slack_thread, points to Thread DO id */
29000
- threadId: external_exports.string().nullable(),
29001
- title: external_exports.string().optional(),
29002
- createdAt: external_exports.string(),
29003
- updatedAt: external_exports.string()
29004
- });
29005
- var ContentStorageRefSchema = external_exports.object({
29006
- storageKey: external_exports.string(),
29007
- /** Optional: mime type or format hint */
29008
- contentType: external_exports.string().optional()
29009
- });
29010
- var ContentCheckpointSummarySchema = CheckpointSummarySchema.extend({
29011
- contentId: external_exports.string()
29012
- });
29013
-
29014
- // ../types/src/stories.ts
29015
- init_zod();
29016
- var StoryMetaSchema = external_exports.object({
29017
- storyId: external_exports.string(),
29018
- workspaceId: external_exports.string(),
29019
- title: external_exports.string(),
29020
- /** feature | bug | epic */
29021
- kind: external_exports.enum(["feature", "bug", "epic"]).default("feature"),
29022
- createdAt: external_exports.string(),
29023
- updatedAt: external_exports.string()
29024
- });
29025
- var StoryContentItemRefSchema = external_exports.object({
29026
- id: external_exports.string(),
29027
- storyId: external_exports.string(),
29028
- contentItemId: external_exports.string(),
29029
- /** Snapshot summary when added to story (or updated) */
29030
- summary: external_exports.string(),
29031
- orderIndex: external_exports.number().default(0),
29032
- createdAt: external_exports.string(),
29033
- updatedAt: external_exports.string()
29034
- });
29035
- var StoryCheckpointSummarySchema = CheckpointSummarySchema.extend({
29036
- storyId: external_exports.string()
29037
- });
29038
-
29039
- // ../types/src/sessions.ts
29040
- init_zod();
29041
- var BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID = "__builtin_change_summary__";
29042
- var SessionMetaSchema = external_exports.object({
29043
- sessionId: external_exports.string(),
29044
- workspaceId: external_exports.string(),
29045
- title: external_exports.string().optional(),
29046
- createdAt: external_exports.string(),
29047
- updatedAt: external_exports.string()
29048
- });
29049
- var SessionPromptSchema = external_exports.object({
29050
- id: external_exports.string(),
29051
- sessionId: external_exports.string(),
29052
- /** text | resource */
29053
- type: external_exports.enum(["text", "resource"]).default("text"),
29054
- text: external_exports.string().optional(),
29055
- resourceUri: external_exports.string().optional(),
29056
- createdAt: external_exports.string()
29057
- });
29058
- var SessionResponseSchema = external_exports.object({
29059
- id: external_exports.string(),
29060
- sessionId: external_exports.string(),
29061
- promptId: external_exports.string(),
29062
- /** message | completion */
29063
- kind: external_exports.enum(["message", "completion"]),
29064
- content: external_exports.string().optional(),
29065
- /** For completion: stopReason etc. */
29066
- stopReason: external_exports.string().optional(),
29067
- createdAt: external_exports.string()
29068
- });
29069
- var SessionToolCallSchema = external_exports.object({
29070
- id: external_exports.string(),
29071
- sessionId: external_exports.string(),
29072
- promptId: external_exports.string(),
29073
- name: external_exports.string(),
29074
- params: external_exports.record(external_exports.unknown()).optional(),
29075
- result: external_exports.record(external_exports.unknown()).optional(),
29076
- createdAt: external_exports.string()
29077
- });
29078
- var SessionThreadRefSchema = external_exports.object({
29079
- sessionId: external_exports.string(),
29080
- threadId: external_exports.string(),
29081
- addedAt: external_exports.string()
29082
- });
29083
-
29084
- // ../types/src/change-summary-path.ts
29085
- function normalizeRepoRelativePath(p) {
29086
- let t = p.trim().replace(/\\/g, "/");
29087
- while (t.startsWith("./")) t = t.slice(2);
29088
- return t.replace(/\/+/g, "/");
28951
+ init_pathspec();
28952
+ function suffixPathsPlugin() {
28953
+ return {
28954
+ type: "spawn.args",
28955
+ action(data) {
28956
+ const prefix = [];
28957
+ let suffix;
28958
+ function append2(args) {
28959
+ (suffix = suffix || []).push(...args);
28960
+ }
28961
+ for (let i = 0; i < data.length; i++) {
28962
+ const param = data[i];
28963
+ if (isPathSpec(param)) {
28964
+ append2(toPaths(param));
28965
+ continue;
28966
+ }
28967
+ if (param === "--") {
28968
+ append2(
28969
+ data.slice(i + 1).flatMap((item) => isPathSpec(item) && toPaths(item) || item)
28970
+ );
28971
+ break;
28972
+ }
28973
+ prefix.push(param);
28974
+ }
28975
+ return !suffix ? prefix : [...prefix, "--", ...suffix.map(String)];
28976
+ }
28977
+ };
29089
28978
  }
29090
- function resolveChangeSummaryPathAgainstAllowed(rawPath, allowed) {
29091
- const trimmed2 = rawPath.trim();
29092
- if (!trimmed2) return null;
29093
- if (allowed.has(trimmed2)) return trimmed2;
29094
- const n = normalizeRepoRelativePath(trimmed2);
29095
- if (allowed.has(n)) return n;
29096
- for (const a of allowed) {
29097
- if (normalizeRepoRelativePath(a) === n) return a;
28979
+ init_utils();
28980
+ var Git = require_git();
28981
+ function gitInstanceFactory(baseDir, options) {
28982
+ const plugins = new PluginStore();
28983
+ const config2 = createInstanceConfig(
28984
+ baseDir && (typeof baseDir === "string" ? { baseDir } : baseDir) || {},
28985
+ options
28986
+ );
28987
+ if (!folderExists(config2.baseDir)) {
28988
+ throw new GitConstructError(
28989
+ config2,
28990
+ `Cannot use simple-git on a directory that does not exist`
28991
+ );
28992
+ }
28993
+ if (Array.isArray(config2.config)) {
28994
+ plugins.add(commandConfigPrefixingPlugin(config2.config));
28995
+ }
28996
+ plugins.add(blockUnsafeOperationsPlugin(config2.unsafe));
28997
+ plugins.add(suffixPathsPlugin());
28998
+ plugins.add(completionDetectionPlugin(config2.completion));
28999
+ config2.abort && plugins.add(abortPlugin(config2.abort));
29000
+ config2.progress && plugins.add(progressMonitorPlugin(config2.progress));
29001
+ config2.timeout && plugins.add(timeoutPlugin(config2.timeout));
29002
+ config2.spawnOptions && plugins.add(spawnOptionsPlugin(config2.spawnOptions));
29003
+ plugins.add(errorDetectionPlugin(errorDetectionHandler(true)));
29004
+ config2.errors && plugins.add(errorDetectionPlugin(config2.errors));
29005
+ customBinaryPlugin(plugins, config2.binary, config2.unsafe?.allowUnsafeCustomBinary);
29006
+ return new Git(config2, plugins);
29007
+ }
29008
+ init_git_response_error();
29009
+ var simpleGit = gitInstanceFactory;
29010
+
29011
+ // src/git/remote-origin-url.ts
29012
+ async function getRemoteOriginUrl(gitDir) {
29013
+ try {
29014
+ const git = simpleGit(gitDir);
29015
+ const remotes = await git.getRemotes(true);
29016
+ const list = Array.isArray(remotes) ? remotes : [];
29017
+ const origin = list.find((r) => r.name === "origin");
29018
+ if (!origin?.refs?.fetch && !origin?.refs?.push) return "";
29019
+ return (origin.refs.fetch ?? origin.refs.push ?? "").trim();
29020
+ } catch {
29021
+ return "";
29098
29022
  }
29099
- return null;
29100
29023
  }
29101
29024
 
29102
- // ../types/src/parse-change-summary-json.ts
29103
- function clampSummaryToAtMostTwoLines(summary) {
29104
- const lines = summary.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
29105
- return lines.slice(0, 2).join("\n");
29025
+ // src/git/is-git-repo.ts
29026
+ async function isGitRepoDirectory(dirPath) {
29027
+ try {
29028
+ return await simpleGit(dirPath).checkIsRepo();
29029
+ } catch {
29030
+ return false;
29031
+ }
29106
29032
  }
29107
- function parseChangeSummaryJson(raw, allowedPaths, options) {
29108
- if (raw == null || raw.trim() === "") return [];
29109
- let text = raw.trim();
29110
- const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
29111
- if (fence?.[1]) text = fence[1].trim();
29112
- let parsed;
29033
+
29034
+ // src/git/discover-repos.ts
29035
+ async function discoverGitRepos(cwd = getBridgeRoot()) {
29036
+ const result = [];
29037
+ const cwdResolved = path6.resolve(cwd);
29038
+ if (await isGitRepoDirectory(cwdResolved)) {
29039
+ const remoteUrl = await getRemoteOriginUrl(cwdResolved);
29040
+ result.push({ absolutePath: cwdResolved, remoteUrl });
29041
+ }
29042
+ let entries;
29113
29043
  try {
29114
- parsed = JSON.parse(text);
29044
+ entries = fs7.readdirSync(cwdResolved, { withFileTypes: true });
29115
29045
  } catch {
29116
- const start = text.indexOf("[");
29117
- const end = text.lastIndexOf("]");
29118
- if (start < 0 || end <= start) return [];
29046
+ return result;
29047
+ }
29048
+ for (const ent of entries) {
29049
+ if (!ent.isDirectory()) continue;
29050
+ const childPath = path6.join(cwdResolved, ent.name);
29051
+ if (await isGitRepoDirectory(childPath)) {
29052
+ const remoteUrl = await getRemoteOriginUrl(childPath);
29053
+ result.push({ absolutePath: childPath, remoteUrl });
29054
+ }
29055
+ }
29056
+ return result;
29057
+ }
29058
+ async function discoverGitReposUnderRoot(rootPath) {
29059
+ const root = path6.resolve(rootPath);
29060
+ const roots = [];
29061
+ async function walk(dir) {
29062
+ if (await isGitRepoDirectory(dir)) {
29063
+ roots.push(path6.resolve(dir));
29064
+ return;
29065
+ }
29066
+ let entries;
29119
29067
  try {
29120
- parsed = JSON.parse(text.slice(start, end + 1));
29068
+ entries = fs7.readdirSync(dir, { withFileTypes: true });
29121
29069
  } catch {
29122
- return [];
29070
+ return;
29071
+ }
29072
+ for (const ent of entries) {
29073
+ if (!ent.isDirectory() || ent.name === ".git") continue;
29074
+ await walk(path6.join(dir, ent.name));
29123
29075
  }
29124
29076
  }
29125
- const rows = [];
29126
- let arr = [];
29127
- if (Array.isArray(parsed)) {
29128
- arr = parsed;
29129
- } else if (parsed && typeof parsed === "object" && Array.isArray(parsed.files)) {
29130
- arr = parsed.files;
29077
+ await walk(root);
29078
+ const uniq = [...new Set(roots)];
29079
+ const out = [];
29080
+ for (const p of uniq) {
29081
+ const remoteUrl = await getRemoteOriginUrl(p);
29082
+ out.push({ absolutePath: p, remoteUrl });
29131
29083
  }
29132
- const skip = options?.skipPathAllowlist === true;
29133
- for (const item of arr) {
29134
- if (!item || typeof item !== "object") continue;
29135
- const o = item;
29136
- const rawPath = typeof o.path === "string" ? o.path.trim() : "";
29137
- const summary = typeof o.summary === "string" ? o.summary.trim() : "";
29138
- if (!rawPath || !summary) continue;
29139
- const path34 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
29140
- if (!path34) continue;
29141
- rows.push({ path: path34, summary: clampSummaryToAtMostTwoLines(summary) });
29084
+ return out;
29085
+ }
29086
+
29087
+ // src/git/pre-turn-snapshot.ts
29088
+ var execFileAsync5 = promisify6(execFile6);
29089
+ function snapshotsDirForCwd(agentCwd) {
29090
+ return path7.join(agentCwd, ".buildautomaton", "snapshots");
29091
+ }
29092
+ async function gitStashCreate(repoRoot, log2) {
29093
+ try {
29094
+ const { stdout } = await execFileAsync5("git", ["stash", "create"], {
29095
+ cwd: repoRoot,
29096
+ maxBuffer: 10 * 1024 * 1024
29097
+ });
29098
+ return stdout.trim();
29099
+ } catch (e) {
29100
+ log2(
29101
+ `[snapshot] Git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`
29102
+ );
29103
+ return "";
29104
+ }
29105
+ }
29106
+ async function gitRun(repoRoot, args, log2, label) {
29107
+ try {
29108
+ await execFileAsync5("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
29109
+ return { ok: true };
29110
+ } catch (e) {
29111
+ const msg = e instanceof Error ? e.message : String(e);
29112
+ log2(`[snapshot] Git ${label} failed in ${repoRoot}: ${msg}`);
29113
+ return { ok: false, error: msg };
29114
+ }
29115
+ }
29116
+ async function resolveSnapshotRepoRoots(options) {
29117
+ const { worktreePaths, fallbackCwd, sessionId, log: log2 } = options;
29118
+ if (worktreePaths?.length) {
29119
+ const uniq = [...new Set(worktreePaths.map((p) => path7.resolve(p)))];
29120
+ return uniq;
29121
+ }
29122
+ try {
29123
+ const repos = await discoverGitReposUnderRoot(fallbackCwd);
29124
+ const mapped = repos.map((r) => r.absolutePath);
29125
+ const sid = sessionId?.trim();
29126
+ if (sid) {
29127
+ const filtered = mapped.filter((root) => path7.basename(root) === sid);
29128
+ if (filtered.length > 0) return filtered;
29129
+ }
29130
+ return mapped;
29131
+ } catch (e) {
29132
+ log2(`[snapshot] Discover repositories failed: ${e instanceof Error ? e.message : String(e)}`);
29133
+ return [];
29134
+ }
29135
+ }
29136
+ async function capturePreTurnSnapshot(options) {
29137
+ const { runId, repoRoots, agentCwd, log: log2 } = options;
29138
+ if (!runId || !repoRoots.length) {
29139
+ return { ok: false, error: "No git repos to snapshot" };
29140
+ }
29141
+ const repos = [];
29142
+ for (const root of repoRoots) {
29143
+ const stashSha = await gitStashCreate(root, log2);
29144
+ repos.push({ path: root, stashSha });
29145
+ }
29146
+ const dir = snapshotsDirForCwd(agentCwd);
29147
+ try {
29148
+ fs8.mkdirSync(dir, { recursive: true });
29149
+ } catch (e) {
29150
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
29151
+ }
29152
+ const payload = {
29153
+ runId,
29154
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
29155
+ repos
29156
+ };
29157
+ const filePath = path7.join(dir, `${runId}.json`);
29158
+ try {
29159
+ fs8.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
29160
+ } catch (e) {
29161
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
29162
+ }
29163
+ const repoList = repos.map((r) => r.path).join(", ");
29164
+ log2(
29165
+ `[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)): ${repoList}`
29166
+ );
29167
+ return { ok: true, filePath, repos };
29168
+ }
29169
+ async function applyPreTurnSnapshot(filePath, log2) {
29170
+ let data;
29171
+ try {
29172
+ const raw = fs8.readFileSync(filePath, "utf8");
29173
+ data = JSON.parse(raw);
29174
+ } catch (e) {
29175
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
29176
+ }
29177
+ if (!Array.isArray(data.repos)) {
29178
+ return { ok: false, error: "Invalid snapshot file" };
29142
29179
  }
29143
- return rows;
29180
+ for (const r of data.repos) {
29181
+ if (!r.path) continue;
29182
+ const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
29183
+ if (!reset.ok) return reset;
29184
+ const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
29185
+ if (!clean.ok) return clean;
29186
+ if (r.stashSha) {
29187
+ const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
29188
+ if (!ap.ok) return ap;
29189
+ }
29190
+ }
29191
+ log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
29192
+ return { ok: true };
29193
+ }
29194
+ function snapshotFilePath(agentCwd, runId) {
29195
+ return path7.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
29144
29196
  }
29145
29197
 
29146
- // ../types/src/build-change-summary-prompt.ts
29147
- var PATCH_PREVIEW_MAX = 12e3;
29148
- function clip(s, max) {
29149
- if (s.length <= max) return s;
29150
- return `${s.slice(0, max)}
29151
-
29152
- \u2026(truncated, ${s.length - max} more characters)`;
29198
+ // src/git/session-git-queue.ts
29199
+ var execFileAsync6 = promisify7(execFile7);
29200
+ var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
29201
+ async function readWorkspaceFileAsUtf8(absPath) {
29202
+ try {
29203
+ const st = await stat(absPath);
29204
+ if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
29205
+ return await readFile(absPath, "utf8");
29206
+ } catch {
29207
+ return void 0;
29208
+ }
29153
29209
  }
29154
- function buildSessionChangeSummaryPrompt(files) {
29155
- const lines = [
29156
- "You are the same agent that produced the changes below. Summarize **your own** edits so a reader can scan them quickly.",
29157
- "",
29158
- "Write in second person (you / your): what you changed in each path and why it matters.",
29159
- "",
29160
- "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.",
29161
- "",
29162
- "## How to format your reply (machine parsing)",
29163
- "",
29164
- "- 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.",
29165
- "- Inside that fence: **nothing except** one valid JSON value \u2014 the array described below. No trailing commentary inside the fence.",
29166
- "- **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.",
29167
- "- If you add optional plain English before the fence (e.g. one short sentence), keep it **separate**: end that sentence, blank line, then ```json.",
29168
- '- In each `"summary"` string, avoid raw double-quote characters, or escape them as `\\"` so the JSON parses.',
29169
- "",
29170
- "JSON shape **inside the fence** (array only):",
29171
- '[{"path":"<file path exactly as given>","summary":"<one line, or two short lines separated by \\n>"}]',
29172
- "",
29173
- "Rules:",
29174
- "- Include **exactly one** object per file path listed below (same path strings).",
29175
- "- If a path is a removed directory, state briefly what you removed.",
29176
- "- Do not invent paths; use only the paths provided.",
29177
- "",
29178
- "## Files you changed",
29179
- ""
29180
- ];
29181
- for (const f of files) {
29182
- lines.push(`### ${f.path}`);
29183
- if (f.directoryRemoved) {
29184
- lines.push("(directory removed)");
29185
- lines.push("");
29210
+ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
29211
+ const { sessionId, runId, agentCwd, sendSessionUpdate, log: log2 } = options;
29212
+ const filePath = snapshotFilePath(agentCwd, runId);
29213
+ let data;
29214
+ try {
29215
+ const raw = await readFile(filePath, "utf8");
29216
+ data = JSON.parse(raw);
29217
+ } catch (e) {
29218
+ log2(
29219
+ `[session-git-queue] No pre-turn snapshot for run ${runId.slice(0, 8)}\u2026: ${e instanceof Error ? e.message : String(e)}`
29220
+ );
29221
+ return;
29222
+ }
29223
+ if (!Array.isArray(data.repos) || !data.repos.length) {
29224
+ log2(`[session-git-queue] Empty repos in snapshot ${runId.slice(0, 8)}\u2026; skipping aggregate diff.`);
29225
+ return;
29226
+ }
29227
+ const multiRepo = data.repos.length > 1;
29228
+ for (const repo of data.repos) {
29229
+ if (!repo.stashSha) continue;
29230
+ let namesRaw;
29231
+ try {
29232
+ const { stdout } = await execFileAsync6("git", ["diff", "--name-only", repo.stashSha], {
29233
+ cwd: repo.path,
29234
+ maxBuffer: 10 * 1024 * 1024
29235
+ });
29236
+ namesRaw = stdout;
29237
+ } catch (e) {
29238
+ log2(
29239
+ `[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
29240
+ );
29186
29241
  continue;
29187
29242
  }
29188
- if (f.patchContent && f.patchContent.trim() !== "") {
29189
- lines.push("```diff");
29190
- lines.push(clip(f.patchContent.trim(), PATCH_PREVIEW_MAX));
29191
- lines.push("```");
29192
- } else if (f.oldText != null || f.newText != null) {
29193
- const oldT = (f.oldText ?? "").trim();
29194
- const newT = (f.newText ?? "").trim();
29195
- lines.push("Previous snippet:", clip(oldT, 6e3));
29196
- lines.push("New snippet:", clip(newT, 6e3));
29197
- } else {
29198
- lines.push("(no diff body stored for this path)");
29243
+ const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
29244
+ const slug = path8.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
29245
+ for (const rel of lines) {
29246
+ if (rel.includes("..")) continue;
29247
+ try {
29248
+ const { stdout: patchContent } = await execFileAsync6(
29249
+ "git",
29250
+ ["diff", "--no-color", repo.stashSha, "--", rel],
29251
+ {
29252
+ cwd: repo.path,
29253
+ maxBuffer: 50 * 1024 * 1024
29254
+ }
29255
+ );
29256
+ if (!patchContent.trim()) continue;
29257
+ const displayPath = multiRepo ? `${slug}/${rel}` : rel;
29258
+ const workspaceFilePath = path8.join(repo.path, rel);
29259
+ const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
29260
+ sendSessionUpdate({
29261
+ type: "session_file_change",
29262
+ sessionId,
29263
+ runId,
29264
+ path: displayPath,
29265
+ patchContent,
29266
+ ...newText !== void 0 ? { newText } : {}
29267
+ });
29268
+ } catch (e) {
29269
+ log2(
29270
+ `[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
29271
+ );
29272
+ }
29199
29273
  }
29200
- lines.push("");
29201
- }
29202
- return lines.join("\n");
29203
- }
29204
-
29205
- // ../types/src/dedupe-session-file-changes-by-path.ts
29206
- function defaultRichness(c) {
29207
- const patch = typeof c.patchContent === "string" ? c.patchContent.length : 0;
29208
- const nt = typeof c.newText === "string" ? c.newText.length : 0;
29209
- const ot = typeof c.oldText === "string" ? c.oldText.length : 0;
29210
- const dir = c.directoryRemoved === true ? 8 : 0;
29211
- return (patch > 0 ? 4 : 0) + (nt > 0 ? 2 : 0) + (ot > 0 ? 1 : 0) + dir;
29212
- }
29213
- function dedupeSessionFileChangesByPath(items, richness = (item) => defaultRichness(item)) {
29214
- const byPath = /* @__PURE__ */ new Map();
29215
- for (const item of items) {
29216
- const p = typeof item.path === "string" ? item.path.trim() : "";
29217
- if (!p) continue;
29218
- const prev = byPath.get(p);
29219
- if (!prev || richness(item) >= richness(prev)) byPath.set(p, item);
29220
29274
  }
29221
- return Array.from(byPath.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v);
29222
29275
  }
29223
29276
 
29224
- // ../types/src/artifacts.ts
29225
- init_zod();
29226
- var ArtifactMetaSchema = external_exports.object({
29227
- artifactId: external_exports.string(),
29228
- workspaceId: external_exports.string(),
29229
- /** Slug for permalink: /workspaces/:wid/artifacts/:slug */
29230
- permalinkSlug: external_exports.string(),
29231
- title: external_exports.string(),
29232
- /** e.g. summary_report, build_log */
29233
- type: external_exports.string().default("report"),
29234
- /** Optional session that produced this artifact */
29235
- sessionId: external_exports.string().nullable(),
29236
- createdAt: external_exports.string(),
29237
- updatedAt: external_exports.string()
29238
- });
29239
-
29240
- // ../types/src/templates.ts
29241
- init_zod();
29242
- var TemplateMetaSchema = external_exports.object({
29243
- templateId: external_exports.string(),
29244
- workspaceId: external_exports.string(),
29245
- name: external_exports.string(),
29246
- /** e.g. summary_report, build_log */
29247
- artifactType: external_exports.string().optional(),
29248
- createdAt: external_exports.string(),
29249
- updatedAt: external_exports.string()
29250
- });
29251
-
29252
- // ../types/src/git-repos.ts
29253
- init_zod();
29254
- var GitRepoMetaSchema = external_exports.object({
29255
- /** Stable id for the repo (e.g. hash of normalized canonical URL). Used for DO idFromName. */
29256
- repoId: external_exports.string(),
29257
- /** Canonical external URL (e.g. https://github.com/org/repo). Normalize before storing. */
29258
- canonicalUrl: external_exports.string().url(),
29259
- /** Optional workspace this repo was first linked in. */
29260
- workspaceId: external_exports.string().nullable(),
29261
- displayName: external_exports.string().optional(),
29262
- createdAt: external_exports.string(),
29263
- updatedAt: external_exports.string()
29264
- });
29265
-
29266
29277
  // src/agents/acp/put-summarize-change-summaries.ts
29267
29278
  async function putEncryptedChangeSummaryRows(params) {
29268
29279
  const base = params.apiBaseUrl.replace(/\/+$/, "");
@@ -29412,6 +29423,8 @@ async function sendPromptToAgent(options) {
29412
29423
  log: log2
29413
29424
  });
29414
29425
  const errStr = typeof result.error === "string" ? result.error : void 0;
29426
+ const resultStop = typeof result.stopReason === "string" ? result.stopReason.trim() : "";
29427
+ const cancelledByAgent = resultStop.toLowerCase() === "cancelled" || errStr != null && isUserEndedSessionTurnErrorText(errStr);
29415
29428
  sendResult2({
29416
29429
  type: "prompt_result",
29417
29430
  id: promptId,
@@ -29419,12 +29432,17 @@ async function sendPromptToAgent(options) {
29419
29432
  ...runId ? { runId } : {},
29420
29433
  ...result,
29421
29434
  ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
29422
- ...augmentPromptResultAuthFields(agentType, errStr)
29435
+ ...augmentPromptResultAuthFields(agentType, errStr),
29436
+ ...!result.success && cancelledByAgent ? { stopReason: "cancelled" } : {}
29423
29437
  });
29424
29438
  if (!result.success) {
29425
- log2(
29426
- `[Agent] Prompt did not run successfully on the agent (no successful start/completion): ${result.error ?? "Unknown error"}`
29427
- );
29439
+ if (cancelledByAgent) {
29440
+ log2("[Agent] Run ended after stop request (stopped by user).");
29441
+ } else {
29442
+ log2(
29443
+ `[Agent] Prompt did not run successfully on the agent (no successful start/completion): ${result.error ?? "Unknown error"}`
29444
+ );
29445
+ }
29428
29446
  }
29429
29447
  } catch (err) {
29430
29448
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -29504,7 +29522,8 @@ async function createClaudeCodeAcpClient(options) {
29504
29522
  ...options,
29505
29523
  command,
29506
29524
  /** Claude-based agents sometimes ignore `session/cancel`; unblocks stop / stuck prompt. */
29507
- killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 1e3
29525
+ /** Claude Code can be slow to honor ACP `session/cancel`; give the subprocess a moment before SIGKILL (see ACP prompt-turn cancellation). */
29526
+ killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 2500
29508
29527
  });
29509
29528
  }
29510
29529
 
@@ -33768,7 +33787,6 @@ var API_TO_BRIDGE_MESSAGE_TYPES = [
33768
33787
  "session_archived",
33769
33788
  "session_discarded",
33770
33789
  "revert_turn_snapshot",
33771
- "cancel_run",
33772
33790
  "cursor_request_response",
33773
33791
  "skill_call",
33774
33792
  "file_browser_request",
@@ -33838,6 +33856,9 @@ var handleAgentConfigMessage = (msg, deps) => {
33838
33856
  handleBridgeAgentConfig(msg, deps);
33839
33857
  };
33840
33858
 
33859
+ // src/prompt-turn-queue/runner.ts
33860
+ import fs28 from "node:fs";
33861
+
33841
33862
  // src/prompt-turn-queue/client-report.ts
33842
33863
  function sendPromptQueueClientReport(ws, queues) {
33843
33864
  if (!ws) return false;
@@ -33872,6 +33893,14 @@ function queueStateFilePath(queueKey) {
33872
33893
  }
33873
33894
 
33874
33895
  // src/prompt-turn-queue/disk-store.ts
33896
+ var MERGEABLE_SERVER_STATES = /* @__PURE__ */ new Set([
33897
+ "queued",
33898
+ "requeued",
33899
+ "requeued_with_revert",
33900
+ "cancel_requested",
33901
+ "stopping",
33902
+ "discarded"
33903
+ ]);
33875
33904
  function parsePersistedQueueFile(raw) {
33876
33905
  try {
33877
33906
  const o = JSON.parse(raw);
@@ -33908,7 +33937,7 @@ function mergeServerQueueSnapshot(queueKey, serverTurns) {
33908
33937
  const lastClientState = o.lastClientState ?? null;
33909
33938
  const payload = o.payload && typeof o.payload === "object" ? o.payload : {};
33910
33939
  if (!turnId || !sessionId) continue;
33911
- if (serverState !== "queued" && serverState !== "stopping" && serverState !== "discarded") continue;
33940
+ if (!MERGEABLE_SERVER_STATES.has(String(serverState))) continue;
33912
33941
  const old = prev?.turns.find((t) => t.turnId === turnId);
33913
33942
  const mergedClient = old?.lastClientState === "running" && lastClientState == null ? "running" : lastClientState;
33914
33943
  turns.push({
@@ -33926,11 +33955,17 @@ function mergeServerQueueSnapshot(queueKey, serverTurns) {
33926
33955
 
33927
33956
  // src/prompt-turn-queue/runner.ts
33928
33957
  var runIdToQueueKey = /* @__PURE__ */ new Map();
33958
+ function isRunnableServerState(s) {
33959
+ return s === "queued" || s === "requeued" || s === "requeued_with_revert";
33960
+ }
33929
33961
  function pickNextRunnableTurn(turns) {
33930
33962
  for (const t of turns) {
33931
33963
  if (t.serverState === "discarded" || t.serverState === "stopping") continue;
33932
- if (t.serverState !== "queued") continue;
33933
- if (t.lastClientState === "running" || t.lastClientState === "stopped" || t.lastClientState === "failed") continue;
33964
+ if (t.serverState === "cancel_requested") continue;
33965
+ if (!isRunnableServerState(t.serverState)) continue;
33966
+ if (t.lastClientState === "running" || t.lastClientState === "stopped" || t.lastClientState === "failed" || t.lastClientState === "cancelled") {
33967
+ continue;
33968
+ }
33934
33969
  return t;
33935
33970
  }
33936
33971
  return null;
@@ -33938,6 +33973,25 @@ function pickNextRunnableTurn(turns) {
33938
33973
  function hasRunningTurn(turns) {
33939
33974
  return turns.some((t) => t.lastClientState === "running");
33940
33975
  }
33976
+ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
33977
+ if (next.serverState !== "requeued_with_revert") return true;
33978
+ const sid = next.sessionId;
33979
+ const pl = next.payload;
33980
+ const tid = typeof pl.snapshotRevertTurnId === "string" && pl.snapshotRevertTurnId.trim() !== "" ? pl.snapshotRevertTurnId.trim() : next.turnId;
33981
+ const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
33982
+ const file2 = snapshotFilePath(agentBase, tid);
33983
+ if (!fs28.existsSync(file2)) {
33984
+ deps.log(
33985
+ `[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
33986
+ );
33987
+ return true;
33988
+ }
33989
+ const res = await applyPreTurnSnapshot(file2, deps.log);
33990
+ if (!res.ok) {
33991
+ deps.log(`[Queue] requeued_with_revert: snapshot apply failed: ${res.error ?? "unknown"}`);
33992
+ }
33993
+ return res.ok;
33994
+ }
33941
33995
  function dispatchLocalPrompt(next, deps) {
33942
33996
  const pl = next.payload;
33943
33997
  const rawParent = pl["sessionParent"];
@@ -33960,7 +34014,7 @@ function dispatchLocalPrompt(next, deps) {
33960
34014
  };
33961
34015
  handleBridgePrompt(msg, deps);
33962
34016
  }
33963
- function applyPromptQueueStateFromServer(msg, deps) {
34017
+ async function applyPromptQueueStateFromServer(msg, deps) {
33964
34018
  const raw = msg.queues;
33965
34019
  if (!raw || typeof raw !== "object") return;
33966
34020
  const getWs = deps.getWs;
@@ -33969,6 +34023,40 @@ function applyPromptQueueStateFromServer(msg, deps) {
33969
34023
  const file2 = mergeServerQueueSnapshot(queueKey, serverTurns);
33970
34024
  writePersistedQueue(file2);
33971
34025
  }
34026
+ for (const [queueKey, serverTurns] of Object.entries(raw)) {
34027
+ if (!Array.isArray(serverTurns)) continue;
34028
+ const file2 = readPersistedQueue(queueKey);
34029
+ if (!file2) continue;
34030
+ for (const running of file2.turns.filter((t) => t.lastClientState === "running")) {
34031
+ runIdToQueueKey.set(running.turnId, queueKey);
34032
+ }
34033
+ }
34034
+ for (const [queueKey, serverTurns] of Object.entries(raw)) {
34035
+ if (!Array.isArray(serverTurns)) continue;
34036
+ const file2 = readPersistedQueue(queueKey);
34037
+ if (!file2) continue;
34038
+ const cancelRow = file2.turns.find((t) => t.serverState === "cancel_requested" && t.lastClientState === "running");
34039
+ if (cancelRow) {
34040
+ const localCancelHandled = await deps.acpManager.cancelRun(cancelRow.turnId);
34041
+ if (!localCancelHandled) {
34042
+ deps.log(
34043
+ `[Queue] server cancel_requested for ${cancelRow.turnId.slice(0, 8)}\u2026 but no local agent run is active (e.g. after CLI restart); marking cancelled and notifying bridge.`
34044
+ );
34045
+ finalizePromptTurnOnBridge(deps.getWs, cancelRow.turnId, false, { terminalClientState: "cancelled" });
34046
+ const ws = deps.getWs();
34047
+ if (ws && cancelRow.sessionId) {
34048
+ sendWsMessage(ws, {
34049
+ type: "prompt_result",
34050
+ sessionId: cancelRow.sessionId,
34051
+ runId: cancelRow.turnId,
34052
+ success: false,
34053
+ error: "Stopped by user",
34054
+ stopReason: "cancelled"
34055
+ });
34056
+ }
34057
+ }
34058
+ }
34059
+ }
33972
34060
  const report = {};
33973
34061
  const startedThisTick = /* @__PURE__ */ new Set();
33974
34062
  for (const [queueKey, serverTurns] of Object.entries(raw)) {
@@ -33978,6 +34066,12 @@ function applyPromptQueueStateFromServer(msg, deps) {
33978
34066
  if (hasRunningTurn(file2.turns)) continue;
33979
34067
  const next = pickNextRunnableTurn(file2.turns);
33980
34068
  if (!next) continue;
34069
+ if (!await runLocalRevertBeforeQueuedPrompt(next, deps)) {
34070
+ next.lastClientState = "failed";
34071
+ writePersistedQueue(file2);
34072
+ sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: next.turnId, clientState: "failed" }] });
34073
+ continue;
34074
+ }
33981
34075
  next.lastClientState = "running";
33982
34076
  writePersistedQueue(file2);
33983
34077
  runIdToQueueKey.set(next.turnId, queueKey);
@@ -33997,18 +34091,19 @@ function applyPromptQueueStateFromServer(msg, deps) {
33997
34091
  dispatchLocalPrompt(running, deps);
33998
34092
  }
33999
34093
  }
34000
- function finalizePromptTurnOnBridge(getWs, runId, success2) {
34001
- if (!runId) return;
34094
+ function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
34095
+ if (!runId) return false;
34002
34096
  const queueKey = runIdToQueueKey.get(runId);
34003
34097
  runIdToQueueKey.delete(runId);
34004
- if (!queueKey) return;
34098
+ if (!queueKey) return false;
34005
34099
  const f = readPersistedQueue(queueKey);
34006
- if (!f) return;
34100
+ if (!f) return false;
34007
34101
  const t = f.turns.find((x) => x.turnId === runId);
34008
- if (!t) return;
34009
- t.lastClientState = success2 ? "stopped" : "failed";
34102
+ if (!t) return false;
34103
+ t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
34010
34104
  writePersistedQueue(f);
34011
34105
  sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
34106
+ return true;
34012
34107
  }
34013
34108
 
34014
34109
  // src/agents/acp/from-bridge/bridge-prompt-wiring.ts
@@ -34025,7 +34120,14 @@ function createBridgePromptSenders(deps, getWs) {
34025
34120
  const encryptedFields = result.type === "prompt_result" && !skipEncryptForChangeSummaryFollowUp ? ["output", "error"] : [];
34026
34121
  sendBridgeMessage(result, encryptedFields);
34027
34122
  if (result.type === "prompt_result") {
34028
- finalizePromptTurnOnBridge(getWs, typeof result.runId === "string" ? result.runId : void 0, result.success === true);
34123
+ const pr = result;
34124
+ const cancelled = pr.stopReason === "cancelled";
34125
+ finalizePromptTurnOnBridge(
34126
+ getWs,
34127
+ typeof pr.runId === "string" ? pr.runId : void 0,
34128
+ pr.success === true,
34129
+ cancelled ? { terminalClientState: "cancelled" } : void 0
34130
+ );
34029
34131
  }
34030
34132
  };
34031
34133
  const sendSessionUpdate = (payload) => {
@@ -34290,34 +34392,9 @@ var handlePromptMessage = (msg, deps) => {
34290
34392
 
34291
34393
  // src/bridge/routing/handlers/prompt-queue-state.ts
34292
34394
  var handlePromptQueueStateMessage = (msg, deps) => {
34293
- applyPromptQueueStateFromServer(msg, deps);
34294
- };
34295
-
34296
- // src/agents/acp/from-bridge/handle-bridge-cancel-run.ts
34297
- async function handleBridgeCancelRun(msg, { log: log2, acpManager, getWs }) {
34298
- const runId = msg.runId;
34299
- if (!runId) return;
34300
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId.trim() : "";
34301
- const sent = await acpManager.cancelRun(runId);
34302
- if (sent) return;
34303
- log2(`[Agent] Cancel: no local run for ${runId.slice(0, 8)}\u2026 \u2014 reporting stopped to cloud.`);
34304
- const ws = getWs();
34305
- if (!ws || ws.readyState !== 1) return;
34306
- sendWsMessage(ws, {
34307
- type: "prompt_result",
34308
- id: `cancel-nack-${runId}`,
34309
- runId,
34310
- ...sessionId ? { sessionId } : {},
34311
- success: false,
34312
- error: "Stopped by user",
34313
- stopReason: "no_local_run"
34395
+ void applyPromptQueueStateFromServer(msg, deps).catch((err) => {
34396
+ deps.log(`[Queue] applyPromptQueueStateFromServer failed: ${err instanceof Error ? err.message : String(err)}`);
34314
34397
  });
34315
- finalizePromptTurnOnBridge(getWs, runId, false);
34316
- }
34317
-
34318
- // src/bridge/routing/handlers/cancel-run.ts
34319
- var handleCancelRunMessage = (msg, deps) => {
34320
- void handleBridgeCancelRun(msg, deps);
34321
34398
  };
34322
34399
 
34323
34400
  // src/agents/acp/from-bridge/handle-bridge-cursor-request-response.ts
@@ -34356,7 +34433,7 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
34356
34433
  };
34357
34434
 
34358
34435
  // src/files/list-dir.ts
34359
- import fs28 from "node:fs";
34436
+ import fs29 from "node:fs";
34360
34437
  import path31 from "node:path";
34361
34438
 
34362
34439
  // src/files/ensure-under-cwd.ts
@@ -34378,7 +34455,7 @@ async function listDirAsync(relativePath) {
34378
34455
  return { error: "Path is outside working directory" };
34379
34456
  }
34380
34457
  try {
34381
- const names = await fs28.promises.readdir(resolved, { withFileTypes: true });
34458
+ const names = await fs29.promises.readdir(resolved, { withFileTypes: true });
34382
34459
  const visible = names.filter((d) => !d.name.startsWith("."));
34383
34460
  const entries = [];
34384
34461
  for (let i = 0; i < visible.length; i++) {
@@ -34391,7 +34468,7 @@ async function listDirAsync(relativePath) {
34391
34468
  let isDir = d.isDirectory();
34392
34469
  if (d.isSymbolicLink()) {
34393
34470
  try {
34394
- const targetStat = await fs28.promises.stat(fullPath);
34471
+ const targetStat = await fs29.promises.stat(fullPath);
34395
34472
  isDir = targetStat.isDirectory();
34396
34473
  } catch {
34397
34474
  isDir = false;
@@ -34416,25 +34493,25 @@ async function listDirAsync(relativePath) {
34416
34493
  }
34417
34494
 
34418
34495
  // src/files/read-file.ts
34419
- import fs29 from "node:fs";
34496
+ import fs30 from "node:fs";
34420
34497
  import { StringDecoder } from "node:string_decoder";
34421
34498
  function resolveFilePath(relativePath) {
34422
34499
  const resolved = ensureUnderCwd(relativePath, getBridgeRoot());
34423
34500
  if (!resolved) return { error: "Path is outside working directory" };
34424
34501
  let real;
34425
34502
  try {
34426
- real = fs29.realpathSync(resolved);
34503
+ real = fs30.realpathSync(resolved);
34427
34504
  } catch {
34428
34505
  real = resolved;
34429
34506
  }
34430
- const stat2 = fs29.statSync(real);
34507
+ const stat2 = fs30.statSync(real);
34431
34508
  if (!stat2.isFile()) return { error: "Not a file" };
34432
34509
  return real;
34433
34510
  }
34434
34511
  var LINE_CHUNK_SIZE = 64 * 1024;
34435
34512
  function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
34436
- const fileSize = fs29.statSync(filePath).size;
34437
- const fd = fs29.openSync(filePath, "r");
34513
+ const fileSize = fs30.statSync(filePath).size;
34514
+ const fd = fs30.openSync(filePath, "r");
34438
34515
  const bufSize = 64 * 1024;
34439
34516
  const buf = Buffer.alloc(bufSize);
34440
34517
  const decoder = new StringDecoder("utf8");
@@ -34447,7 +34524,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
34447
34524
  let line0Accum = "";
34448
34525
  try {
34449
34526
  let bytesRead;
34450
- while (!done && (bytesRead = fs29.readSync(fd, buf, 0, bufSize, null)) > 0) {
34527
+ while (!done && (bytesRead = fs30.readSync(fd, buf, 0, bufSize, null)) > 0) {
34451
34528
  const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
34452
34529
  partial2 = "";
34453
34530
  let lineStart = 0;
@@ -34582,7 +34659,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
34582
34659
  }
34583
34660
  return { content: resultLines.join("\n"), size: fileSize };
34584
34661
  } finally {
34585
- fs29.closeSync(fd);
34662
+ fs30.closeSync(fd);
34586
34663
  }
34587
34664
  }
34588
34665
  function readFile2(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
@@ -34593,8 +34670,8 @@ function readFile2(relativePath, startLine, endLine, lineOffset, lineChunkSize =
34593
34670
  if (hasRange) {
34594
34671
  return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
34595
34672
  }
34596
- const stat2 = fs29.statSync(result);
34597
- const raw = fs29.readFileSync(result, "utf8");
34673
+ const stat2 = fs30.statSync(result);
34674
+ const raw = fs30.readFileSync(result, "utf8");
34598
34675
  const lines = raw.split(/\r?\n/);
34599
34676
  return { content: raw, totalLines: lines.length, size: stat2.size };
34600
34677
  } catch (err) {
@@ -34712,7 +34789,7 @@ function handleSkillLayoutRequest(msg, deps) {
34712
34789
  }
34713
34790
 
34714
34791
  // src/skills/install-remote-skills.ts
34715
- import fs30 from "node:fs";
34792
+ import fs31 from "node:fs";
34716
34793
  import path32 from "node:path";
34717
34794
  function installRemoteSkills(cwd, targetDir, items) {
34718
34795
  const installed2 = [];
@@ -34728,11 +34805,11 @@ function installRemoteSkills(cwd, targetDir, items) {
34728
34805
  for (const f of item.files) {
34729
34806
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
34730
34807
  const dest = path32.join(skillDir, f.path);
34731
- fs30.mkdirSync(path32.dirname(dest), { recursive: true });
34808
+ fs31.mkdirSync(path32.dirname(dest), { recursive: true });
34732
34809
  if (f.text !== void 0) {
34733
- fs30.writeFileSync(dest, f.text, "utf8");
34810
+ fs31.writeFileSync(dest, f.text, "utf8");
34734
34811
  } else if (f.base64) {
34735
- fs30.writeFileSync(dest, Buffer.from(f.base64, "base64"));
34812
+ fs31.writeFileSync(dest, Buffer.from(f.base64, "base64"));
34736
34813
  }
34737
34814
  }
34738
34815
  installed2.push({
@@ -34882,7 +34959,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
34882
34959
  };
34883
34960
 
34884
34961
  // src/bridge/routing/handlers/revert-turn-snapshot.ts
34885
- import * as fs31 from "node:fs";
34962
+ import * as fs32 from "node:fs";
34886
34963
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
34887
34964
  const id = typeof msg.id === "string" ? msg.id : "";
34888
34965
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -34894,7 +34971,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
34894
34971
  if (!s) return;
34895
34972
  const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
34896
34973
  const file2 = snapshotFilePath(agentBase, turnId);
34897
- if (!fs31.existsSync(file2)) {
34974
+ if (!fs32.existsSync(file2)) {
34898
34975
  sendWsMessage(s, {
34899
34976
  type: "revert_turn_snapshot_result",
34900
34977
  id,
@@ -34973,9 +35050,6 @@ function dispatchBridgeMessage(msg, deps) {
34973
35050
  case "revert_turn_snapshot":
34974
35051
  handleRevertTurnSnapshotMessage(msg, deps);
34975
35052
  break;
34976
- case "cancel_run":
34977
- handleCancelRunMessage(msg, deps);
34978
- break;
34979
35053
  case "cursor_request_response":
34980
35054
  handleCursorRequestResponseMessage(msg, deps);
34981
35055
  break;