@buildautomaton/cli 0.1.5 → 0.1.7

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
@@ -21966,12 +21966,12 @@ var require_src2 = __commonJS({
21966
21966
  function check2(path24, isFile, isDirectory) {
21967
21967
  log2(`checking %s`, path24);
21968
21968
  try {
21969
- const stat = fs_1.statSync(path24);
21970
- if (stat.isFile() && isFile) {
21969
+ const stat2 = fs_1.statSync(path24);
21970
+ if (stat2.isFile() && isFile) {
21971
21971
  log2(`[OK] path represents a file`);
21972
21972
  return true;
21973
21973
  }
21974
- if (stat.isDirectory() && isDirectory) {
21974
+ if (stat2.isDirectory() && isDirectory) {
21975
21975
  log2(`[OK] path represents a directory`);
21976
21976
  return true;
21977
21977
  }
@@ -22061,14 +22061,30 @@ var import_websocket = __toESM(require_websocket(), 1);
22061
22061
  var import_websocket_server = __toESM(require_websocket_server(), 1);
22062
22062
  var wrapper_default = import_websocket.default;
22063
22063
 
22064
+ // src/net/apply-cli-outbound-network-prefs.ts
22065
+ import dns from "node:dns";
22066
+ var applied = false;
22067
+ function applyCliOutboundNetworkPreferences() {
22068
+ if (applied) return;
22069
+ applied = true;
22070
+ try {
22071
+ dns.setDefaultResultOrder("ipv4first");
22072
+ } catch {
22073
+ }
22074
+ }
22075
+
22064
22076
  // src/bridge/connection/create-ws-bridge.ts
22065
22077
  var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
22066
22078
  var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
22067
22079
  function createWsBridge(options) {
22068
22080
  const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid, clientPingIntervalMs } = options;
22069
- const wsOptions = {};
22081
+ applyCliOutboundNetworkPreferences();
22082
+ const wsOptions = {
22083
+ perMessageDeflate: false,
22084
+ family: 4
22085
+ };
22070
22086
  if (url2.startsWith("wss://")) {
22071
- wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
22087
+ wsOptions.agent = new https.Agent({ rejectUnauthorized: false, family: 4 });
22072
22088
  }
22073
22089
  const ws = new wrapper_default(url2, wsOptions);
22074
22090
  let clientPingTimer = null;
@@ -22132,10 +22148,336 @@ function sendWsMessage(ws, payload) {
22132
22148
  }
22133
22149
  }
22134
22150
 
22135
- // src/acp/clients/acp-client.ts
22151
+ // ../types/dist/index.js
22152
+ init_zod();
22153
+ init_zod();
22154
+ init_zod();
22155
+ init_zod();
22156
+ init_zod();
22157
+ init_zod();
22158
+ init_zod();
22159
+ init_zod();
22160
+ init_zod();
22161
+ init_zod();
22162
+ init_zod();
22163
+ init_zod();
22164
+ var WorkItemStatusSchema = external_exports.enum(["backlog", "in-progress", "completed"]);
22165
+ var WorkItemProgressSchema = external_exports.object({
22166
+ remainingCriteria: external_exports.array(external_exports.string()).default([]),
22167
+ openQuestions: external_exports.array(external_exports.string()).default([]),
22168
+ assignedTo: external_exports.enum(["agent", "human-product", "human-expert"]).optional()
22169
+ });
22170
+ var ChangeSchema = external_exports.object({
22171
+ id: external_exports.string(),
22172
+ description: external_exports.string(),
22173
+ buildingBlockId: external_exports.string(),
22174
+ buildingBlockType: external_exports.enum(["function", "workflow", "connector", "ui-component", "app-fragment", "application", "project"]),
22175
+ action: external_exports.enum(["create", "update", "split", "combine"])
22176
+ });
22177
+ var CompletionCriterionSchema = external_exports.object({
22178
+ id: external_exports.string(),
22179
+ description: external_exports.string(),
22180
+ type: external_exports.enum(["write-code", "write-tests", "verify-tests", "other"]),
22181
+ verified: external_exports.boolean().default(false)
22182
+ });
22183
+ var WorkItemPrioritySchema = external_exports.enum(["low", "medium", "high", "critical"]);
22184
+ var IterationPhaseSchema = external_exports.enum(["analysis", "implementation", "verify", "reprioritize", "completed"]);
22185
+ var WorkItemDependencySchema = external_exports.object({
22186
+ type: external_exports.enum(["work-item"]),
22187
+ id: external_exports.string()
22188
+ });
22189
+ var WorkItemSchema = external_exports.object({
22190
+ id: external_exports.string(),
22191
+ sessionId: external_exports.string().optional(),
22192
+ summary: external_exports.string().optional(),
22193
+ description: external_exports.string(),
22194
+ status: WorkItemStatusSchema,
22195
+ buildingBlockId: external_exports.string().optional(),
22196
+ buildingBlockType: external_exports.enum(["function", "workflow", "connector", "ui-component", "app-fragment", "application", "project"]),
22197
+ changes: external_exports.array(ChangeSchema).default([]),
22198
+ completionCriteria: external_exports.array(CompletionCriterionSchema).default([]),
22199
+ priority: WorkItemPrioritySchema.default("medium"),
22200
+ dependencies: external_exports.array(WorkItemDependencySchema).default([]),
22201
+ assignedToUserId: external_exports.string().optional()
22202
+ });
22203
+ var UserWorkspaceProfileSchema = external_exports.object({
22204
+ id: external_exports.string(),
22205
+ workspaceId: external_exports.string(),
22206
+ userId: external_exports.string(),
22207
+ roleDescription: external_exports.string().optional(),
22208
+ expertiseAreas: external_exports.array(external_exports.string()),
22209
+ preferences: external_exports.record(external_exports.unknown()).optional(),
22210
+ learnings: external_exports.array(external_exports.string())
22211
+ });
22212
+ var WorkspaceOwnerInfoSchema = external_exports.object({
22213
+ ownerId: external_exports.string(),
22214
+ ownerName: external_exports.string().optional(),
22215
+ ownerEmail: external_exports.string().optional(),
22216
+ ownerProfilePictureUrl: external_exports.string().optional()
22217
+ });
22218
+ var WorkspaceRuntimeEntrySchema = external_exports.object({
22219
+ workspaceId: external_exports.string(),
22220
+ path: external_exports.string(),
22221
+ name: external_exports.string().optional(),
22222
+ owner: WorkspaceOwnerInfoSchema.optional(),
22223
+ isOwner: external_exports.boolean().optional()
22224
+ });
22225
+ var ProjectContextSchema = external_exports.object({
22226
+ projectId: external_exports.string(),
22227
+ context: external_exports.record(external_exports.unknown()).default({}),
22228
+ updatedAt: external_exports.string()
22229
+ });
22230
+ var WebSocketMessageTypeSchema = external_exports.enum([
22231
+ "plan-update",
22232
+ "work-item-update",
22233
+ "work-item-added",
22234
+ "work-item-removed",
22235
+ "project-processing-start",
22236
+ "project-processing-update",
22237
+ "project-processing-complete",
22238
+ "project-processing-error",
22239
+ "file-tool-request",
22240
+ "file-tool-response",
22241
+ "file-generated"
22242
+ ]);
22243
+ var WebSocketMessageSchema = external_exports.object({
22244
+ type: WebSocketMessageTypeSchema,
22245
+ contextId: external_exports.string().optional(),
22246
+ data: external_exports.any().optional(),
22247
+ error: external_exports.string().optional()
22248
+ });
22249
+ var CheckpointKindSchema = external_exports.enum(["daily", "weekly", "overall"]);
22250
+ var CheckpointSummarySchema = external_exports.object({
22251
+ id: external_exports.string(),
22252
+ kind: CheckpointKindSchema,
22253
+ /** ISO date for daily (YYYY-MM-DD), ISO week for weekly, null for overall */
22254
+ periodKey: external_exports.string().nullable(),
22255
+ summary: external_exports.string(),
22256
+ createdAt: external_exports.string(),
22257
+ updatedAt: external_exports.string()
22258
+ });
22259
+ var ThreadMetaSchema = external_exports.object({
22260
+ threadId: external_exports.string(),
22261
+ workspaceId: external_exports.string(),
22262
+ /** External source (e.g. slack, discord); null if internal-only */
22263
+ externalSource: external_exports.string().nullable(),
22264
+ /** Id in the external system (e.g. channel_id + thread_ts) */
22265
+ externalId: external_exports.string().nullable(),
22266
+ title: external_exports.string().optional(),
22267
+ createdAt: external_exports.string(),
22268
+ updatedAt: external_exports.string()
22269
+ });
22270
+ var ThreadMessageSchema = external_exports.object({
22271
+ messageId: external_exports.string(),
22272
+ threadId: external_exports.string(),
22273
+ /** Role: user, assistant, system */
22274
+ role: external_exports.enum(["user", "assistant", "system"]),
22275
+ content: external_exports.string(),
22276
+ /** Optional reference to a ContentItem (e.g. doc, Notion page) */
22277
+ contentItemId: external_exports.string().nullable(),
22278
+ /** External message id if synced from external chat */
22279
+ externalId: external_exports.string().nullable(),
22280
+ createdAt: external_exports.string(),
22281
+ updatedAt: external_exports.string()
22282
+ });
22283
+ var ThreadCheckpointSummarySchema = CheckpointSummarySchema.extend({
22284
+ threadId: external_exports.string()
22285
+ });
22286
+ var ContentSourceSchema = external_exports.enum(["notion", "doc", "slack_thread", "other"]);
22287
+ var ContentItemMetaSchema = external_exports.object({
22288
+ contentId: external_exports.string(),
22289
+ workspaceId: external_exports.string(),
22290
+ source: ContentSourceSchema,
22291
+ /** Id in the external system (e.g. Notion page id, doc url) */
22292
+ externalId: external_exports.string(),
22293
+ /** If source is slack_thread, points to Thread DO id */
22294
+ threadId: external_exports.string().nullable(),
22295
+ title: external_exports.string().optional(),
22296
+ createdAt: external_exports.string(),
22297
+ updatedAt: external_exports.string()
22298
+ });
22299
+ var ContentStorageRefSchema = external_exports.object({
22300
+ storageKey: external_exports.string(),
22301
+ /** Optional: mime type or format hint */
22302
+ contentType: external_exports.string().optional()
22303
+ });
22304
+ var ContentCheckpointSummarySchema = CheckpointSummarySchema.extend({
22305
+ contentId: external_exports.string()
22306
+ });
22307
+ var StoryMetaSchema = external_exports.object({
22308
+ storyId: external_exports.string(),
22309
+ workspaceId: external_exports.string(),
22310
+ title: external_exports.string(),
22311
+ /** feature | bug | epic */
22312
+ kind: external_exports.enum(["feature", "bug", "epic"]).default("feature"),
22313
+ createdAt: external_exports.string(),
22314
+ updatedAt: external_exports.string()
22315
+ });
22316
+ var StoryContentItemRefSchema = external_exports.object({
22317
+ id: external_exports.string(),
22318
+ storyId: external_exports.string(),
22319
+ contentItemId: external_exports.string(),
22320
+ /** Snapshot summary when added to story (or updated) */
22321
+ summary: external_exports.string(),
22322
+ orderIndex: external_exports.number().default(0),
22323
+ createdAt: external_exports.string(),
22324
+ updatedAt: external_exports.string()
22325
+ });
22326
+ var StoryCheckpointSummarySchema = CheckpointSummarySchema.extend({
22327
+ storyId: external_exports.string()
22328
+ });
22329
+ var SessionMetaSchema = external_exports.object({
22330
+ sessionId: external_exports.string(),
22331
+ workspaceId: external_exports.string(),
22332
+ title: external_exports.string().optional(),
22333
+ createdAt: external_exports.string(),
22334
+ updatedAt: external_exports.string()
22335
+ });
22336
+ var SessionPromptSchema = external_exports.object({
22337
+ id: external_exports.string(),
22338
+ sessionId: external_exports.string(),
22339
+ /** text | resource */
22340
+ type: external_exports.enum(["text", "resource"]).default("text"),
22341
+ text: external_exports.string().optional(),
22342
+ resourceUri: external_exports.string().optional(),
22343
+ createdAt: external_exports.string()
22344
+ });
22345
+ var SessionResponseSchema = external_exports.object({
22346
+ id: external_exports.string(),
22347
+ sessionId: external_exports.string(),
22348
+ promptId: external_exports.string(),
22349
+ /** message | completion */
22350
+ kind: external_exports.enum(["message", "completion"]),
22351
+ content: external_exports.string().optional(),
22352
+ /** For completion: stopReason etc. */
22353
+ stopReason: external_exports.string().optional(),
22354
+ createdAt: external_exports.string()
22355
+ });
22356
+ var SessionToolCallSchema = external_exports.object({
22357
+ id: external_exports.string(),
22358
+ sessionId: external_exports.string(),
22359
+ promptId: external_exports.string(),
22360
+ name: external_exports.string(),
22361
+ params: external_exports.record(external_exports.unknown()).optional(),
22362
+ result: external_exports.record(external_exports.unknown()).optional(),
22363
+ createdAt: external_exports.string()
22364
+ });
22365
+ var SessionThreadRefSchema = external_exports.object({
22366
+ sessionId: external_exports.string(),
22367
+ threadId: external_exports.string(),
22368
+ addedAt: external_exports.string()
22369
+ });
22370
+ var ArtifactMetaSchema = external_exports.object({
22371
+ artifactId: external_exports.string(),
22372
+ workspaceId: external_exports.string(),
22373
+ /** Slug for permalink: /workspaces/:wid/artifacts/:slug */
22374
+ permalinkSlug: external_exports.string(),
22375
+ title: external_exports.string(),
22376
+ /** e.g. summary_report, build_log */
22377
+ type: external_exports.string().default("report"),
22378
+ /** Optional session that produced this artifact */
22379
+ sessionId: external_exports.string().nullable(),
22380
+ createdAt: external_exports.string(),
22381
+ updatedAt: external_exports.string()
22382
+ });
22383
+ var TemplateMetaSchema = external_exports.object({
22384
+ templateId: external_exports.string(),
22385
+ workspaceId: external_exports.string(),
22386
+ name: external_exports.string(),
22387
+ /** e.g. summary_report, build_log */
22388
+ artifactType: external_exports.string().optional(),
22389
+ createdAt: external_exports.string(),
22390
+ updatedAt: external_exports.string()
22391
+ });
22392
+ var GitRepoMetaSchema = external_exports.object({
22393
+ /** Stable id for the repo (e.g. hash of normalized canonical URL). Used for DO idFromName. */
22394
+ repoId: external_exports.string(),
22395
+ /** Canonical external URL (e.g. https://github.com/org/repo). Normalize before storing. */
22396
+ canonicalUrl: external_exports.string().url(),
22397
+ /** Optional workspace this repo was first linked in. */
22398
+ workspaceId: external_exports.string().nullable(),
22399
+ displayName: external_exports.string().optional(),
22400
+ createdAt: external_exports.string(),
22401
+ updatedAt: external_exports.string()
22402
+ });
22403
+ var LOCAL_AGENT_AUTH_ERROR_HINTS = {
22404
+ "kiro-acp": [/not logged in/i, /kiro-cli\s+login/i, /log in with kiro-cli/i],
22405
+ "cursor-cli": [/cursor_login/i, /authenticate.*cursor/i, /not logged in.*cursor/i, /run:\s*agent\s+login/i],
22406
+ "codex-acp": [
22407
+ /authentication failed/i,
22408
+ /not authenticated/i,
22409
+ /invalid.*api key/i,
22410
+ /sign in.*openai/i,
22411
+ /login.*openai/i,
22412
+ /unauthorized/i
22413
+ ],
22414
+ "claude-code": [
22415
+ /ANTHROPIC_API_KEY/i,
22416
+ /not authenticated/i,
22417
+ /authentication failed/i,
22418
+ /claude\s+login/i,
22419
+ /please run.*claude.*login/i
22420
+ ]
22421
+ };
22422
+ function localAgentErrorSuggestsAuth(agentType, errorText) {
22423
+ if (agentType == null || agentType === "" || errorText == null || !String(errorText).trim()) return false;
22424
+ const hints = LOCAL_AGENT_AUTH_ERROR_HINTS[agentType];
22425
+ if (!hints?.length) return false;
22426
+ return hints.some((re) => re.test(String(errorText)));
22427
+ }
22428
+
22429
+ // src/acp/clients/sdk-stdio-acp-client.ts
22136
22430
  import { spawn } from "node:child_process";
22431
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
22432
+ import { dirname } from "node:path";
22137
22433
  import { Readable, Writable } from "node:stream";
22138
22434
 
22435
+ // src/files/diff/unified-diff.ts
22436
+ function computeLineDiff(oldText, newText) {
22437
+ const oldLines = oldText.split("\n");
22438
+ const newLines = newText.split("\n");
22439
+ const m = oldLines.length;
22440
+ const n = newLines.length;
22441
+ const dp = Array(m + 1);
22442
+ for (let i2 = 0; i2 <= m; i2++) dp[i2] = Array(n + 1).fill(0);
22443
+ for (let i2 = 1; i2 <= m; i2++) {
22444
+ for (let j2 = 1; j2 <= n; j2++) {
22445
+ if (oldLines[i2 - 1] === newLines[j2 - 1]) {
22446
+ dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
22447
+ } else {
22448
+ dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
22449
+ }
22450
+ }
22451
+ }
22452
+ const result = [];
22453
+ let i = m;
22454
+ let j = n;
22455
+ while (i > 0 || j > 0) {
22456
+ if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
22457
+ result.unshift({ type: "context", line: oldLines[i - 1] });
22458
+ i--;
22459
+ j--;
22460
+ } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
22461
+ result.unshift({ type: "add", line: newLines[j - 1] });
22462
+ j--;
22463
+ } else {
22464
+ result.unshift({ type: "remove", line: oldLines[i - 1] });
22465
+ i--;
22466
+ }
22467
+ }
22468
+ return result;
22469
+ }
22470
+ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
22471
+ const lines = computeLineDiff(oldText, newText);
22472
+ const out = [`--- ${filePath}`, `+++ ${filePath}`];
22473
+ for (const d of lines) {
22474
+ if (d.type === "add") out.push(`+${d.line}`);
22475
+ else if (d.type === "remove") out.push(`-${d.line}`);
22476
+ else out.push(` ${d.line}`);
22477
+ }
22478
+ return out.join("\n");
22479
+ }
22480
+
22139
22481
  // src/files/cwd/bridge-workspace-directory.ts
22140
22482
  import * as path from "node:path";
22141
22483
  var bridgeWorkspaceDirectory = null;
@@ -22146,47 +22488,217 @@ function getBridgeWorkspaceDirectory() {
22146
22488
  return bridgeWorkspaceDirectory;
22147
22489
  }
22148
22490
 
22149
- // src/acp/clients/acp-client.ts
22491
+ // src/acp/safe-fs-path.ts
22492
+ import * as path2 from "node:path";
22493
+ function resolveSafePathUnderCwd(cwd, filePath) {
22494
+ const trimmed2 = filePath.trim();
22495
+ if (!trimmed2) return null;
22496
+ const normalizedCwd = path2.resolve(cwd);
22497
+ const resolved = path2.isAbsolute(trimmed2) ? path2.normalize(trimmed2) : path2.resolve(normalizedCwd, trimmed2);
22498
+ const rel = path2.relative(normalizedCwd, resolved);
22499
+ if (rel.startsWith("..") || path2.isAbsolute(rel)) return null;
22500
+ return resolved;
22501
+ }
22502
+ function toDisplayPathRelativeToCwd(cwd, absolutePath) {
22503
+ const normalizedCwd = path2.resolve(cwd);
22504
+ const rel = path2.relative(normalizedCwd, path2.resolve(absolutePath));
22505
+ if (!rel || rel === "") return path2.basename(absolutePath);
22506
+ return rel.split(path2.sep).join("/");
22507
+ }
22508
+
22509
+ // src/acp/clients/agent-stderr-capture.ts
22510
+ var STDERR_CAPTURE_MAX = 48e3;
22511
+ function createStderrCapture(child) {
22512
+ const chunks = [];
22513
+ let total = 0;
22514
+ return {
22515
+ append(chunk) {
22516
+ try {
22517
+ process.stderr.write(chunk);
22518
+ } catch {
22519
+ }
22520
+ if (total >= STDERR_CAPTURE_MAX) return;
22521
+ const n = Math.min(chunk.length, STDERR_CAPTURE_MAX - total);
22522
+ if (n <= 0) return;
22523
+ chunks.push(n === chunk.length ? chunk : chunk.subarray(0, n));
22524
+ total += n;
22525
+ },
22526
+ getText() {
22527
+ return Buffer.concat(chunks).toString("utf8").trim();
22528
+ }
22529
+ };
22530
+ }
22531
+ function formatJsonRpcStyleError(err) {
22532
+ if (err instanceof Error) return err.message;
22533
+ if (err != null && typeof err === "object") {
22534
+ const o = err;
22535
+ const msg = typeof o.message === "string" ? o.message : null;
22536
+ const code = o.code != null ? String(o.code) : "";
22537
+ if (msg) return code ? `[${code}] ${msg}` : msg;
22538
+ }
22539
+ if (typeof err === "string") return err;
22540
+ try {
22541
+ return JSON.stringify(err);
22542
+ } catch {
22543
+ return String(err);
22544
+ }
22545
+ }
22546
+ function mergeErrorWithStderr(primary, stderrText) {
22547
+ const s = stderrText.trim();
22548
+ const p = (primary ?? "").trim();
22549
+ if (!s) return p;
22550
+ if (!p) return s;
22551
+ if (p.includes(s) || s.includes(p)) return p.length >= s.length ? p : s;
22552
+ return `${p}
22553
+ ${s}`;
22554
+ }
22555
+
22556
+ // src/acp/clients/kiro-sdk-ext-notifications.ts
22557
+ function createKiroSdkExtNotificationHandler(options) {
22558
+ const { onSessionUpdate } = options;
22559
+ return async (method, params) => {
22560
+ if (method === "_kiro.dev/metadata") {
22561
+ const p = params && typeof params === "object" ? params : {};
22562
+ const pct = p.contextUsagePercentage;
22563
+ if (typeof pct !== "number" || !Number.isFinite(pct) || !onSessionUpdate) return;
22564
+ onSessionUpdate({
22565
+ sessionUpdate: "context_usage",
22566
+ contextUsagePercentage: pct
22567
+ });
22568
+ return;
22569
+ }
22570
+ };
22571
+ }
22572
+
22573
+ // src/acp/clients/sdk-stdio-ext-notifications.ts
22574
+ var noopExtNotification = async () => {
22575
+ };
22576
+ function createSdkStdioExtNotificationHandler(options) {
22577
+ const { backendAgentType, onSessionUpdate } = options;
22578
+ switch (backendAgentType) {
22579
+ case "kiro-acp":
22580
+ return createKiroSdkExtNotificationHandler({ onSessionUpdate });
22581
+ default:
22582
+ return noopExtNotification;
22583
+ }
22584
+ }
22585
+
22586
+ // src/acp/clients/sdk-stdio-acp-client.ts
22150
22587
  function formatSpawnError(err, command) {
22151
22588
  if (err.code === "ENOENT") {
22152
22589
  return `Command "${command}" not found. Install the agent (e.g. Cursor CLI) or add it to PATH.`;
22153
22590
  }
22154
22591
  return err.message || String(err);
22155
22592
  }
22156
- function toErrorMessage(err) {
22157
- if (err instanceof Error) return err.message;
22158
- if (err != null && typeof err === "object" && "message" in err)
22159
- return String(err.message);
22160
- if (typeof err === "string") return err;
22161
- if (err != null && typeof err === "object") return JSON.stringify(err);
22162
- return String(err);
22593
+ function sliceFileContentRange(content, line, limit) {
22594
+ if (line == null && limit == null) return content;
22595
+ const lines = content.split("\n");
22596
+ const start = line != null && line > 0 ? line - 1 : 0;
22597
+ const end = limit != null && limit > 0 ? start + limit : lines.length;
22598
+ return lines.slice(start, end).join("\n");
22599
+ }
22600
+ function bridgePayloadFromSdkSessionNotification(params) {
22601
+ return { sessionId: params.sessionId, ...params.update };
22163
22602
  }
22164
- async function createAcpClient(options) {
22165
- const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
22166
- const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate } = options;
22603
+ async function createSdkStdioAcpClient(options) {
22604
+ const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
22605
+ const {
22606
+ command,
22607
+ cwd = getBridgeWorkspaceDirectory(),
22608
+ backendAgentType,
22609
+ onSessionUpdate,
22610
+ onFileChange,
22611
+ killSubprocessAfterCancelMs,
22612
+ onAgentSubprocessExit
22613
+ } = options;
22167
22614
  const isWindows = process.platform === "win32";
22168
22615
  const child = spawn(command[0], command.slice(1), {
22169
22616
  cwd,
22170
- stdio: ["pipe", "pipe", "inherit"],
22617
+ stdio: ["pipe", "pipe", "pipe"],
22171
22618
  env: process.env,
22172
22619
  shell: isWindows
22173
22620
  });
22621
+ const stderrCapture = createStderrCapture(child);
22622
+ child.once("close", (code, signal) => {
22623
+ onAgentSubprocessExit?.({ code, signal });
22624
+ });
22174
22625
  return new Promise((resolve14, reject) => {
22626
+ let initSettled = false;
22627
+ const settleReject = (err) => {
22628
+ if (initSettled) return;
22629
+ initSettled = true;
22630
+ try {
22631
+ child.kill();
22632
+ } catch {
22633
+ }
22634
+ reject(err);
22635
+ };
22636
+ const settleResolve = (handle) => {
22637
+ if (initSettled) return;
22638
+ initSettled = true;
22639
+ resolve14(handle);
22640
+ };
22175
22641
  child.on("error", (err) => {
22176
- child.kill();
22177
- reject(new Error(formatSpawnError(err, command[0])));
22642
+ settleReject(new Error(formatSpawnError(err, command[0])));
22643
+ });
22644
+ child.stderr?.on("data", (chunk) => {
22645
+ stderrCapture.append(chunk);
22646
+ if (initSettled) return;
22647
+ const stderrText = stderrCapture.getText();
22648
+ if (backendAgentType && stderrText.trim() && localAgentErrorSuggestsAuth(backendAgentType, stderrText)) {
22649
+ settleReject(new Error(stderrText.trim()));
22650
+ }
22178
22651
  });
22179
22652
  (async () => {
22180
22653
  try {
22181
22654
  const writable = Writable.toWeb(child.stdin);
22182
22655
  const readable = Readable.toWeb(child.stdout);
22183
22656
  const stream = ndJsonStream2(writable, readable);
22657
+ const extNotification = createSdkStdioExtNotificationHandler({
22658
+ backendAgentType,
22659
+ onSessionUpdate
22660
+ });
22184
22661
  const client = (_agent) => ({
22185
- async requestPermission(_params) {
22186
- return { outcome: "approved" };
22662
+ async requestPermission(params) {
22663
+ const opt = params?.options?.[0];
22664
+ if (opt && typeof opt.optionId === "string") {
22665
+ return { outcome: { outcome: "selected", optionId: opt.optionId } };
22666
+ }
22667
+ return { outcome: { outcome: "cancelled" } };
22668
+ },
22669
+ async readTextFile(params) {
22670
+ const abs = resolveSafePathUnderCwd(cwd, params.path);
22671
+ if (!abs) throw new Error("Invalid or disallowed path");
22672
+ try {
22673
+ let content = readFileSync(abs, "utf8");
22674
+ content = sliceFileContentRange(content, params.line, params.limit);
22675
+ return { content };
22676
+ } catch (e) {
22677
+ if (e.code === "ENOENT") return { content: "" };
22678
+ throw e;
22679
+ }
22680
+ },
22681
+ async writeTextFile(params) {
22682
+ const abs = resolveSafePathUnderCwd(cwd, params.path);
22683
+ if (!abs) throw new Error("Invalid or disallowed path");
22684
+ let oldText = "";
22685
+ try {
22686
+ oldText = readFileSync(abs, "utf8");
22687
+ } catch (e) {
22688
+ if (e.code !== "ENOENT") throw e;
22689
+ }
22690
+ mkdirSync(dirname(abs), { recursive: true });
22691
+ writeFileSync(abs, params.content, "utf8");
22692
+ const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
22693
+ const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, params.content);
22694
+ onFileChange?.({ path: displayPath, oldText, newText: params.content, patchContent });
22695
+ return {};
22187
22696
  },
22188
22697
  async sessionUpdate(params) {
22189
- onSessionUpdate?.(params);
22698
+ onSessionUpdate?.(bridgePayloadFromSdkSessionNotification(params));
22699
+ },
22700
+ async extNotification(method, params) {
22701
+ await extNotification(method, params);
22190
22702
  }
22191
22703
  });
22192
22704
  const connection = new ClientSideConnection2(client, stream);
@@ -22194,39 +22706,82 @@ async function createAcpClient(options) {
22194
22706
  child.kill();
22195
22707
  });
22196
22708
  await connection.initialize({
22197
- protocolVersion: "0.1.0",
22198
- capabilities: {},
22709
+ protocolVersion: PROTOCOL_VERSION2,
22710
+ clientCapabilities: {
22711
+ fs: { readTextFile: true, writeTextFile: true }
22712
+ },
22199
22713
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
22200
22714
  });
22201
- const newSessionRes = await connection.newSession({ workingDirectory: cwd });
22715
+ const newSessionRes = await connection.newSession({ cwd, mcpServers: [] });
22202
22716
  const sessionId = newSessionRes.sessionId;
22203
- resolve14({
22717
+ settleResolve({
22204
22718
  sessionId,
22205
22719
  async sendPrompt(prompt, _options) {
22206
22720
  try {
22207
22721
  const response = await connection.prompt({
22208
22722
  sessionId,
22209
- prompt: { type: "text", text: prompt }
22723
+ prompt: [{ type: "text", text: prompt }]
22210
22724
  });
22725
+ await new Promise((r2) => setImmediate(r2));
22211
22726
  const r = response;
22212
- const cancelled = (r?.stopReason ?? "").toLowerCase() === "cancelled";
22727
+ const stopReason = (r?.stopReason ?? "").toLowerCase();
22728
+ const cancelled = stopReason === "cancelled";
22729
+ const refusal = stopReason === "refusal";
22730
+ const stderrAfter = stderrCapture.getText();
22731
+ const agentType = backendAgentType ?? null;
22732
+ const stderrEvaluated = Boolean(stderrAfter && agentType);
22733
+ const stderrSuggestsAuth = stderrEvaluated ? localAgentErrorSuggestsAuth(agentType, stderrAfter) : false;
22734
+ if (cancelled) {
22735
+ return {
22736
+ success: false,
22737
+ stopReason: r?.stopReason,
22738
+ output: r?.output,
22739
+ error: mergeErrorWithStderr("Stopped by user", stderrAfter)
22740
+ };
22741
+ }
22742
+ if (refusal) {
22743
+ return {
22744
+ success: false,
22745
+ stopReason: r?.stopReason,
22746
+ output: r?.output,
22747
+ error: mergeErrorWithStderr("The agent refused the request.", stderrAfter)
22748
+ };
22749
+ }
22750
+ if (stderrSuggestsAuth) {
22751
+ return {
22752
+ success: false,
22753
+ stopReason: r?.stopReason,
22754
+ output: r?.output,
22755
+ error: stderrAfter
22756
+ };
22757
+ }
22213
22758
  return {
22214
- success: !cancelled,
22759
+ success: true,
22215
22760
  stopReason: r?.stopReason,
22216
- output: r?.output,
22217
- ...cancelled ? { error: "Stopped by user" } : {}
22761
+ output: r?.output
22218
22762
  };
22219
22763
  } catch (err) {
22764
+ await new Promise((r) => setImmediate(r));
22765
+ const stderrAfter = stderrCapture.getText();
22766
+ const merged = mergeErrorWithStderr(formatJsonRpcStyleError(err), stderrAfter);
22220
22767
  return {
22221
22768
  success: false,
22222
- error: err instanceof Error ? err.message : String(err)
22769
+ error: merged
22223
22770
  };
22224
22771
  }
22225
22772
  },
22226
22773
  async cancel() {
22227
- const conn = connection;
22228
- if (typeof conn.cancel === "function") {
22229
- await conn.cancel({ sessionId });
22774
+ try {
22775
+ await connection.cancel({ sessionId });
22776
+ } catch {
22777
+ }
22778
+ if (killSubprocessAfterCancelMs != null && killSubprocessAfterCancelMs >= 0) {
22779
+ const t = setTimeout(() => {
22780
+ if (child.exitCode == null && child.signalCode == null) {
22781
+ child.kill("SIGTERM");
22782
+ }
22783
+ }, killSubprocessAfterCancelMs);
22784
+ t.unref?.();
22230
22785
  }
22231
22786
  },
22232
22787
  resolveRequest() {
@@ -22236,8 +22791,14 @@ async function createAcpClient(options) {
22236
22791
  }
22237
22792
  });
22238
22793
  } catch (err) {
22239
- child.kill();
22240
- reject(new Error(toErrorMessage(err)));
22794
+ if (initSettled) return;
22795
+ try {
22796
+ child.kill();
22797
+ } catch {
22798
+ }
22799
+ const stderrText = stderrCapture.getText();
22800
+ const base = formatJsonRpcStyleError(err);
22801
+ settleReject(new Error(mergeErrorWithStderr(base, stderrText)));
22241
22802
  }
22242
22803
  })();
22243
22804
  });
@@ -22540,11 +23101,11 @@ function logImmediate(line) {
22540
23101
 
22541
23102
  // src/config.ts
22542
23103
  import fs from "node:fs";
22543
- import path2 from "node:path";
23104
+ import path3 from "node:path";
22544
23105
  import os from "node:os";
22545
23106
  function getConfigPath() {
22546
- const dir = path2.join(os.homedir(), ".buildautomaton");
22547
- return path2.join(dir, "config.json");
23107
+ const dir = path3.join(os.homedir(), ".buildautomaton");
23108
+ return path3.join(dir, "config.json");
22548
23109
  }
22549
23110
  function normalizeApiUrl(url2) {
22550
23111
  return url2.replace(/\/$/, "");
@@ -22560,7 +23121,7 @@ function readRawConfig() {
22560
23121
  }
22561
23122
  function writeConfigForApi(apiUrl, auth) {
22562
23123
  const p = getConfigPath();
22563
- const dir = path2.dirname(p);
23124
+ const dir = path3.dirname(p);
22564
23125
  const key = normalizeApiUrl(apiUrl);
22565
23126
  const prev = readRawConfig() ?? {};
22566
23127
  const servers = { ...prev.servers ?? {}, [key]: { ...auth } };
@@ -22755,7 +23316,7 @@ function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconn
22755
23316
  });
22756
23317
  }
22757
23318
  function clearMainBridgeReconnectQuietOnOpen(state, log2) {
22758
- clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "[Bridge service] Reconnected.");
23319
+ clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "Bridge connection restored.");
22759
23320
  }
22760
23321
  function scheduleMainBridgeReconnect(state, connect, log2) {
22761
23322
  if (state.closedByUser || state.currentWs != null) return;
@@ -22792,7 +23353,7 @@ function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
22792
23353
  clearReconnectQuietOnSuccessfulConnection(
22793
23354
  ctx.firehoseQuiet,
22794
23355
  log2,
22795
- `${PROXY_AND_LOG_SERVICE_LABEL} Reconnected.`
23356
+ "Preview tunnel restored (local HTTP proxy and dev logs)."
22796
23357
  );
22797
23358
  }
22798
23359
 
@@ -22943,7 +23504,7 @@ function buildBridgeUrl(apiUrl, workspaceId, authToken) {
22943
23504
 
22944
23505
  // src/git/discover-repos.ts
22945
23506
  import * as fs2 from "node:fs";
22946
- import * as path3 from "node:path";
23507
+ import * as path4 from "node:path";
22947
23508
 
22948
23509
  // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
22949
23510
  var import_file_exists = __toESM(require_dist(), 1);
@@ -22952,7 +23513,7 @@ var import_promise_deferred = __toESM(require_dist2(), 1);
22952
23513
  var import_promise_deferred2 = __toESM(require_dist2(), 1);
22953
23514
  import { Buffer as Buffer2 } from "node:buffer";
22954
23515
  import { spawn as spawn3 } from "child_process";
22955
- import { normalize } from "node:path";
23516
+ import { normalize as normalize2 } from "node:path";
22956
23517
  import { EventEmitter } from "node:events";
22957
23518
  var __defProp2 = Object.defineProperty;
22958
23519
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
@@ -26255,7 +26816,7 @@ var init_branch = __esm2({
26255
26816
  });
26256
26817
  function toPath(input) {
26257
26818
  const path24 = input.trim().replace(/^["']|["']$/g, "");
26258
- return path24 && normalize(path24);
26819
+ return path24 && normalize2(path24);
26259
26820
  }
26260
26821
  var parseCheckIgnore;
26261
26822
  var init_CheckIgnore = __esm2({
@@ -27530,7 +28091,7 @@ async function isGitRepoDirectory(dirPath) {
27530
28091
  // src/git/discover-repos.ts
27531
28092
  async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
27532
28093
  const result = [];
27533
- const cwdResolved = path3.resolve(cwd);
28094
+ const cwdResolved = path4.resolve(cwd);
27534
28095
  if (await isGitRepoDirectory(cwdResolved)) {
27535
28096
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
27536
28097
  result.push({ absolutePath: cwdResolved, remoteUrl });
@@ -27543,7 +28104,7 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
27543
28104
  }
27544
28105
  for (const ent of entries) {
27545
28106
  if (!ent.isDirectory()) continue;
27546
- const childPath = path3.join(cwdResolved, ent.name);
28107
+ const childPath = path4.join(cwdResolved, ent.name);
27547
28108
  if (await isGitRepoDirectory(childPath)) {
27548
28109
  const remoteUrl = await getRemoteOriginUrl(childPath);
27549
28110
  result.push({ absolutePath: childPath, remoteUrl });
@@ -27552,11 +28113,11 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
27552
28113
  return result;
27553
28114
  }
27554
28115
  async function discoverGitReposUnderRoot(rootAbs) {
27555
- const root = path3.resolve(rootAbs);
28116
+ const root = path4.resolve(rootAbs);
27556
28117
  const roots = [];
27557
28118
  async function walk(dir) {
27558
28119
  if (await isGitRepoDirectory(dir)) {
27559
- roots.push(path3.resolve(dir));
28120
+ roots.push(path4.resolve(dir));
27560
28121
  return;
27561
28122
  }
27562
28123
  let entries;
@@ -27567,7 +28128,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
27567
28128
  }
27568
28129
  for (const ent of entries) {
27569
28130
  if (!ent.isDirectory() || ent.name === ".git") continue;
27570
- await walk(path3.join(dir, ent.name));
28131
+ await walk(path4.join(dir, ent.name));
27571
28132
  }
27572
28133
  }
27573
28134
  await walk(root);
@@ -27603,34 +28164,31 @@ function reportGitRepos(getWs, log2) {
27603
28164
 
27604
28165
  // src/bridge/connection/close-bridge-connection.ts
27605
28166
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
27606
- log2?.("Shutting down\u2026");
28167
+ const say = log2 ?? logImmediate;
28168
+ say("Cleaning up connections\u2026");
27607
28169
  await new Promise((resolve14) => setImmediate(resolve14));
27608
- if (devServerManager) {
27609
- log2?.("Requesting dev server processes to stop\u2026");
27610
- await devServerManager.shutdownAllGraceful();
27611
- }
27612
28170
  state.closedByUser = true;
27613
28171
  clearReconnectQuietTimer(state.mainQuiet);
27614
28172
  clearReconnectQuietTimer(state.firehoseQuiet);
27615
28173
  if (state.reconnectTimeout != null) {
27616
- log2?.("Cancelling reconnect timer\u2026");
28174
+ say("Cancelling bridge reconnect timer\u2026");
27617
28175
  clearTimeout(state.reconnectTimeout);
27618
28176
  state.reconnectTimeout = null;
27619
28177
  }
27620
28178
  if (state.firehoseReconnectTimeout != null) {
27621
- log2?.("[Proxy and log service] Cancelling reconnect timer\u2026");
28179
+ say("Cancelling preview tunnel reconnect timer\u2026");
27622
28180
  clearTimeout(state.firehoseReconnectTimeout);
27623
28181
  state.firehoseReconnectTimeout = null;
27624
28182
  }
27625
28183
  if (state.firehoseHandle) {
27626
- log2?.("[Proxy and log service] Closing connection (CLI shutdown)\u2026");
28184
+ say("Closing preview tunnel (local HTTP proxy and dev logs)\u2026");
27627
28185
  state.firehoseHandle.close();
27628
28186
  state.firehoseHandle = null;
27629
28187
  }
27630
- log2?.("Disconnecting local agents (ACP)\u2026");
28188
+ say("Disconnecting local agent\u2026");
27631
28189
  acpManager.disconnect();
27632
28190
  if (state.currentWs) {
27633
- log2?.("[Bridge service] Closing connection (CLI shutdown)\u2026");
28191
+ say("Closing bridge connection to the cloud\u2026");
27634
28192
  state.currentWs.removeAllListeners();
27635
28193
  const wsState = state.currentWs.readyState;
27636
28194
  if (wsState === 1 || wsState === 2) {
@@ -27643,22 +28201,27 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
27643
28201
  }
27644
28202
  state.currentWs = null;
27645
28203
  }
28204
+ if (devServerManager) {
28205
+ say("Stopping local dev server processes\u2026");
28206
+ await devServerManager.shutdownAllGraceful();
28207
+ }
28208
+ say("Shutdown complete.");
27646
28209
  }
27647
28210
 
27648
28211
  // src/git/session-git-queue.ts
27649
28212
  import { execFile as execFile2 } from "node:child_process";
28213
+ import { readFile, stat } from "node:fs/promises";
27650
28214
  import { promisify as promisify2 } from "node:util";
27651
- import * as fs4 from "node:fs";
27652
- import * as path5 from "node:path";
28215
+ import * as path6 from "node:path";
27653
28216
 
27654
28217
  // src/git/pre-turn-snapshot.ts
27655
28218
  import * as fs3 from "node:fs";
27656
- import * as path4 from "node:path";
28219
+ import * as path5 from "node:path";
27657
28220
  import { execFile } from "node:child_process";
27658
28221
  import { promisify } from "node:util";
27659
28222
  var execFileAsync = promisify(execFile);
27660
28223
  function snapshotsDirForCwd(agentCwd) {
27661
- return path4.join(agentCwd, ".buildautomaton", "snapshots");
28224
+ return path5.join(agentCwd, ".buildautomaton", "snapshots");
27662
28225
  }
27663
28226
  async function gitStashCreate(repoRoot, log2) {
27664
28227
  try {
@@ -27687,7 +28250,7 @@ async function gitRun(repoRoot, args, log2, label) {
27687
28250
  async function resolveSnapshotRepoRoots(options) {
27688
28251
  const { worktreePaths, fallbackCwd, log: log2 } = options;
27689
28252
  if (worktreePaths?.length) {
27690
- const uniq = [...new Set(worktreePaths.map((p) => path4.resolve(p)))];
28253
+ const uniq = [...new Set(worktreePaths.map((p) => path5.resolve(p)))];
27691
28254
  return uniq;
27692
28255
  }
27693
28256
  try {
@@ -27719,7 +28282,7 @@ async function capturePreTurnSnapshot(options) {
27719
28282
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
27720
28283
  repos
27721
28284
  };
27722
- const filePath = path4.join(dir, `${runId}.json`);
28285
+ const filePath = path5.join(dir, `${runId}.json`);
27723
28286
  try {
27724
28287
  fs3.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
27725
28288
  } catch (e) {
@@ -27757,17 +28320,17 @@ async function applyPreTurnSnapshot(filePath, log2) {
27757
28320
  return { ok: true };
27758
28321
  }
27759
28322
  function snapshotFilePath(agentCwd, runId) {
27760
- return path4.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
28323
+ return path5.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
27761
28324
  }
27762
28325
 
27763
28326
  // src/git/session-git-queue.ts
27764
28327
  var execFileAsync2 = promisify2(execFile2);
27765
28328
  var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
27766
- function readWorkspaceFileAsUtf8(absPath) {
28329
+ async function readWorkspaceFileAsUtf8(absPath) {
27767
28330
  try {
27768
- const st = fs4.statSync(absPath);
28331
+ const st = await stat(absPath);
27769
28332
  if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
27770
- return fs4.readFileSync(absPath, "utf8");
28333
+ return await readFile(absPath, "utf8");
27771
28334
  } catch {
27772
28335
  return void 0;
27773
28336
  }
@@ -27777,7 +28340,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
27777
28340
  const filePath = snapshotFilePath(agentCwd, runId);
27778
28341
  let data;
27779
28342
  try {
27780
- const raw = fs4.readFileSync(filePath, "utf8");
28343
+ const raw = await readFile(filePath, "utf8");
27781
28344
  data = JSON.parse(raw);
27782
28345
  } catch (e) {
27783
28346
  log2(
@@ -27806,7 +28369,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
27806
28369
  continue;
27807
28370
  }
27808
28371
  const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
27809
- const slug = path5.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
28372
+ const slug = path6.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
27810
28373
  for (const rel of lines) {
27811
28374
  if (rel.includes("..")) continue;
27812
28375
  try {
@@ -27820,8 +28383,8 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
27820
28383
  );
27821
28384
  if (!patchContent.trim()) continue;
27822
28385
  const displayPath = multiRepo ? `${slug}/${rel}` : rel;
27823
- const absFile = path5.join(repo.path, rel);
27824
- const newText = readWorkspaceFileAsUtf8(absFile);
28386
+ const absFile = path6.join(repo.path, rel);
28387
+ const newText = await readWorkspaceFileAsUtf8(absFile);
27825
28388
  sendSessionUpdate({
27826
28389
  type: "session_file_change",
27827
28390
  sessionId,
@@ -27847,11 +28410,20 @@ async function sendPromptToAgent(options) {
27847
28410
  promptId,
27848
28411
  sessionId,
27849
28412
  runId,
28413
+ agentType,
27850
28414
  agentCwd,
27851
28415
  sendResult,
27852
28416
  sendSessionUpdate,
27853
28417
  log: log2
27854
28418
  } = options;
28419
+ function augmentAuthFields(errorText) {
28420
+ const err = errorText ?? "";
28421
+ const at = agentType ?? null;
28422
+ const evaluated = Boolean(at && err.trim());
28423
+ const suggestsAuth = evaluated && at ? localAgentErrorSuggestsAuth(at, err) : false;
28424
+ if (!suggestsAuth || !agentType) return {};
28425
+ return { agentAuthRequired: true, agentType };
28426
+ }
27855
28427
  try {
27856
28428
  const result = await handle.sendPrompt(promptText, {});
27857
28429
  if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
@@ -27863,12 +28435,14 @@ async function sendPromptToAgent(options) {
27863
28435
  log: log2
27864
28436
  });
27865
28437
  }
28438
+ const errStr = typeof result.error === "string" ? result.error : void 0;
27866
28439
  sendResult({
27867
28440
  type: "prompt_result",
27868
28441
  id: promptId,
27869
28442
  ...sessionId ? { sessionId } : {},
27870
28443
  ...runId ? { runId } : {},
27871
- ...result
28444
+ ...result,
28445
+ ...augmentAuthFields(errStr)
27872
28446
  });
27873
28447
  if (!result.success) {
27874
28448
  log2(`[Agent] ${result.error ?? "Error"}`);
@@ -27876,20 +28450,20 @@ async function sendPromptToAgent(options) {
27876
28450
  } catch (err) {
27877
28451
  const errMsg = err instanceof Error ? err.message : String(err);
27878
28452
  log2(`[Agent] Send failed: ${errMsg}`);
27879
- if (err instanceof Error && err.stack) log2(`[Agent] ${err.stack}`);
27880
28453
  sendResult({
27881
28454
  type: "prompt_result",
27882
28455
  id: promptId,
27883
28456
  ...sessionId ? { sessionId } : {},
27884
28457
  ...runId ? { runId } : {},
27885
28458
  success: false,
27886
- error: errMsg
28459
+ error: errMsg,
28460
+ ...augmentAuthFields(errMsg)
27887
28461
  });
27888
28462
  }
27889
28463
  }
27890
28464
 
27891
28465
  // src/acp/ensure-acp-client.ts
27892
- import * as fs5 from "node:fs";
28466
+ import * as fs4 from "node:fs";
27893
28467
  import * as path9 from "node:path";
27894
28468
 
27895
28469
  // src/error-message.ts
@@ -27902,87 +28476,99 @@ function errorMessage(err) {
27902
28476
  return String(err);
27903
28477
  }
27904
28478
 
28479
+ // src/acp/clients/claude-code-acp-client.ts
28480
+ var claude_code_acp_client_exports = {};
28481
+ __export(claude_code_acp_client_exports, {
28482
+ BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE,
28483
+ buildClaudeCodeAcpSpawnCommand: () => buildClaudeCodeAcpSpawnCommand,
28484
+ createClaudeCodeAcpClient: () => createClaudeCodeAcpClient,
28485
+ detectLocalAgentPresence: () => detectLocalAgentPresence
28486
+ });
28487
+ import { execFile as execFile4 } from "node:child_process";
28488
+ import { promisify as promisify4 } from "node:util";
28489
+
28490
+ // src/acp/clients/detect-command-on-path.ts
28491
+ import { execFile as execFile3 } from "node:child_process";
28492
+ import { promisify as promisify3 } from "node:util";
28493
+ var execFileAsync3 = promisify3(execFile3);
28494
+ async function isCommandOnPath(command, timeoutMs = 4e3) {
28495
+ try {
28496
+ await execFileAsync3("which", [command], { timeout: timeoutMs });
28497
+ return true;
28498
+ } catch {
28499
+ return false;
28500
+ }
28501
+ }
28502
+
28503
+ // src/acp/clients/claude-code-acp-client.ts
28504
+ var execFileAsync4 = promisify4(execFile4);
28505
+ var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
28506
+ async function detectLocalAgentPresence() {
28507
+ if (await isCommandOnPath("claude")) return true;
28508
+ try {
28509
+ await execFileAsync4("npx", ["--yes", "@anthropic-ai/claude-code", "--version"], { timeout: 25e3 });
28510
+ return true;
28511
+ } catch {
28512
+ return false;
28513
+ }
28514
+ }
28515
+ function buildClaudeCodeAcpSpawnCommand(base, sessionMode) {
28516
+ if (!sessionMode) return [...base];
28517
+ const m = sessionMode.trim();
28518
+ if (m === "plan") return [...base, "--permission-mode", "plan"];
28519
+ return [...base];
28520
+ }
28521
+ async function createClaudeCodeAcpClient(options) {
28522
+ const command = buildClaudeCodeAcpSpawnCommand(options.command, options.sessionMode);
28523
+ return createSdkStdioAcpClient({
28524
+ ...options,
28525
+ command,
28526
+ /** Claude-based agents sometimes ignore `session/cancel`; unblocks stop / stuck prompt. */
28527
+ killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 1e3
28528
+ });
28529
+ }
28530
+
27905
28531
  // src/acp/clients/codex-acp-client.ts
28532
+ var codex_acp_client_exports = {};
28533
+ __export(codex_acp_client_exports, {
28534
+ BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE2,
28535
+ DEFAULT_CODEX_ACP_COMMAND: () => DEFAULT_CODEX_ACP_COMMAND,
28536
+ buildCodexAcpSpawnCommand: () => buildCodexAcpSpawnCommand,
28537
+ createCodexAcpClient: () => createCodexAcpClient,
28538
+ detectLocalAgentPresence: () => detectLocalAgentPresence2,
28539
+ isCodexAcpCommand: () => isCodexAcpCommand
28540
+ });
28541
+ var BACKEND_LOCAL_AGENT_TYPE2 = "codex-acp";
28542
+ async function detectLocalAgentPresence2() {
28543
+ return isCommandOnPath("codex");
28544
+ }
27906
28545
  var DEFAULT_CODEX_ACP_COMMAND = ["npx", "--yes", "@zed-industries/codex-acp"];
27907
28546
  function isCodexAcpCommand(command) {
27908
28547
  const i = command.indexOf("@zed-industries/codex-acp");
27909
28548
  return i >= 0 && (i === 0 || command[i - 1] === "npx" || command[i - 1] === "bunx");
27910
28549
  }
28550
+ function buildCodexAcpSpawnCommand(base, _sessionMode) {
28551
+ return [...base];
28552
+ }
27911
28553
  async function createCodexAcpClient(options) {
27912
- const command = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
27913
- return createAcpClient({ ...options, command });
28554
+ const base = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
28555
+ const command = buildCodexAcpSpawnCommand(base, options.sessionMode);
28556
+ return createSdkStdioAcpClient({ ...options, command });
27914
28557
  }
27915
28558
 
27916
28559
  // src/acp/clients/cursor-acp-client.ts
27917
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
27918
- import { dirname } from "node:path";
28560
+ var cursor_acp_client_exports = {};
28561
+ __export(cursor_acp_client_exports, {
28562
+ BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE3,
28563
+ buildCursorAcpSpawnCommand: () => buildCursorAcpSpawnCommand,
28564
+ createCursorAcpClient: () => createCursorAcpClient,
28565
+ detectLocalAgentPresence: () => detectLocalAgentPresence3
28566
+ });
28567
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
28568
+ import { dirname as dirname2 } from "node:path";
27919
28569
  import { spawn as spawn4 } from "node:child_process";
27920
28570
  import * as readline from "node:readline";
27921
28571
 
27922
- // src/acp/safe-fs-path.ts
27923
- import * as path6 from "node:path";
27924
- function resolveSafePathUnderCwd(cwd, filePath) {
27925
- const trimmed2 = filePath.trim();
27926
- if (!trimmed2) return null;
27927
- const normalizedCwd = path6.resolve(cwd);
27928
- const resolved = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.resolve(normalizedCwd, trimmed2);
27929
- const rel = path6.relative(normalizedCwd, resolved);
27930
- if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
27931
- return resolved;
27932
- }
27933
- function toDisplayPathRelativeToCwd(cwd, absolutePath) {
27934
- const normalizedCwd = path6.resolve(cwd);
27935
- const rel = path6.relative(normalizedCwd, path6.resolve(absolutePath));
27936
- if (!rel || rel === "") return path6.basename(absolutePath);
27937
- return rel.split(path6.sep).join("/");
27938
- }
27939
-
27940
- // src/files/diff/unified-diff.ts
27941
- function computeLineDiff(oldText, newText) {
27942
- const oldLines = oldText.split("\n");
27943
- const newLines = newText.split("\n");
27944
- const m = oldLines.length;
27945
- const n = newLines.length;
27946
- const dp = Array(m + 1);
27947
- for (let i2 = 0; i2 <= m; i2++) dp[i2] = Array(n + 1).fill(0);
27948
- for (let i2 = 1; i2 <= m; i2++) {
27949
- for (let j2 = 1; j2 <= n; j2++) {
27950
- if (oldLines[i2 - 1] === newLines[j2 - 1]) {
27951
- dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
27952
- } else {
27953
- dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
27954
- }
27955
- }
27956
- }
27957
- const result = [];
27958
- let i = m;
27959
- let j = n;
27960
- while (i > 0 || j > 0) {
27961
- if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
27962
- result.unshift({ type: "context", line: oldLines[i - 1] });
27963
- i--;
27964
- j--;
27965
- } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
27966
- result.unshift({ type: "add", line: newLines[j - 1] });
27967
- j--;
27968
- } else {
27969
- result.unshift({ type: "remove", line: oldLines[i - 1] });
27970
- i--;
27971
- }
27972
- }
27973
- return result;
27974
- }
27975
- function editSnippetToUnifiedDiff(filePath, oldText, newText) {
27976
- const lines = computeLineDiff(oldText, newText);
27977
- const out = [`--- ${filePath}`, `+++ ${filePath}`];
27978
- for (const d of lines) {
27979
- if (d.type === "add") out.push(`+${d.line}`);
27980
- else if (d.type === "remove") out.push(`-${d.line}`);
27981
- else out.push(` ${d.line}`);
27982
- }
27983
- return out.join("\n");
27984
- }
27985
-
27986
28572
  // src/acp/format-session-update-kind-for-log.ts
27987
28573
  var SESSION_UPDATE_KIND_LABELS = {
27988
28574
  tool_call: "Tool call",
@@ -28020,16 +28606,31 @@ function sliceLinesByRange(content, line, limit) {
28020
28606
  const end = limit != null && limit > 0 ? start + limit : lines.length;
28021
28607
  return lines.slice(start, end).join("\n");
28022
28608
  }
28609
+ function buildCursorAcpSpawnCommand(base, sessionMode) {
28610
+ if (!sessionMode) return [...base];
28611
+ const m = sessionMode.trim();
28612
+ if (m !== "ask" && m !== "plan") return [...base];
28613
+ return [...base, "--mode", m];
28614
+ }
28023
28615
  async function createCursorAcpClient(options) {
28024
- const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate, onRequest, onFileChange } = options;
28616
+ const command = buildCursorAcpSpawnCommand(options.command, options.sessionMode);
28617
+ const {
28618
+ cwd = getBridgeWorkspaceDirectory(),
28619
+ backendAgentType,
28620
+ onSessionUpdate,
28621
+ onRequest,
28622
+ onFileChange
28623
+ } = options;
28025
28624
  const dbgFs = process.env.BUILDAMATON_DEBUG_ACP_FS === "1";
28026
28625
  const isWindows = process.platform === "win32";
28027
28626
  const child = spawn4(command[0], command.slice(1), {
28028
28627
  cwd,
28029
- stdio: ["pipe", "pipe", "inherit"],
28628
+ stdio: ["pipe", "pipe", "pipe"],
28030
28629
  env: process.env,
28031
28630
  shell: isWindows
28032
28631
  });
28632
+ const stderrCapture = createStderrCapture(child);
28633
+ child.stderr?.on("data", (chunk) => stderrCapture.append(chunk));
28033
28634
  return new Promise((resolve14, reject) => {
28034
28635
  child.on("error", (err) => {
28035
28636
  child.kill();
@@ -28153,8 +28754,8 @@ async function createCursorAcpClient(options) {
28153
28754
  }
28154
28755
  }
28155
28756
  try {
28156
- mkdirSync2(dirname(abs), { recursive: true });
28157
- writeFileSync2(abs, newText, "utf8");
28757
+ mkdirSync3(dirname2(abs), { recursive: true });
28758
+ writeFileSync3(abs, newText, "utf8");
28158
28759
  } catch (e) {
28159
28760
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
28160
28761
  return;
@@ -28213,17 +28814,52 @@ async function createCursorAcpClient(options) {
28213
28814
  sessionId,
28214
28815
  prompt: [{ type: "text", text: prompt }]
28215
28816
  });
28817
+ await new Promise((r) => setImmediate(r));
28216
28818
  const output = (result?.output ?? promptOutputBuffer) || void 0;
28217
- const cancelled = (result?.stopReason ?? "").toLowerCase() === "cancelled";
28819
+ const stopReason = (result?.stopReason ?? "").toLowerCase();
28820
+ const cancelled = stopReason === "cancelled";
28821
+ const refusal = stopReason === "refusal";
28822
+ const stderrAfter = stderrCapture.getText();
28823
+ const agentType = backendAgentType ?? null;
28824
+ const stderrEvaluated = Boolean(stderrAfter && agentType);
28825
+ const stderrSuggestsAuth = stderrEvaluated ? localAgentErrorSuggestsAuth(agentType, stderrAfter) : false;
28826
+ if (cancelled) {
28827
+ return {
28828
+ success: false,
28829
+ stopReason: result?.stopReason,
28830
+ output: output || void 0,
28831
+ error: mergeErrorWithStderr("Stopped by user", stderrAfter)
28832
+ };
28833
+ }
28834
+ if (refusal) {
28835
+ return {
28836
+ success: false,
28837
+ stopReason: result?.stopReason,
28838
+ output: output || void 0,
28839
+ error: mergeErrorWithStderr("The agent refused the request.", stderrAfter)
28840
+ };
28841
+ }
28842
+ if (stderrSuggestsAuth) {
28843
+ return {
28844
+ success: false,
28845
+ stopReason: result?.stopReason,
28846
+ output: output || void 0,
28847
+ error: stderrAfter
28848
+ };
28849
+ }
28218
28850
  return {
28219
- success: !cancelled,
28851
+ success: true,
28220
28852
  stopReason: result?.stopReason,
28221
- output: output || void 0,
28222
- ...cancelled ? { error: "Stopped by user" } : {}
28853
+ output: output || void 0
28223
28854
  };
28224
28855
  } catch (err) {
28225
- const message = err != null && typeof err === "object" && "message" in err ? String(err.message) : String(err);
28226
- return { success: false, error: message };
28856
+ await new Promise((r) => setImmediate(r));
28857
+ const stderrAfter = stderrCapture.getText();
28858
+ const merged = mergeErrorWithStderr(formatJsonRpcStyleError(err), stderrAfter);
28859
+ return {
28860
+ success: false,
28861
+ error: merged
28862
+ };
28227
28863
  }
28228
28864
  },
28229
28865
  async cancel() {
@@ -28242,38 +28878,120 @@ async function createCursorAcpClient(options) {
28242
28878
  });
28243
28879
  } catch (err) {
28244
28880
  child.kill();
28245
- reject(err);
28881
+ const merged = mergeErrorWithStderr(formatJsonRpcStyleError(err), stderrCapture.getText());
28882
+ reject(merged ? new Error(merged) : err instanceof Error ? err : new Error(String(err)));
28246
28883
  }
28247
28884
  })();
28248
28885
  });
28249
28886
  }
28887
+ var BACKEND_LOCAL_AGENT_TYPE3 = "cursor-cli";
28888
+ async function detectLocalAgentPresence3() {
28889
+ return isCommandOnPath("agent");
28890
+ }
28891
+
28892
+ // src/acp/clients/kiro-acp-client.ts
28893
+ var kiro_acp_client_exports = {};
28894
+ __export(kiro_acp_client_exports, {
28895
+ BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE4,
28896
+ DEFAULT_KIRO_ACP_COMMAND: () => DEFAULT_KIRO_ACP_COMMAND,
28897
+ buildKiroAcpSpawnCommand: () => buildKiroAcpSpawnCommand,
28898
+ createKiroAcpClient: () => createKiroAcpClient,
28899
+ detectLocalAgentPresence: () => detectLocalAgentPresence4,
28900
+ isKiroAcpCommand: () => isKiroAcpCommand
28901
+ });
28902
+ var BACKEND_LOCAL_AGENT_TYPE4 = "kiro-acp";
28903
+ async function detectLocalAgentPresence4() {
28904
+ return isCommandOnPath("kiro-cli");
28905
+ }
28906
+ var DEFAULT_KIRO_ACP_COMMAND = ["kiro-cli", "acp"];
28907
+ function isKiroAcpCommand(command) {
28908
+ if (command.length < 2) return false;
28909
+ if (command[command.length - 1] !== "acp") return false;
28910
+ return command.slice(0, -1).some(
28911
+ (a) => a === "kiro-cli" || /[/\\]kiro-cli(\.exe)?$/i.test(a)
28912
+ );
28913
+ }
28914
+ function buildKiroAcpSpawnCommand(base, _sessionMode) {
28915
+ return [...base];
28916
+ }
28917
+ async function createKiroAcpClient(options) {
28918
+ const base = options.command?.length && isKiroAcpCommand(options.command) ? options.command : [...DEFAULT_KIRO_ACP_COMMAND];
28919
+ const command = buildKiroAcpSpawnCommand(base, options.sessionMode);
28920
+ return createSdkStdioAcpClient({ ...options, command });
28921
+ }
28250
28922
 
28251
28923
  // src/acp/resolve-agent-command.ts
28252
28924
  var AGENT_TYPE_DEFAULT_COMMANDS = {
28253
- "cursor-cli": ["agent", "acp"],
28254
- "codex-acp": [...DEFAULT_CODEX_ACP_COMMAND],
28255
- "claude-code": ["npx", "--yes", "@anthropic-ai/claude-code"]
28925
+ [BACKEND_LOCAL_AGENT_TYPE3]: ["agent", "acp"],
28926
+ [BACKEND_LOCAL_AGENT_TYPE2]: [...DEFAULT_CODEX_ACP_COMMAND],
28927
+ /** ACP stdio agent; `@anthropic-ai/claude-code` is the interactive CLI and does not speak ACP on stdout. */
28928
+ [BACKEND_LOCAL_AGENT_TYPE]: ["npx", "--yes", "@agentclientprotocol/claude-agent-acp"],
28929
+ /** [Kiro CLI ACP](https://kiro.dev/docs/cli/acp/) — use full path to `kiro-cli` in PATH if the IDE cannot find it. */
28930
+ [BACKEND_LOCAL_AGENT_TYPE4]: [...DEFAULT_KIRO_ACP_COMMAND]
28256
28931
  };
28932
+ var AGENT_TYPE_DISPLAY_NAMES = {
28933
+ [BACKEND_LOCAL_AGENT_TYPE3]: "Cursor",
28934
+ [BACKEND_LOCAL_AGENT_TYPE2]: "Codex",
28935
+ [BACKEND_LOCAL_AGENT_TYPE]: "Claude Code",
28936
+ [BACKEND_LOCAL_AGENT_TYPE4]: "Kiro"
28937
+ };
28938
+ function getAgentTypeDisplayName(agentType) {
28939
+ if (agentType == null || agentType === "") return "Unknown agent";
28940
+ const known = AGENT_TYPE_DISPLAY_NAMES[agentType];
28941
+ if (known) return known;
28942
+ return agentType.split(/[-_]/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
28943
+ }
28257
28944
  function useCursorAcp(agentType, command) {
28258
- if (agentType === "cursor-cli") return true;
28945
+ if (agentType === BACKEND_LOCAL_AGENT_TYPE3) return true;
28259
28946
  return command[0] === "agent" && command[1] === "acp";
28260
28947
  }
28261
28948
  function useCodexAcp(agentType, command) {
28262
- if (agentType === "codex-acp") return true;
28949
+ if (agentType === BACKEND_LOCAL_AGENT_TYPE2) return true;
28263
28950
  return isCodexAcpCommand(command);
28264
28951
  }
28952
+ function useKiroAcp(agentType, command) {
28953
+ if (agentType === BACKEND_LOCAL_AGENT_TYPE4) return true;
28954
+ return isKiroAcpCommand(command);
28955
+ }
28265
28956
  function resolveAgentCommand(preferredAgentType) {
28266
28957
  if (!preferredAgentType) return null;
28267
28958
  const command = AGENT_TYPE_DEFAULT_COMMANDS[preferredAgentType];
28268
28959
  if (!command?.length) return null;
28269
- const createClient = useCursorAcp(preferredAgentType, command) ? createCursorAcpClient : useCodexAcp(preferredAgentType, command) ? createCodexAcpClient : createAcpClient;
28270
- const label = preferredAgentType;
28271
- return { command, label, createClient };
28960
+ if (useCursorAcp(preferredAgentType, command)) {
28961
+ return {
28962
+ command,
28963
+ label: preferredAgentType,
28964
+ createClient: createCursorAcpClient,
28965
+ spawnCommandForSession: (sessionMode) => buildCursorAcpSpawnCommand(command, sessionMode)
28966
+ };
28967
+ }
28968
+ if (useCodexAcp(preferredAgentType, command)) {
28969
+ return {
28970
+ command,
28971
+ label: preferredAgentType,
28972
+ createClient: createCodexAcpClient,
28973
+ spawnCommandForSession: (sessionMode) => buildCodexAcpSpawnCommand(command, sessionMode)
28974
+ };
28975
+ }
28976
+ if (useKiroAcp(preferredAgentType, command)) {
28977
+ return {
28978
+ command,
28979
+ label: preferredAgentType,
28980
+ createClient: createKiroAcpClient,
28981
+ spawnCommandForSession: (sessionMode) => buildKiroAcpSpawnCommand(command, sessionMode)
28982
+ };
28983
+ }
28984
+ return {
28985
+ command,
28986
+ label: preferredAgentType,
28987
+ createClient: createClaudeCodeAcpClient,
28988
+ spawnCommandForSession: (sessionMode) => buildClaudeCodeAcpSpawnCommand(command, sessionMode)
28989
+ };
28272
28990
  }
28273
28991
 
28274
28992
  // src/acp/session-file-change-path-kind.ts
28275
28993
  import { execFileSync as execFileSync3 } from "node:child_process";
28276
- import { existsSync, statSync as statSync2 } from "node:fs";
28994
+ import { existsSync, statSync } from "node:fs";
28277
28995
 
28278
28996
  // src/git/get-git-repo-root-sync.ts
28279
28997
  import { execFileSync } from "node:child_process";
@@ -28382,7 +29100,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
28382
29100
  const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
28383
29101
  if (abs && existsSync(abs)) {
28384
29102
  try {
28385
- if (statSync2(abs).isDirectory()) {
29103
+ if (statSync(abs).isDirectory()) {
28386
29104
  return { isDirectory: true, directoryRemoved: false };
28387
29105
  }
28388
29106
  return { isDirectory: false, directoryRemoved: false };
@@ -28928,7 +29646,8 @@ async function ensureAcpClient(options) {
28928
29646
  state.lastAcpStartError = "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge.";
28929
29647
  return null;
28930
29648
  }
28931
- const agentKey = `${resolved.label}::${resolved.command.join("\0")}`;
29649
+ const fullCmd = resolved.spawnCommandForSession(mode);
29650
+ const agentKey = `${resolved.label}::${fullCmd.join("\0")}`;
28932
29651
  if (state.acpHandle && state.acpAgentKey !== agentKey) {
28933
29652
  try {
28934
29653
  state.acpHandle.disconnect();
@@ -28942,7 +29661,7 @@ async function ensureAcpClient(options) {
28942
29661
  if (!state.acpStartPromise) {
28943
29662
  let statOk = false;
28944
29663
  try {
28945
- const st = fs5.statSync(targetCwd);
29664
+ const st = fs4.statSync(targetCwd);
28946
29665
  statOk = st.isDirectory();
28947
29666
  if (!statOk) {
28948
29667
  state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
@@ -28955,8 +29674,6 @@ async function ensureAcpClient(options) {
28955
29674
  if (!statOk) {
28956
29675
  return null;
28957
29676
  }
28958
- const modeFlag = mode && ["ask", "plan"].includes(mode) ? ["--mode", mode] : [];
28959
- const fullCmd = [...resolved.command, ...modeFlag];
28960
29677
  const hooks = buildAcpSessionBridgeHooks({
28961
29678
  routing,
28962
29679
  getSendSessionUpdate: () => sendSessionUpdate,
@@ -28964,8 +29681,16 @@ async function ensureAcpClient(options) {
28964
29681
  log: log2
28965
29682
  });
28966
29683
  state.acpStartPromise = resolved.createClient({
28967
- command: fullCmd,
29684
+ command: resolved.command,
29685
+ sessionMode: mode,
28968
29686
  cwd: targetCwd,
29687
+ backendAgentType: preferredAgentType,
29688
+ onAgentSubprocessExit: () => {
29689
+ state.acpHandle = null;
29690
+ state.acpStartPromise = null;
29691
+ state.acpAgentKey = null;
29692
+ state.lastAcpStartError = "Agent subprocess exited";
29693
+ },
28969
29694
  ...hooks
28970
29695
  }).then((h) => {
28971
29696
  state.lastAcpStartError = null;
@@ -29003,6 +29728,14 @@ async function createAcpManager(options) {
29003
29728
  backendFallbackAgentType = agentType;
29004
29729
  }
29005
29730
  }
29731
+ function logPromptReceivedFromBridge(opts) {
29732
+ const { agentType, mode } = opts;
29733
+ const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
29734
+ const modeLabel = typeof mode === "string" && mode.trim() !== "" ? mode.trim() : "not set";
29735
+ log2(
29736
+ `[Agent] Prompt received (${getAgentTypeDisplayName(preferredForPrompt)}, mode: ${modeLabel})`
29737
+ );
29738
+ }
29006
29739
  function handlePrompt(opts) {
29007
29740
  const {
29008
29741
  promptText,
@@ -29032,17 +29765,24 @@ async function createAcpManager(options) {
29032
29765
  log: log2
29033
29766
  });
29034
29767
  if (!handle) {
29768
+ const errMsg = state.lastAcpStartError || "No agent configured. Register local agents on this bridge in the app.";
29769
+ const evaluated = Boolean(preferredForPrompt && errMsg.trim());
29770
+ const suggestsAuth = evaluated ? localAgentErrorSuggestsAuth(preferredForPrompt, errMsg) : false;
29771
+ const auth = suggestsAuth && preferredForPrompt ? { agentAuthRequired: true, agentType: preferredForPrompt } : {};
29035
29772
  sendResult({
29036
29773
  type: "prompt_result",
29037
29774
  id: promptId,
29038
29775
  ...sessionId ? { sessionId } : {},
29039
29776
  ...runId ? { runId } : {},
29040
29777
  success: false,
29041
- error: state.lastAcpStartError || "No agent configured. Register local agents on this bridge in the app."
29778
+ error: errMsg,
29779
+ ...auth
29042
29780
  });
29043
29781
  return;
29044
29782
  }
29045
- if (promptRouting.sessionId !== sessionId || promptRouting.runId !== runId) return;
29783
+ if (promptRouting.sessionId !== sessionId || promptRouting.runId !== runId) {
29784
+ return;
29785
+ }
29046
29786
  if (runId && pendingCancelRunId === runId) {
29047
29787
  pendingCancelRunId = void 0;
29048
29788
  try {
@@ -29066,6 +29806,7 @@ async function createAcpManager(options) {
29066
29806
  promptId,
29067
29807
  sessionId,
29068
29808
  runId,
29809
+ agentType: preferredForPrompt,
29069
29810
  agentCwd: cwd,
29070
29811
  sendResult,
29071
29812
  sendSessionUpdate,
@@ -29083,6 +29824,7 @@ async function createAcpManager(options) {
29083
29824
  if (promptRouting.runId !== runId) return false;
29084
29825
  const handle = state.acpHandle;
29085
29826
  if (handle?.cancel) {
29827
+ log2("[Agent] Stop requested");
29086
29828
  try {
29087
29829
  await handle.cancel();
29088
29830
  return true;
@@ -29091,6 +29833,7 @@ async function createAcpManager(options) {
29091
29833
  return false;
29092
29834
  }
29093
29835
  }
29836
+ log2("[Agent] Stop requested (agent still starting)");
29094
29837
  pendingCancelRunId = runId;
29095
29838
  return true;
29096
29839
  }
@@ -29103,7 +29846,14 @@ async function createAcpManager(options) {
29103
29846
  state.acpStartPromise = null;
29104
29847
  state.acpAgentKey = null;
29105
29848
  }
29106
- return { setPreferredAgentType, handlePrompt, cancelRun, resolveRequest, disconnect };
29849
+ return {
29850
+ setPreferredAgentType,
29851
+ logPromptReceivedFromBridge,
29852
+ handlePrompt,
29853
+ cancelRun,
29854
+ resolveRequest,
29855
+ disconnect
29856
+ };
29107
29857
  }
29108
29858
 
29109
29859
  // src/bridge/routing/handlers/auth-token.ts
@@ -29154,8 +29904,8 @@ var handleAgentConfigMessage = (msg, deps) => {
29154
29904
 
29155
29905
  // src/acp/from-bridge/handle-bridge-prompt.ts
29156
29906
  import * as path11 from "node:path";
29157
- import { execFile as execFile3 } from "node:child_process";
29158
- import { promisify as promisify3 } from "node:util";
29907
+ import { execFile as execFile5 } from "node:child_process";
29908
+ import { promisify as promisify5 } from "node:util";
29159
29909
 
29160
29910
  // src/git/bridge-queue-key.ts
29161
29911
  import * as path10 from "node:path";
@@ -29213,10 +29963,10 @@ async function resolveBridgeQueueBindFields(options) {
29213
29963
  }
29214
29964
 
29215
29965
  // src/acp/from-bridge/handle-bridge-prompt.ts
29216
- var execFileAsync3 = promisify3(execFile3);
29966
+ var execFileAsync5 = promisify5(execFile5);
29217
29967
  async function readGitBranch(cwd) {
29218
29968
  try {
29219
- const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
29969
+ const { stdout } = await execFileAsync5("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
29220
29970
  const b = stdout.trim();
29221
29971
  return b || null;
29222
29972
  } catch {
@@ -29238,6 +29988,8 @@ function handleBridgePrompt(msg, deps) {
29238
29988
  const sessionWorktreesEnabled = msg.sessionWorktreesEnabled === true;
29239
29989
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
29240
29990
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
29991
+ const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
29992
+ acpManager.logPromptReceivedFromBridge({ agentType, mode });
29241
29993
  const sendResult = (result) => {
29242
29994
  const s = getWs();
29243
29995
  if (s) sendWsMessage(s, result);
@@ -29300,7 +30052,7 @@ function handleBridgePrompt(msg, deps) {
29300
30052
  promptId: msg.id,
29301
30053
  sessionId,
29302
30054
  runId,
29303
- mode: msg.mode,
30055
+ mode,
29304
30056
  agentType,
29305
30057
  cwd: effectiveCwd,
29306
30058
  sendResult,
@@ -29370,7 +30122,7 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
29370
30122
  };
29371
30123
 
29372
30124
  // src/files/list-dir.ts
29373
- import fs6 from "node:fs";
30125
+ import fs5 from "node:fs";
29374
30126
  import path13 from "node:path";
29375
30127
 
29376
30128
  // src/files/ensure-under-cwd.ts
@@ -29391,14 +30143,14 @@ function listDir(relativePath) {
29391
30143
  return { error: "Path is outside working directory" };
29392
30144
  }
29393
30145
  try {
29394
- const names = fs6.readdirSync(resolved, { withFileTypes: true });
30146
+ const names = fs5.readdirSync(resolved, { withFileTypes: true });
29395
30147
  const entries = names.filter((d) => !d.name.startsWith(".")).map((d) => {
29396
30148
  const entryPath = path13.join(relativePath || ".", d.name).replace(/\\/g, "/");
29397
30149
  const fullPath = path13.join(resolved, d.name);
29398
30150
  let isDir = d.isDirectory();
29399
30151
  if (d.isSymbolicLink()) {
29400
30152
  try {
29401
- const targetStat = fs6.statSync(fullPath);
30153
+ const targetStat = fs5.statSync(fullPath);
29402
30154
  isDir = targetStat.isDirectory();
29403
30155
  } catch {
29404
30156
  isDir = false;
@@ -29422,25 +30174,25 @@ function listDir(relativePath) {
29422
30174
  }
29423
30175
 
29424
30176
  // src/files/read-file.ts
29425
- import fs7 from "node:fs";
30177
+ import fs6 from "node:fs";
29426
30178
  import { StringDecoder } from "node:string_decoder";
29427
30179
  function resolveFilePath(relativePath) {
29428
30180
  const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
29429
30181
  if (!resolved) return { error: "Path is outside working directory" };
29430
30182
  let real;
29431
30183
  try {
29432
- real = fs7.realpathSync(resolved);
30184
+ real = fs6.realpathSync(resolved);
29433
30185
  } catch {
29434
30186
  real = resolved;
29435
30187
  }
29436
- const stat = fs7.statSync(real);
29437
- if (!stat.isFile()) return { error: "Not a file" };
30188
+ const stat2 = fs6.statSync(real);
30189
+ if (!stat2.isFile()) return { error: "Not a file" };
29438
30190
  return real;
29439
30191
  }
29440
30192
  var LINE_CHUNK_SIZE = 64 * 1024;
29441
30193
  function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
29442
- const fileSize = fs7.statSync(filePath).size;
29443
- const fd = fs7.openSync(filePath, "r");
30194
+ const fileSize = fs6.statSync(filePath).size;
30195
+ const fd = fs6.openSync(filePath, "r");
29444
30196
  const bufSize = 64 * 1024;
29445
30197
  const buf = Buffer.alloc(bufSize);
29446
30198
  const decoder = new StringDecoder("utf8");
@@ -29453,7 +30205,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
29453
30205
  let line0Accum = "";
29454
30206
  try {
29455
30207
  let bytesRead;
29456
- while (!done && (bytesRead = fs7.readSync(fd, buf, 0, bufSize, null)) > 0) {
30208
+ while (!done && (bytesRead = fs6.readSync(fd, buf, 0, bufSize, null)) > 0) {
29457
30209
  const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
29458
30210
  partial2 = "";
29459
30211
  let lineStart = 0;
@@ -29588,10 +30340,10 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
29588
30340
  }
29589
30341
  return { content: resultLines.join("\n"), size: fileSize };
29590
30342
  } finally {
29591
- fs7.closeSync(fd);
30343
+ fs6.closeSync(fd);
29592
30344
  }
29593
30345
  }
29594
- function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
30346
+ function readFile2(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
29595
30347
  try {
29596
30348
  const result = resolveFilePath(relativePath);
29597
30349
  if (typeof result === "object") return result;
@@ -29599,17 +30351,17 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
29599
30351
  if (hasRange) {
29600
30352
  return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
29601
30353
  }
29602
- const stat = fs7.statSync(result);
29603
- const raw = fs7.readFileSync(result, "utf8");
30354
+ const stat2 = fs6.statSync(result);
30355
+ const raw = fs6.readFileSync(result, "utf8");
29604
30356
  const lines = raw.split(/\r?\n/);
29605
- return { content: raw, totalLines: lines.length, size: stat.size };
30357
+ return { content: raw, totalLines: lines.length, size: stat2.size };
29606
30358
  } catch (err) {
29607
30359
  return { error: err instanceof Error ? err.message : String(err) };
29608
30360
  }
29609
30361
  }
29610
30362
 
29611
30363
  // src/files/file-index.ts
29612
- import fs8 from "node:fs";
30364
+ import fs7 from "node:fs";
29613
30365
  import path14 from "node:path";
29614
30366
  import os2 from "node:os";
29615
30367
  import crypto2 from "node:crypto";
@@ -29656,23 +30408,23 @@ function binarySearch(arr, x) {
29656
30408
  function walkDir(dir, baseDir, out) {
29657
30409
  let names;
29658
30410
  try {
29659
- names = fs8.readdirSync(dir);
30411
+ names = fs7.readdirSync(dir);
29660
30412
  } catch {
29661
30413
  return;
29662
30414
  }
29663
30415
  for (const name of names) {
29664
30416
  if (name.startsWith(".")) continue;
29665
30417
  const full = path14.join(dir, name);
29666
- let stat;
30418
+ let stat2;
29667
30419
  try {
29668
- stat = fs8.statSync(full);
30420
+ stat2 = fs7.statSync(full);
29669
30421
  } catch {
29670
30422
  continue;
29671
30423
  }
29672
30424
  const relative4 = path14.relative(baseDir, full).replace(/\\/g, "/");
29673
- if (stat.isDirectory()) {
30425
+ if (stat2.isDirectory()) {
29674
30426
  walkDir(full, baseDir, out);
29675
- } else if (stat.isFile()) {
30427
+ } else if (stat2.isFile()) {
29676
30428
  out.push(relative4);
29677
30429
  }
29678
30430
  }
@@ -29696,8 +30448,8 @@ function buildFileIndex(cwd) {
29696
30448
  const data = { version: INDEX_VERSION, paths, trigramIndex };
29697
30449
  const indexPath = getIndexPath(resolved);
29698
30450
  try {
29699
- if (!fs8.existsSync(INDEX_DIR)) fs8.mkdirSync(INDEX_DIR, { recursive: true });
29700
- fs8.writeFileSync(indexPath, JSON.stringify(data), "utf8");
30451
+ if (!fs7.existsSync(INDEX_DIR)) fs7.mkdirSync(INDEX_DIR, { recursive: true });
30452
+ fs7.writeFileSync(indexPath, JSON.stringify(data), "utf8");
29701
30453
  } catch (e) {
29702
30454
  console.error("[file-index] Failed to write index:", e);
29703
30455
  }
@@ -29707,7 +30459,7 @@ function loadFileIndex(cwd) {
29707
30459
  const resolved = path14.resolve(cwd);
29708
30460
  const indexPath = getIndexPath(resolved);
29709
30461
  try {
29710
- const raw = fs8.readFileSync(indexPath, "utf8");
30462
+ const raw = fs7.readFileSync(indexPath, "utf8");
29711
30463
  const parsed = JSON.parse(raw);
29712
30464
  if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
29713
30465
  const obj = parsed;
@@ -29811,7 +30563,7 @@ function handleFileBrowserRequest(msg, socket) {
29811
30563
  const endLine = typeof msg.endLine === "number" ? msg.endLine : void 0;
29812
30564
  const lineOffset = typeof msg.lineOffset === "number" ? msg.lineOffset : void 0;
29813
30565
  const lineChunkSize = typeof msg.lineChunkSize === "number" ? msg.lineChunkSize : void 0;
29814
- const result = readFile(reqPath, startLine, endLine, lineOffset, lineChunkSize);
30566
+ const result = readFile2(reqPath, startLine, endLine, lineOffset, lineChunkSize);
29815
30567
  if ("error" in result) {
29816
30568
  sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
29817
30569
  } else {
@@ -29846,7 +30598,7 @@ function handleFileBrowserSearchMessage(msg, { getWs }) {
29846
30598
  }
29847
30599
 
29848
30600
  // src/skills/discover-local-agent-skills.ts
29849
- import fs9 from "node:fs";
30601
+ import fs8 from "node:fs";
29850
30602
  import path15 from "node:path";
29851
30603
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
29852
30604
  function discoverLocalSkills(cwd) {
@@ -29854,22 +30606,22 @@ function discoverLocalSkills(cwd) {
29854
30606
  const seenKeys = /* @__PURE__ */ new Set();
29855
30607
  for (const rel of SKILL_DISCOVERY_ROOTS) {
29856
30608
  const base = path15.join(cwd, rel);
29857
- if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
30609
+ if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
29858
30610
  let entries = [];
29859
30611
  try {
29860
- entries = fs9.readdirSync(base);
30612
+ entries = fs8.readdirSync(base);
29861
30613
  } catch {
29862
30614
  continue;
29863
30615
  }
29864
30616
  for (const name of entries) {
29865
30617
  const dir = path15.join(base, name);
29866
30618
  try {
29867
- if (!fs9.statSync(dir).isDirectory()) continue;
30619
+ if (!fs8.statSync(dir).isDirectory()) continue;
29868
30620
  } catch {
29869
30621
  continue;
29870
30622
  }
29871
30623
  const skillMd = path15.join(dir, "SKILL.md");
29872
- if (!fs9.existsSync(skillMd)) continue;
30624
+ if (!fs8.existsSync(skillMd)) continue;
29873
30625
  const key = `${rel}/${name}`;
29874
30626
  if (seenKeys.has(key)) continue;
29875
30627
  seenKeys.add(key);
@@ -29882,10 +30634,10 @@ function discoverSkillLayoutRoots(cwd) {
29882
30634
  const roots = [];
29883
30635
  for (const rel of SKILL_DISCOVERY_ROOTS) {
29884
30636
  const base = path15.join(cwd, rel);
29885
- if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
30637
+ if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
29886
30638
  let entries = [];
29887
30639
  try {
29888
- entries = fs9.readdirSync(base);
30640
+ entries = fs8.readdirSync(base);
29889
30641
  } catch {
29890
30642
  continue;
29891
30643
  }
@@ -29893,11 +30645,11 @@ function discoverSkillLayoutRoots(cwd) {
29893
30645
  for (const name of entries) {
29894
30646
  const dir = path15.join(base, name);
29895
30647
  try {
29896
- if (!fs9.statSync(dir).isDirectory()) continue;
30648
+ if (!fs8.statSync(dir).isDirectory()) continue;
29897
30649
  } catch {
29898
30650
  continue;
29899
30651
  }
29900
- if (!fs9.existsSync(path15.join(dir, "SKILL.md"))) continue;
30652
+ if (!fs8.existsSync(path15.join(dir, "SKILL.md"))) continue;
29901
30653
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
29902
30654
  skills2.push({ name, relPath });
29903
30655
  }
@@ -29917,7 +30669,7 @@ function handleSkillLayoutRequest(msg, deps) {
29917
30669
  }
29918
30670
 
29919
30671
  // src/skills/install-remote-skills.ts
29920
- import fs10 from "node:fs";
30672
+ import fs9 from "node:fs";
29921
30673
  import path16 from "node:path";
29922
30674
  function installRemoteSkills(cwd, targetDir, items) {
29923
30675
  const installed = [];
@@ -29933,11 +30685,11 @@ function installRemoteSkills(cwd, targetDir, items) {
29933
30685
  for (const f of item.files) {
29934
30686
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
29935
30687
  const dest = path16.join(skillDir, f.path);
29936
- fs10.mkdirSync(path16.dirname(dest), { recursive: true });
30688
+ fs9.mkdirSync(path16.dirname(dest), { recursive: true });
29937
30689
  if (f.text !== void 0) {
29938
- fs10.writeFileSync(dest, f.text, "utf8");
30690
+ fs9.writeFileSync(dest, f.text, "utf8");
29939
30691
  } else if (f.base64) {
29940
- fs10.writeFileSync(dest, Buffer.from(f.base64, "base64"));
30692
+ fs9.writeFileSync(dest, Buffer.from(f.base64, "base64"));
29941
30693
  }
29942
30694
  }
29943
30695
  installed.push({
@@ -30031,7 +30783,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
30031
30783
  };
30032
30784
 
30033
30785
  // src/bridge/routing/handlers/revert-turn-snapshot.ts
30034
- import * as fs11 from "node:fs";
30786
+ import * as fs10 from "node:fs";
30035
30787
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
30036
30788
  const id = typeof msg.id === "string" ? msg.id : "";
30037
30789
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -30043,7 +30795,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
30043
30795
  if (!s) return;
30044
30796
  const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
30045
30797
  const file2 = snapshotFilePath(agentBase, turnId);
30046
- if (!fs11.existsSync(file2)) {
30798
+ if (!fs10.existsSync(file2)) {
30047
30799
  sendWsMessage(s, {
30048
30800
  type: "revert_turn_snapshot_result",
30049
30801
  id,
@@ -30144,25 +30896,12 @@ function dispatchBridgeMessage(msg, deps) {
30144
30896
  }
30145
30897
 
30146
30898
  // src/bridge/routing/handle-bridge-message.ts
30147
- var DEFERRED_INBOUND_TYPES = /* @__PURE__ */ new Set([
30148
- "server_control",
30149
- "prompt",
30150
- "install_skills",
30151
- "refresh_local_skills",
30152
- "dev_servers_config"
30153
- ]);
30154
30899
  function handleBridgeMessage(data, deps) {
30155
30900
  const msg = data;
30156
- const socket = deps.getWs();
30157
- if (!socket) return;
30158
- const type = msg.type;
30159
- if (typeof type === "string" && DEFERRED_INBOUND_TYPES.has(type)) {
30160
- setImmediate(() => {
30161
- dispatchBridgeMessage(msg, deps);
30162
- });
30163
- return;
30164
- }
30165
- dispatchBridgeMessage(msg, deps);
30901
+ if (!deps.getWs()) return;
30902
+ setImmediate(() => {
30903
+ dispatchBridgeMessage(msg, deps);
30904
+ });
30166
30905
  }
30167
30906
 
30168
30907
  // src/worktrees/session-worktree-manager.ts
@@ -30170,7 +30909,7 @@ import * as path20 from "node:path";
30170
30909
  import os4 from "node:os";
30171
30910
 
30172
30911
  // src/worktrees/prepare-new-session-worktrees.ts
30173
- import * as fs13 from "node:fs";
30912
+ import * as fs12 from "node:fs";
30174
30913
  import * as path18 from "node:path";
30175
30914
 
30176
30915
  // src/git/worktree-add.ts
@@ -30180,7 +30919,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
30180
30919
  }
30181
30920
 
30182
30921
  // src/worktrees/worktree-layout-file.ts
30183
- import * as fs12 from "node:fs";
30922
+ import * as fs11 from "node:fs";
30184
30923
  import * as path17 from "node:path";
30185
30924
  import os3 from "node:os";
30186
30925
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
@@ -30197,8 +30936,8 @@ function normalizeLoadedLayout(raw) {
30197
30936
  function loadWorktreeLayout() {
30198
30937
  try {
30199
30938
  const p = defaultWorktreeLayoutPath();
30200
- if (!fs12.existsSync(p)) return { launcherCwds: [] };
30201
- const raw = JSON.parse(fs12.readFileSync(p, "utf8"));
30939
+ if (!fs11.existsSync(p)) return { launcherCwds: [] };
30940
+ const raw = JSON.parse(fs11.readFileSync(p, "utf8"));
30202
30941
  return normalizeLoadedLayout(raw);
30203
30942
  } catch {
30204
30943
  return { launcherCwds: [] };
@@ -30207,8 +30946,8 @@ function loadWorktreeLayout() {
30207
30946
  function saveWorktreeLayout(layout) {
30208
30947
  try {
30209
30948
  const dir = path17.dirname(defaultWorktreeLayoutPath());
30210
- fs12.mkdirSync(dir, { recursive: true });
30211
- fs12.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
30949
+ fs11.mkdirSync(dir, { recursive: true });
30950
+ fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
30212
30951
  } catch {
30213
30952
  }
30214
30953
  }
@@ -30245,13 +30984,13 @@ async function prepareNewSessionWorktrees(options) {
30245
30984
  }
30246
30985
  const branch = `session-${sessionId}`;
30247
30986
  const worktreePaths = [];
30248
- fs13.mkdirSync(agentMirrorRoot, { recursive: true });
30987
+ fs12.mkdirSync(agentMirrorRoot, { recursive: true });
30249
30988
  for (const repo of repos) {
30250
30989
  let rel = path18.relative(launcherResolved, repo.absolutePath);
30251
30990
  if (rel.startsWith("..") || path18.isAbsolute(rel)) continue;
30252
30991
  const relNorm = rel === "" ? "." : rel;
30253
30992
  const wtPath = path18.join(agentMirrorRoot, relNorm, sessionId);
30254
- fs13.mkdirSync(path18.dirname(wtPath), { recursive: true });
30993
+ fs12.mkdirSync(path18.dirname(wtPath), { recursive: true });
30255
30994
  try {
30256
30995
  await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
30257
30996
  log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
@@ -30288,18 +31027,18 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
30288
31027
  }
30289
31028
 
30290
31029
  // src/worktrees/remove-session-worktrees.ts
30291
- import * as fs16 from "node:fs";
31030
+ import * as fs15 from "node:fs";
30292
31031
 
30293
31032
  // src/git/worktree-remove.ts
30294
- import * as fs15 from "node:fs";
31033
+ import * as fs14 from "node:fs";
30295
31034
 
30296
31035
  // src/git/resolve-main-repo-from-git-file.ts
30297
- import * as fs14 from "node:fs";
31036
+ import * as fs13 from "node:fs";
30298
31037
  import * as path19 from "node:path";
30299
31038
  function resolveMainRepoFromWorktreeGitFile(wt) {
30300
31039
  const gitDirFile = path19.join(wt, ".git");
30301
- if (!fs14.existsSync(gitDirFile) || !fs14.statSync(gitDirFile).isFile()) return "";
30302
- const first2 = fs14.readFileSync(gitDirFile, "utf8").trim();
31040
+ if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
31041
+ const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
30303
31042
  const m = first2.match(/^gitdir:\s*(.+)$/im);
30304
31043
  if (!m) return "";
30305
31044
  const gitWorktreePath = path19.resolve(wt, m[1].trim());
@@ -30313,7 +31052,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
30313
31052
  if (mainRepo) {
30314
31053
  await simpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
30315
31054
  } else {
30316
- fs15.rmSync(worktreePath, { recursive: true, force: true });
31055
+ fs14.rmSync(worktreePath, { recursive: true, force: true });
30317
31056
  }
30318
31057
  }
30319
31058
 
@@ -30326,7 +31065,7 @@ async function removeSessionWorktrees(paths, log2) {
30326
31065
  } catch (e) {
30327
31066
  log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
30328
31067
  try {
30329
- fs16.rmSync(wt, { recursive: true, force: true });
31068
+ fs15.rmSync(wt, { recursive: true, force: true });
30330
31069
  } catch {
30331
31070
  }
30332
31071
  }
@@ -30598,7 +31337,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
30598
31337
  }
30599
31338
 
30600
31339
  // src/dev-servers/process/wire-dev-server-child-process.ts
30601
- import fs17 from "node:fs";
31340
+ import fs16 from "node:fs";
30602
31341
 
30603
31342
  // src/dev-servers/manager/forward-pipe.ts
30604
31343
  function forwardChildPipe(childReadable, terminal, onData) {
@@ -30634,7 +31373,7 @@ function wireDevServerChildProcess(d) {
30634
31373
  d.setPollInterval(void 0);
30635
31374
  return;
30636
31375
  }
30637
- fs17.readFile(d.mergedLogPath, (err, buf) => {
31376
+ fs16.readFile(d.mergedLogPath, (err, buf) => {
30638
31377
  if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
30639
31378
  if (buf.length <= d.mergedReadPos.value) return;
30640
31379
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
@@ -30672,7 +31411,7 @@ ${errTail}` : ""}`);
30672
31411
  d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
30673
31412
  };
30674
31413
  if (mergedPath) {
30675
- fs17.readFile(mergedPath, (err, buf) => {
31414
+ fs16.readFile(mergedPath, (err, buf) => {
30676
31415
  if (!err && buf.length > d.mergedReadPos.value) {
30677
31416
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
30678
31417
  if (chunk.length > 0) {
@@ -30774,13 +31513,13 @@ function parseDevServerDefs(servers) {
30774
31513
  }
30775
31514
 
30776
31515
  // src/dev-servers/manager/shell-spawn/utils.ts
30777
- import fs18 from "node:fs";
31516
+ import fs17 from "node:fs";
30778
31517
  function isSpawnEbadf(e) {
30779
31518
  return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
30780
31519
  }
30781
31520
  function rmDirQuiet(dir) {
30782
31521
  try {
30783
- fs18.rmSync(dir, { recursive: true, force: true });
31522
+ fs17.rmSync(dir, { recursive: true, force: true });
30784
31523
  } catch {
30785
31524
  }
30786
31525
  }
@@ -30788,7 +31527,7 @@ var cachedDevNullReadFd;
30788
31527
  function devNullReadFd() {
30789
31528
  if (cachedDevNullReadFd === void 0) {
30790
31529
  const devPath = process.platform === "win32" ? "nul" : "/dev/null";
30791
- cachedDevNullReadFd = fs18.openSync(devPath, "r");
31530
+ cachedDevNullReadFd = fs17.openSync(devPath, "r");
30792
31531
  }
30793
31532
  return cachedDevNullReadFd;
30794
31533
  }
@@ -30862,15 +31601,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
30862
31601
 
30863
31602
  // src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
30864
31603
  import { spawn as spawn7 } from "node:child_process";
30865
- import fs19 from "node:fs";
31604
+ import fs18 from "node:fs";
30866
31605
  import { tmpdir } from "node:os";
30867
31606
  import path22 from "node:path";
30868
31607
  function trySpawnMergedLogFile(command, env, cwd, signal) {
30869
- const tmpRoot = fs19.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
31608
+ const tmpRoot = fs18.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
30870
31609
  const logPath = path22.join(tmpRoot, "combined.log");
30871
31610
  let logFd;
30872
31611
  try {
30873
- logFd = fs19.openSync(logPath, "a");
31612
+ logFd = fs18.openSync(logPath, "a");
30874
31613
  } catch {
30875
31614
  rmDirQuiet(tmpRoot);
30876
31615
  return null;
@@ -30889,7 +31628,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
30889
31628
  } else {
30890
31629
  proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
30891
31630
  }
30892
- fs19.closeSync(logFd);
31631
+ fs18.closeSync(logFd);
30893
31632
  return {
30894
31633
  proc,
30895
31634
  pipedStdoutStderr: true,
@@ -30898,7 +31637,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
30898
31637
  };
30899
31638
  } catch (e) {
30900
31639
  try {
30901
- fs19.closeSync(logFd);
31640
+ fs18.closeSync(logFd);
30902
31641
  } catch {
30903
31642
  }
30904
31643
  rmDirQuiet(tmpRoot);
@@ -30909,22 +31648,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
30909
31648
 
30910
31649
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
30911
31650
  import { spawn as spawn8 } from "node:child_process";
30912
- import fs20 from "node:fs";
31651
+ import fs19 from "node:fs";
30913
31652
  import { tmpdir as tmpdir2 } from "node:os";
30914
31653
  import path23 from "node:path";
30915
31654
  function shSingleQuote(s) {
30916
31655
  return `'${s.replace(/'/g, `'\\''`)}'`;
30917
31656
  }
30918
31657
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
30919
- const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
31658
+ const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
30920
31659
  const logPath = path23.join(tmpRoot, "combined.log");
30921
31660
  const innerPath = path23.join(tmpRoot, "_cmd.sh");
30922
31661
  const runnerPath = path23.join(tmpRoot, "_run.sh");
30923
31662
  try {
30924
- fs20.writeFileSync(innerPath, `#!/bin/sh
31663
+ fs19.writeFileSync(innerPath, `#!/bin/sh
30925
31664
  ${command}
30926
31665
  `);
30927
- fs20.writeFileSync(
31666
+ fs19.writeFileSync(
30928
31667
  runnerPath,
30929
31668
  `#!/bin/sh
30930
31669
  cd ${shSingleQuote(cwd)}
@@ -30950,13 +31689,13 @@ cd ${shSingleQuote(cwd)}
30950
31689
  }
30951
31690
  }
30952
31691
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
30953
- const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
31692
+ const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
30954
31693
  const logPath = path23.join(tmpRoot, "combined.log");
30955
31694
  const runnerPath = path23.join(tmpRoot, "_run.bat");
30956
31695
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
30957
31696
  const com = process.env.ComSpec || "cmd.exe";
30958
31697
  try {
30959
- fs20.writeFileSync(
31698
+ fs19.writeFileSync(
30960
31699
  runnerPath,
30961
31700
  `@ECHO OFF\r
30962
31701
  CD /D ${q(cwd)}\r
@@ -31310,7 +32049,9 @@ var DevServerManager = class {
31310
32049
  async shutdownAllGraceful() {
31311
32050
  const pairs = [...this.processes.entries()];
31312
32051
  if (pairs.length === 0) return;
31313
- this.log(`[dev-server] Stopping ${pairs.length} dev server process(es) (bridge shutting down)\u2026`);
32052
+ this.log(
32053
+ `[dev-server] Stopping ${pairs.length} local dev server process${pairs.length === 1 ? "" : "es"}\u2026`
32054
+ );
31314
32055
  await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc)));
31315
32056
  }
31316
32057
  async gracefulTerminateOrUnknown(serverId, proc) {
@@ -31477,9 +32218,10 @@ var FIREHOSE_CLIENT_PING_MS = 25e3;
31477
32218
  function connectFirehose(options) {
31478
32219
  const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
31479
32220
  const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
31480
- const wsOptions = { perMessageDeflate: false };
32221
+ applyCliOutboundNetworkPreferences();
32222
+ const wsOptions = { perMessageDeflate: false, family: 4 };
31481
32223
  if (wsUrl.startsWith("wss://")) {
31482
- wsOptions.agent = new https2.Agent({ rejectUnauthorized: false });
32224
+ wsOptions.agent = new https2.Agent({ rejectUnauthorized: false, family: 4 });
31483
32225
  }
31484
32226
  const ws = new wrapper_default(wsUrl, wsOptions);
31485
32227
  let clientPingTimer = null;
@@ -31515,16 +32257,14 @@ function connectFirehose(options) {
31515
32257
  sendWsMessage(ws, { type: "identify", workspaceId, bridgeName, proxyPorts });
31516
32258
  });
31517
32259
  ws.on("message", (raw) => {
31518
- setImmediate(() => {
31519
- if (Buffer.isBuffer(raw) && tryConsumeBinaryProxyBody(raw, deps)) {
31520
- return;
31521
- }
31522
- try {
31523
- const text = Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw);
31524
- dispatchFirehoseJsonMessage(JSON.parse(text), deps);
31525
- } catch {
31526
- }
31527
- });
32260
+ if (Buffer.isBuffer(raw) && tryConsumeBinaryProxyBody(raw, deps)) {
32261
+ return;
32262
+ }
32263
+ try {
32264
+ const text = Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw);
32265
+ dispatchFirehoseJsonMessage(JSON.parse(text), deps);
32266
+ } catch {
32267
+ }
31528
32268
  });
31529
32269
  ws.on("close", (code, reason) => {
31530
32270
  clearClientPing();
@@ -31570,6 +32310,9 @@ function createOnBridgeIdentified(opts) {
31570
32310
  function attachFirehose(params) {
31571
32311
  state.lastFirehoseParams = params;
31572
32312
  clearFirehoseReconnectTimer();
32313
+ if (state.firehoseReconnectAttempt === 0) {
32314
+ logFn("Connecting to preview tunnel (local HTTP proxy and dev logs)\u2026");
32315
+ }
31573
32316
  state.firehoseGeneration += 1;
31574
32317
  const myGen = state.firehoseGeneration;
31575
32318
  if (state.firehoseHandle) {
@@ -31588,8 +32331,8 @@ function createOnBridgeIdentified(opts) {
31588
32331
  clearFirehoseReconnectQuietOnOpen({ firehoseQuiet: state.firehoseQuiet }, logFn);
31589
32332
  const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
31590
32333
  state.firehoseReconnectAttempt = 0;
31591
- if (logOpenAsFirehoseReconnect) {
31592
- logFn("[Proxy and log service] reconnected.");
32334
+ if (!logOpenAsFirehoseReconnect) {
32335
+ logFn("Connected to preview tunnel (local HTTP proxy and dev logs).");
31593
32336
  }
31594
32337
  },
31595
32338
  onClose: (code, reason) => {
@@ -31657,47 +32400,19 @@ function createOnBridgeIdentified(opts) {
31657
32400
  };
31658
32401
  }
31659
32402
 
31660
- // src/dev-servers/detect-local-agent-types.ts
31661
- import { execFile as execFile4 } from "node:child_process";
31662
- import { promisify as promisify4 } from "node:util";
31663
- var execFileAsync4 = promisify4(execFile4);
31664
- var CHECKS = {
31665
- "cursor-cli": async () => {
31666
- try {
31667
- await execFileAsync4("which", ["agent"], { timeout: 4e3 });
31668
- return true;
31669
- } catch {
31670
- return false;
31671
- }
31672
- },
31673
- "codex-acp": async () => {
31674
- try {
31675
- await execFileAsync4("which", ["codex"], { timeout: 4e3 });
31676
- return true;
31677
- } catch {
31678
- return false;
31679
- }
31680
- },
31681
- "claude-code": async () => {
31682
- try {
31683
- await execFileAsync4("which", ["claude"], { timeout: 4e3 });
31684
- return true;
31685
- } catch {
31686
- try {
31687
- await execFileAsync4("npx", ["--yes", "@anthropic-ai/claude-code", "--version"], { timeout: 25e3 });
31688
- return true;
31689
- } catch {
31690
- return false;
31691
- }
31692
- }
31693
- }
31694
- };
32403
+ // src/acp/detect-local-agent-types.ts
32404
+ var LOCAL_AGENT_ACP_MODULES = [
32405
+ cursor_acp_client_exports,
32406
+ codex_acp_client_exports,
32407
+ kiro_acp_client_exports,
32408
+ claude_code_acp_client_exports
32409
+ ];
31695
32410
  async function detectLocalAgentTypes() {
31696
32411
  try {
31697
32412
  const out = [];
31698
- for (const [type, check2] of Object.entries(CHECKS)) {
32413
+ for (const mod of LOCAL_AGENT_ACP_MODULES) {
31699
32414
  try {
31700
- if (await check2()) out.push(type);
32415
+ if (await mod.detectLocalAgentPresence()) out.push(mod.BACKEND_LOCAL_AGENT_TYPE);
31701
32416
  } catch {
31702
32417
  }
31703
32418
  }
@@ -31787,8 +32502,8 @@ async function createBridgeConnection(options) {
31787
32502
  clearMainBridgeReconnectQuietOnOpen(state, logFn);
31788
32503
  state.reconnectAttempt = 0;
31789
32504
  state.logBridgeOpenAsReconnect = false;
31790
- if (logOpenAsPostRefreshReconnect) {
31791
- logFn("[Bridge service] reconnected.");
32505
+ if (!logOpenAsPostRefreshReconnect) {
32506
+ logFn("Connected to bridge service.");
31792
32507
  }
31793
32508
  const socket = getWs();
31794
32509
  if (socket) {
@@ -31827,6 +32542,9 @@ async function createBridgeConnection(options) {
31827
32542
  clearTimeout(state.reconnectTimeout);
31828
32543
  state.reconnectTimeout = null;
31829
32544
  }
32545
+ if (state.reconnectAttempt === 0) {
32546
+ logFn("Connecting to bridge service\u2026");
32547
+ }
31830
32548
  const prev = state.currentWs;
31831
32549
  if (prev) {
31832
32550
  prev.removeAllListeners();
@@ -31899,15 +32617,17 @@ async function runBridge(options) {
31899
32617
  onAuth: (_auth) => {
31900
32618
  }
31901
32619
  });
31902
- const onSignal2 = (signal) => {
31903
- logImmediate(`Received ${signal}; shutting down\u2026`);
32620
+ const onSignal2 = (kind) => {
32621
+ logImmediate(
32622
+ kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
32623
+ );
31904
32624
  setImmediate(() => {
31905
32625
  handle2.close();
31906
32626
  process.exit(0);
31907
32627
  });
31908
32628
  };
31909
- const onSigInt2 = () => onSignal2("SIGINT");
31910
- const onSigTerm2 = () => onSignal2("SIGTERM");
32629
+ const onSigInt2 = () => onSignal2("interrupt");
32630
+ const onSigTerm2 = () => onSignal2("stop");
31911
32631
  process.on("SIGINT", onSigInt2);
31912
32632
  process.on("SIGTERM", onSigTerm2);
31913
32633
  const auth = await handle2.authPromise;
@@ -31956,22 +32676,24 @@ async function runBridge(options) {
31956
32676
  });
31957
32677
  }
31958
32678
  });
31959
- const onSignal = (signal) => {
31960
- logImmediate(`Received ${signal}; shutting down\u2026`);
32679
+ const onSignal = (kind) => {
32680
+ logImmediate(
32681
+ kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
32682
+ );
31961
32683
  setImmediate(() => {
31962
32684
  void handle.close().then(() => {
31963
32685
  process.exit(0);
31964
32686
  });
31965
32687
  });
31966
32688
  };
31967
- const onSigInt = () => onSignal("SIGINT");
31968
- const onSigTerm = () => onSignal("SIGTERM");
32689
+ const onSigInt = () => onSignal("interrupt");
32690
+ const onSigTerm = () => onSignal("stop");
31969
32691
  process.on("SIGINT", onSigInt);
31970
32692
  process.on("SIGTERM", onSigTerm);
31971
32693
  }
31972
32694
  export {
31973
32695
  callSkill,
31974
- createAcpClient,
32696
+ createSdkStdioAcpClient as createAcpClient,
31975
32697
  createBridgeConnection,
31976
32698
  createWsBridge,
31977
32699
  getSkill,