@buildautomaton/cli 0.1.90 → 0.1.92

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
@@ -20762,6 +20762,7 @@ import os from "node:os";
20762
20762
  var INDEX_WORK_YIELD_EVERY, INDEX_DIR, INDEX_HASH_LEN;
20763
20763
  var init_constants = __esm({
20764
20764
  "../bridge/src/files/index/constants.ts"() {
20765
+ "use strict";
20765
20766
  INDEX_WORK_YIELD_EVERY = 128;
20766
20767
  INDEX_DIR = path3.join(os.homedir(), ".buildautomaton");
20767
20768
  INDEX_HASH_LEN = 16;
@@ -21108,6 +21109,7 @@ var init_idle_yield = __esm({
21108
21109
  // ../bridge/src/files/browser/in-flight.ts
21109
21110
  var init_in_flight = __esm({
21110
21111
  "../bridge/src/files/browser/in-flight.ts"() {
21112
+ "use strict";
21111
21113
  init_activity_shared();
21112
21114
  init_activity_types();
21113
21115
  init_activity_tracker();
@@ -21922,7 +21924,6 @@ function withCodeNavCacheSqliteWorkLock(fn) {
21922
21924
  var chain, workLockDepth;
21923
21925
  var init_code_nav_cache_sqlite_work_lock = __esm({
21924
21926
  "../bridge/src/sqlite/code-nav-cache/code-nav-cache-sqlite-work-lock.ts"() {
21925
- "use strict";
21926
21927
  init_code_nav_cache_paths();
21927
21928
  chain = Promise.resolve();
21928
21929
  workLockDepth = 0;
@@ -27577,6 +27578,7 @@ function createWsBridge(options) {
27577
27578
  });
27578
27579
  ws.on("open", () => {
27579
27580
  disposeClientPing();
27581
+ if (isActiveSocket && !isActiveSocket(ws)) return;
27580
27582
  if (clientPingIntervalMs != null && clientPingIntervalMs > 0) {
27581
27583
  clearClientPing = attachWebSocketClientPing(ws, clientPingIntervalMs);
27582
27584
  }
@@ -27596,10 +27598,12 @@ function createWsBridge(options) {
27596
27598
  });
27597
27599
  ws.on("close", (code, reason) => {
27598
27600
  disposeClientPing();
27599
- onClose?.(code, reason.toString());
27601
+ if (isActiveSocket && !isActiveSocket(ws)) return;
27602
+ onClose?.(code, reason.toString(), ws);
27600
27603
  });
27601
27604
  ws.on("error", (err) => {
27602
27605
  disposeClientPing();
27606
+ if (isActiveSocket && !isActiveSocket(ws)) return;
27603
27607
  onError2?.(err);
27604
27608
  });
27605
27609
  return ws;
@@ -27704,7 +27708,7 @@ function formatSpawnError(err, command) {
27704
27708
  }
27705
27709
 
27706
27710
  // ../bridge/src/agents/acp/clients/kill-process-tree.ts
27707
- var import_tree_kill = __toESM(require_tree_kill());
27711
+ var import_tree_kill = __toESM(require_tree_kill(), 1);
27708
27712
  import { promisify } from "node:util";
27709
27713
  var treeKillAsync = promisify(import_tree_kill.default);
27710
27714
  var ACP_PROCESS_TREE_KILL_GRACE_MS = 2500;
@@ -27773,29 +27777,50 @@ function forceAcpSubprocessDisconnect(child) {
27773
27777
  killChildProcessTree(child, "SIGKILL");
27774
27778
  }
27775
27779
 
27776
- // ../bridge/src/agents/local-agent-auth.ts
27777
- var LOCAL_AGENT_AUTH_ERROR_HINTS = {
27778
- "kiro-acp": [/not logged in/i, /kiro-cli\s+login/i, /log in with kiro-cli/i],
27779
- "cursor-cli": [/cursor_login/i, /authenticate.*cursor/i, /not logged in.*cursor/i, /run:\s*agent\s+login/i],
27780
- "codex-acp": [
27781
- /authentication failed/i,
27782
- /not authenticated/i,
27783
- /invalid.*api key/i,
27784
- /sign in.*openai/i,
27785
- /login.*openai/i,
27786
- /unauthorized/i
27787
- ],
27788
- "claude-code": [
27789
- /ANTHROPIC_API_KEY/i,
27790
- /not authenticated/i,
27791
- /authentication failed/i,
27792
- /claude\s+login/i,
27793
- /please run.*claude.*login/i
27794
- ]
27780
+ // ../bridge/src/agents/providers/claude-code/auth.ts
27781
+ var claudeCodeAuthErrorHints = [
27782
+ /ANTHROPIC_API_KEY/i,
27783
+ /not authenticated/i,
27784
+ /authentication failed/i,
27785
+ /claude\s+login/i,
27786
+ /please run.*claude.*login/i
27787
+ ];
27788
+
27789
+ // ../bridge/src/agents/providers/codex/auth.ts
27790
+ var codexAuthErrorHints = [
27791
+ /authentication failed/i,
27792
+ /not authenticated/i,
27793
+ /invalid.*api key/i,
27794
+ /sign in.*openai/i,
27795
+ /login.*openai/i,
27796
+ /unauthorized/i
27797
+ ];
27798
+
27799
+ // ../bridge/src/agents/providers/cursor/auth.ts
27800
+ var cursorAuthErrorHints = [
27801
+ /cursor_login/i,
27802
+ /authenticate.*cursor/i,
27803
+ /not logged in.*cursor/i,
27804
+ /run:\s*agent\s+login/i
27805
+ ];
27806
+
27807
+ // ../bridge/src/agents/providers/kiro/auth.ts
27808
+ var kiroAuthErrorHints = [
27809
+ /not logged in/i,
27810
+ /kiro-cli\s+login/i,
27811
+ /log in with kiro-cli/i
27812
+ ];
27813
+
27814
+ // ../bridge/src/agents/providers/auth.ts
27815
+ var AUTH_ERROR_HINTS = {
27816
+ "claude-code": claudeCodeAuthErrorHints,
27817
+ "codex-acp": codexAuthErrorHints,
27818
+ "cursor-cli": cursorAuthErrorHints,
27819
+ "kiro-acp": kiroAuthErrorHints
27795
27820
  };
27796
27821
  function localAgentErrorSuggestsAuth(agentType, errorText) {
27797
27822
  if (agentType == null || agentType === "" || errorText == null || !String(errorText).trim()) return false;
27798
- const hints = LOCAL_AGENT_AUTH_ERROR_HINTS[agentType];
27823
+ const hints = AUTH_ERROR_HINTS[agentType];
27799
27824
  if (!hints?.length) return false;
27800
27825
  return hints.some((re) => re.test(String(errorText)));
27801
27826
  }
@@ -32988,34 +33013,11 @@ function flattenSdkSessionNotificationParams(params) {
32988
33013
  return { sessionId: params.sessionId, ...params.update };
32989
33014
  }
32990
33015
 
32991
- // ../bridge/src/agents/acp/clients/kiro-sdk-ext-notifications.ts
32992
- function createKiroSdkExtNotificationHandler(options) {
32993
- const { onSessionUpdate } = options;
32994
- return async (method, params) => {
32995
- if (method === "_kiro.dev/metadata") {
32996
- const p = params && typeof params === "object" ? params : {};
32997
- const pct = p.contextUsagePercentage;
32998
- if (typeof pct !== "number" || !Number.isFinite(pct) || !onSessionUpdate) return;
32999
- onSessionUpdate({
33000
- sessionUpdate: "context_usage",
33001
- contextUsagePercentage: pct
33002
- });
33003
- return;
33004
- }
33005
- };
33006
- }
33007
-
33008
33016
  // ../bridge/src/agents/acp/clients/sdk/sdk-stdio-ext-notifications.ts
33009
33017
  var noopExtNotification = async () => {
33010
33018
  };
33011
- function createSdkStdioExtNotificationHandler(options) {
33012
- const { backendAgentType, onSessionUpdate } = options;
33013
- switch (backendAgentType) {
33014
- case "kiro-acp":
33015
- return createKiroSdkExtNotificationHandler({ onSessionUpdate });
33016
- default:
33017
- return noopExtNotification;
33018
- }
33019
+ function createSdkStdioExtNotificationHandler(_options) {
33020
+ return noopExtNotification;
33019
33021
  }
33020
33022
 
33021
33023
  // ../bridge/src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
@@ -33049,11 +33051,8 @@ function resolvePendingSdkStdioPermissionCancellations(pending2) {
33049
33051
 
33050
33052
  // ../bridge/src/agents/acp/clients/sdk/sdk-stdio-connection-client.ts
33051
33053
  function createSdkStdioConnectionClient(deps) {
33052
- const { backendAgentType, onSessionUpdate, onRequest, sessionCtx, pendingPermissionReplies } = deps;
33053
- const extNotification = createSdkStdioExtNotificationHandler({
33054
- backendAgentType,
33055
- onSessionUpdate
33056
- });
33054
+ const { onSessionUpdate, onRequest, sessionCtx, pendingPermissionReplies, createExtNotificationHandler } = deps;
33055
+ const extNotification = createExtNotificationHandler?.({ onSessionUpdate }) ?? createSdkStdioExtNotificationHandler({ onSessionUpdate });
33057
33056
  let permissionSeq = 0;
33058
33057
  return (_agent) => ({
33059
33058
  async requestPermission(params) {
@@ -33165,121 +33164,16 @@ function createSdkStdioSessionContext(options) {
33165
33164
  onAcpSessionEstablished: options.onAcpSessionEstablished,
33166
33165
  onAcpConfigOptionsUpdated: options.onAcpConfigOptionsUpdated,
33167
33166
  logDebug,
33168
- getStderrText: () => options.stderrCapture.getText()
33167
+ getStderrText: () => options.stderrCapture.getText(),
33168
+ afterSessionEstablished: options.afterSessionEstablished
33169
33169
  };
33170
33170
  }
33171
33171
 
33172
33172
  // ../bridge/src/agents/acp/clients/sdk/sdk-stdio-bootstrap-connection.ts
33173
33173
  import { Readable, Writable } from "node:stream";
33174
33174
 
33175
- // ../bridge/src/agents/acp/claude-acp-permission-from-session.ts
33176
- function flattenSelectOptions(options) {
33177
- if (options == null || options.length === 0) return [];
33178
- const first2 = options[0];
33179
- if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
33180
- return options.flatMap(
33181
- (g) => Array.isArray(g.options) ? g.options : []
33182
- );
33183
- }
33184
- return options;
33185
- }
33186
- function pickModeConfigOption(configOptions) {
33187
- if (configOptions == null || configOptions.length === 0) return null;
33188
- const byCategory = configOptions.find((o) => o.category === "mode");
33189
- if (byCategory) return byCategory;
33190
- return configOptions.find((o) => o.id === "mode") ?? null;
33191
- }
33192
- async function applyClaudePermissionFromAcpSession(params) {
33193
- const { sessionId, agentConfig, configOptions, modes, setSessionConfigOption, setSessionMode, logDebug: logDebug2 } = params;
33194
- const desiredMode = getClaudePermissionModeFromAgentConfig(agentConfig);
33195
- if (desiredMode == null) return;
33196
- const modeOpt = pickModeConfigOption(configOptions ?? null);
33197
- if (modeOpt != null) {
33198
- const flat = flattenSelectOptions(modeOpt.options);
33199
- const allowed = flat.some((o) => o.value === desiredMode);
33200
- if (allowed && modeOpt.currentValue !== desiredMode) {
33201
- try {
33202
- logDebug2(
33203
- `[Agent] Claude Code: sending ACP session/set_config_option (permission mode) configId=${JSON.stringify(modeOpt.id)} value=${JSON.stringify(desiredMode)} was=${JSON.stringify(modeOpt.currentValue)} sessionId=${sessionId.slice(0, 8)}\u2026`
33204
- );
33205
- await setSessionConfigOption({ sessionId, configId: modeOpt.id, value: desiredMode });
33206
- } catch (e) {
33207
- logDebug2(
33208
- `[Agent] Claude Code: session/set_config_option failed: ${e instanceof Error ? e.message : String(e)}`
33209
- );
33210
- }
33211
- }
33212
- return;
33213
- }
33214
- if (modes?.availableModes?.length) {
33215
- const allowed = modes.availableModes.some((m) => m.id === desiredMode);
33216
- if (allowed && desiredMode !== modes.currentModeId) {
33217
- try {
33218
- logDebug2(
33219
- `[Agent] Claude Code: sending ACP session/set_mode (permission mode) modeId=${JSON.stringify(desiredMode)} was=${JSON.stringify(modes.currentModeId ?? null)} sessionId=${sessionId.slice(0, 8)}\u2026`
33220
- );
33221
- await setSessionMode({ sessionId, modeId: desiredMode });
33222
- } catch (e) {
33223
- logDebug2(`[Agent] Claude Code: session/set_mode failed: ${e instanceof Error ? e.message : String(e)}`);
33224
- }
33225
- }
33226
- }
33227
- }
33228
-
33229
- // ../bridge/src/agents/acp/codex-acp-permission-from-session.ts
33230
- function flattenSelectOptions2(options) {
33231
- if (options == null || options.length === 0) return [];
33232
- const first2 = options[0];
33233
- if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
33234
- return options.flatMap(
33235
- (g) => Array.isArray(g.options) ? g.options : []
33236
- );
33237
- }
33238
- return options;
33239
- }
33240
- function pickModeConfigOption2(configOptions) {
33241
- if (configOptions == null || configOptions.length === 0) return null;
33242
- const byCategory = configOptions.find((o) => o.category === "mode");
33243
- if (byCategory) return byCategory;
33244
- return configOptions.find((o) => o.id === "mode") ?? null;
33245
- }
33246
- async function applyCodexPermissionFromAcpSession(params) {
33247
- const { sessionId, agentConfig, configOptions, modes, setSessionConfigOption, setSessionMode, logDebug: logDebug2 } = params;
33248
- const desiredMode = getCodexPermissionModeFromAgentConfig(agentConfig);
33249
- if (desiredMode == null) return;
33250
- const modeOpt = pickModeConfigOption2(configOptions ?? null);
33251
- if (modeOpt != null) {
33252
- const flat = flattenSelectOptions2(modeOpt.options);
33253
- const allowed = flat.some((o) => o.value === desiredMode);
33254
- if (allowed && modeOpt.currentValue !== desiredMode) {
33255
- try {
33256
- logDebug2(
33257
- `[Agent] Codex: sending ACP session/set_config_option (mode) configId=${JSON.stringify(modeOpt.id)} value=${JSON.stringify(desiredMode)} was=${JSON.stringify(modeOpt.currentValue)} sessionId=${sessionId.slice(0, 8)}\u2026`
33258
- );
33259
- await setSessionConfigOption({ sessionId, configId: modeOpt.id, value: desiredMode });
33260
- } catch (e) {
33261
- logDebug2(`[Agent] Codex: session/set_config_option failed: ${e instanceof Error ? e.message : String(e)}`);
33262
- }
33263
- }
33264
- return;
33265
- }
33266
- if (modes?.availableModes?.length) {
33267
- const allowed = modes.availableModes.some((m) => m.id === desiredMode);
33268
- if (allowed && desiredMode !== modes.currentModeId) {
33269
- try {
33270
- logDebug2(
33271
- `[Agent] Codex: sending ACP session/set_mode modeId=${JSON.stringify(desiredMode)} was=${JSON.stringify(modes.currentModeId ?? null)} sessionId=${sessionId.slice(0, 8)}\u2026`
33272
- );
33273
- await setSessionMode({ sessionId, modeId: desiredMode });
33274
- } catch (e) {
33275
- logDebug2(`[Agent] Codex: session/set_mode failed: ${e instanceof Error ? e.message : String(e)}`);
33276
- }
33277
- }
33278
- }
33279
- }
33280
-
33281
33175
  // ../bridge/src/agents/acp/apply-acp-model-from-agent-session.ts
33282
- function flattenSelectOptions3(options) {
33176
+ function flattenSelectOptions(options) {
33283
33177
  if (options == null || options.length === 0) return [];
33284
33178
  const first2 = options[0];
33285
33179
  if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
@@ -33304,7 +33198,7 @@ async function applyAcpModelFromAcpSession(params) {
33304
33198
  if (desired == null) return;
33305
33199
  const modelOpt = pickModelConfigOption(configOptions ?? null);
33306
33200
  if (modelOpt == null) return;
33307
- const flat = flattenSelectOptions3(modelOpt.options);
33201
+ const flat = flattenSelectOptions(modelOpt.options);
33308
33202
  const allowed = flat.some((o) => o.value === desired);
33309
33203
  if (!allowed) return;
33310
33204
  if (modelOpt.currentValue === desired) return;
@@ -33426,38 +33320,13 @@ async function bootstrapAcpWireSession(transport, ctx, initializeRequest) {
33426
33320
  configOptions: established.configOptions,
33427
33321
  modes: established.modes
33428
33322
  });
33429
- if (ctx.backendAgentType === "claude-code") {
33430
- const cfg = ctx.agentConfig != null && typeof ctx.agentConfig === "object" && !Array.isArray(ctx.agentConfig) ? ctx.agentConfig : null;
33431
- const configOptionsTyped = established.configOptions;
33432
- const modesTyped = established.modes;
33433
- await applyClaudePermissionFromAcpSession({
33434
- sessionId,
33435
- agentConfig: cfg,
33436
- configOptions: configOptionsForPermission(ctx.getActiveConfigOptions, configOptionsTyped),
33437
- modes: modesTyped,
33438
- setSessionConfigOption: transport.setSessionConfigOption ? (p) => transport.setSessionConfigOption(p) : async () => {
33439
- },
33440
- setSessionMode: transport.setSessionMode ? (p) => transport.setSessionMode(p) : async () => {
33441
- },
33442
- logDebug: ctx.logDebug
33443
- });
33444
- }
33445
- if (ctx.backendAgentType === "codex-acp") {
33446
- const cfg = ctx.agentConfig != null && typeof ctx.agentConfig === "object" && !Array.isArray(ctx.agentConfig) ? ctx.agentConfig : null;
33447
- const configOptionsTyped = established.configOptions;
33448
- const modesTyped = established.modes;
33449
- await applyCodexPermissionFromAcpSession({
33450
- sessionId,
33451
- agentConfig: cfg,
33452
- configOptions: configOptionsForPermission(ctx.getActiveConfigOptions, configOptionsTyped),
33453
- modes: modesTyped,
33454
- setSessionConfigOption: transport.setSessionConfigOption ? (p) => transport.setSessionConfigOption(p) : async () => {
33455
- },
33456
- setSessionMode: transport.setSessionMode ? (p) => transport.setSessionMode(p) : async () => {
33457
- },
33458
- logDebug: ctx.logDebug
33459
- });
33460
- }
33323
+ await ctx.afterSessionEstablished?.({
33324
+ sessionId,
33325
+ transport,
33326
+ ctx,
33327
+ configOptions: established.configOptions,
33328
+ modes: established.modes
33329
+ });
33461
33330
  const cfgAll = ctx.agentConfig != null && typeof ctx.agentConfig === "object" && !Array.isArray(ctx.agentConfig) ? ctx.agentConfig : null;
33462
33331
  const configOptionsForModel = established.configOptions;
33463
33332
  if (transport.setSessionConfigOption) {
@@ -33510,7 +33379,8 @@ async function bootstrapSdkStdioConnection(options) {
33510
33379
  onSessionUpdate: options.onSessionUpdate,
33511
33380
  onRequest: options.onRequest,
33512
33381
  sessionCtx: options.sessionCtx,
33513
- pendingPermissionReplies: options.pendingPermissionReplies
33382
+ pendingPermissionReplies: options.pendingPermissionReplies,
33383
+ createExtNotificationHandler: options.createExtNotificationHandler
33514
33384
  });
33515
33385
  const connection = new ClientSideConnection2(client, stream);
33516
33386
  connection.signal.addEventListener("abort", () => {
@@ -33610,7 +33480,9 @@ async function createSdkStdioAcpClient(options) {
33610
33480
  persistedAcpSessionId,
33611
33481
  onAcpSessionEstablished,
33612
33482
  onAcpConfigOptionsUpdated,
33613
- getActiveConfigOptions
33483
+ getActiveConfigOptions,
33484
+ afterSessionEstablished,
33485
+ createExtNotificationHandler
33614
33486
  } = options;
33615
33487
  const { child, stderrCapture } = spawnSdkStdioProcess({
33616
33488
  command,
@@ -33627,6 +33499,7 @@ async function createSdkStdioAcpClient(options) {
33627
33499
  onAcpSessionEstablished,
33628
33500
  onAcpConfigOptionsUpdated,
33629
33501
  onFileChange,
33502
+ afterSessionEstablished,
33630
33503
  stderrCapture
33631
33504
  });
33632
33505
  return new Promise((resolve36, reject) => {
@@ -33651,7 +33524,8 @@ async function createSdkStdioAcpClient(options) {
33651
33524
  onSessionUpdate,
33652
33525
  onRequest,
33653
33526
  pendingPermissionReplies,
33654
- protocolVersion: PROTOCOL_VERSION2
33527
+ protocolVersion: PROTOCOL_VERSION2,
33528
+ createExtNotificationHandler
33655
33529
  });
33656
33530
  init.settleResolve(
33657
33531
  resolve36,
@@ -35311,7 +35185,7 @@ async function cancelRun(ctx, runId) {
35311
35185
  }
35312
35186
 
35313
35187
  // ../bridge/src/agents/acp/ensure-acp-client.ts
35314
- import * as fs18 from "node:fs";
35188
+ import * as fs19 from "node:fs";
35315
35189
  import * as path29 from "node:path";
35316
35190
 
35317
35191
  // ../bridge/src/paths/session-layout-paths.ts
@@ -35339,14 +35213,86 @@ function errorMessage(err) {
35339
35213
  return String(err);
35340
35214
  }
35341
35215
 
35342
- // ../bridge/src/agents/acp/clients/claude-code-acp-client.ts
35343
- var claude_code_acp_client_exports = {};
35344
- __export(claude_code_acp_client_exports, {
35345
- BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE,
35346
- buildClaudeCodeAcpSpawnCommand: () => buildClaudeCodeAcpSpawnCommand,
35347
- createClaudeCodeAcpClient: () => createClaudeCodeAcpClient,
35348
- detectLocalAgentPresence: () => detectLocalAgentPresence
35349
- });
35216
+ // ../bridge/src/agents/providers/claude-code/apply-permission.ts
35217
+ function flattenSelectOptions2(options) {
35218
+ if (options == null || options.length === 0) return [];
35219
+ const first2 = options[0];
35220
+ if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
35221
+ return options.flatMap(
35222
+ (g) => Array.isArray(g.options) ? g.options : []
35223
+ );
35224
+ }
35225
+ return options;
35226
+ }
35227
+ function pickModeConfigOption(configOptions) {
35228
+ if (configOptions == null || configOptions.length === 0) return null;
35229
+ const byCategory = configOptions.find((o) => o.category === "mode");
35230
+ if (byCategory) return byCategory;
35231
+ return configOptions.find((o) => o.id === "mode") ?? null;
35232
+ }
35233
+ async function applyClaudePermissionFromAcpSession(params) {
35234
+ const { sessionId, agentConfig, configOptions, modes, setSessionConfigOption, setSessionMode, logDebug: logDebug2 } = params;
35235
+ const desiredMode = getClaudePermissionModeFromAgentConfig(agentConfig);
35236
+ if (desiredMode == null) return;
35237
+ const modeOpt = pickModeConfigOption(configOptions ?? null);
35238
+ if (modeOpt != null) {
35239
+ const flat = flattenSelectOptions2(modeOpt.options);
35240
+ const allowed = flat.some((o) => o.value === desiredMode);
35241
+ if (allowed && modeOpt.currentValue !== desiredMode) {
35242
+ try {
35243
+ logDebug2(
35244
+ `[Agent] Claude Code: sending ACP session/set_config_option (permission mode) configId=${JSON.stringify(modeOpt.id)} value=${JSON.stringify(desiredMode)} was=${JSON.stringify(modeOpt.currentValue)} sessionId=${sessionId.slice(0, 8)}\u2026`
35245
+ );
35246
+ await setSessionConfigOption({ sessionId, configId: modeOpt.id, value: desiredMode });
35247
+ } catch (e) {
35248
+ logDebug2(
35249
+ `[Agent] Claude Code: session/set_config_option failed: ${e instanceof Error ? e.message : String(e)}`
35250
+ );
35251
+ }
35252
+ }
35253
+ return;
35254
+ }
35255
+ if (modes?.availableModes?.length) {
35256
+ const allowed = modes.availableModes.some((m) => m.id === desiredMode);
35257
+ if (allowed && desiredMode !== modes.currentModeId) {
35258
+ try {
35259
+ logDebug2(
35260
+ `[Agent] Claude Code: sending ACP session/set_mode (permission mode) modeId=${JSON.stringify(desiredMode)} was=${JSON.stringify(modes.currentModeId ?? null)} sessionId=${sessionId.slice(0, 8)}\u2026`
35261
+ );
35262
+ await setSessionMode({ sessionId, modeId: desiredMode });
35263
+ } catch (e) {
35264
+ logDebug2(`[Agent] Claude Code: session/set_mode failed: ${e instanceof Error ? e.message : String(e)}`);
35265
+ }
35266
+ }
35267
+ }
35268
+ }
35269
+
35270
+ // ../bridge/src/agents/providers/shared/wrap-permission-after-session.ts
35271
+ function wrapPermissionAfterSession(apply) {
35272
+ return async ({ sessionId, transport, ctx, configOptions, modes }) => {
35273
+ const raw = ctx.agentConfig;
35274
+ const agentConfig = raw != null && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
35275
+ await apply({
35276
+ sessionId,
35277
+ agentConfig,
35278
+ configOptions: configOptionsForPermission(
35279
+ ctx.getActiveConfigOptions,
35280
+ configOptions
35281
+ ),
35282
+ modes,
35283
+ setSessionConfigOption: transport.setSessionConfigOption ? (p) => transport.setSessionConfigOption(p) : async () => {
35284
+ },
35285
+ setSessionMode: transport.setSessionMode ? (p) => transport.setSessionMode(p) : async () => {
35286
+ },
35287
+ logDebug: ctx.logDebug
35288
+ });
35289
+ };
35290
+ }
35291
+
35292
+ // ../bridge/src/agents/providers/claude-code/after-session.ts
35293
+ var applyClaudeCodeAfterSessionEstablished = wrapPermissionAfterSession(
35294
+ applyClaudePermissionFromAcpSession
35295
+ );
35350
35296
 
35351
35297
  // ../bridge/src/agents/acp/clients/detect-command-on-path.ts
35352
35298
  init_cli_process_interrupt();
@@ -35418,8 +35364,7 @@ async function execProbeShutdownAware(file2, args, timeoutMs) {
35418
35364
  }
35419
35365
  }
35420
35366
 
35421
- // ../bridge/src/agents/acp/clients/claude-code-acp-client.ts
35422
- var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
35367
+ // ../bridge/src/agents/providers/claude-code/client.ts
35423
35368
  var CLAUDE_ACP_ADAPTER_NPX_ARGS = ["--yes", "@agentclientprotocol/claude-agent-acp"];
35424
35369
  async function detectLocalAgentPresence() {
35425
35370
  return execProbeShutdownAware("npx", [...CLAUDE_ACP_ADAPTER_NPX_ARGS, "--help"], 8e3);
@@ -35438,58 +35383,230 @@ async function createClaudeCodeAcpClient(options) {
35438
35383
  });
35439
35384
  }
35440
35385
 
35441
- // ../bridge/src/agents/acp/clients/codex-acp-client.ts
35442
- var codex_acp_client_exports = {};
35443
- __export(codex_acp_client_exports, {
35444
- BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE2,
35445
- CODEX_ACP_PACKAGE: () => CODEX_ACP_PACKAGE,
35446
- DEFAULT_CODEX_ACP_COMMAND: () => DEFAULT_CODEX_ACP_COMMAND,
35447
- LEGACY_CODEX_ACP_PACKAGE: () => LEGACY_CODEX_ACP_PACKAGE,
35448
- buildCodexAcpSpawnCommand: () => buildCodexAcpSpawnCommand,
35449
- createCodexAcpClient: () => createCodexAcpClient,
35450
- detectLocalAgentPresence: () => detectLocalAgentPresence2,
35451
- isCodexAcpCommand: () => isCodexAcpCommand,
35452
- normalizeCodexAcpCommand: () => normalizeCodexAcpCommand
35453
- });
35454
- var BACKEND_LOCAL_AGENT_TYPE2 = "codex-acp";
35386
+ // ../bridge/src/agents/install/run-streaming-command.ts
35387
+ import { spawn as spawn2 } from "node:child_process";
35388
+ import * as readline from "node:readline";
35389
+ function runStreamingCommand(command, args, options) {
35390
+ return new Promise((resolve36, reject) => {
35391
+ const child = spawn2(command, args, {
35392
+ env: options.env,
35393
+ stdio: ["ignore", "pipe", "pipe"]
35394
+ });
35395
+ let settled = false;
35396
+ const timer = options.timeoutMs != null ? setTimeout(() => {
35397
+ child.kill("SIGKILL");
35398
+ if (!settled) {
35399
+ settled = true;
35400
+ reject(new Error(`Command timed out after ${options.timeoutMs}ms`));
35401
+ }
35402
+ }, options.timeoutMs) : null;
35403
+ const onLine = (line) => {
35404
+ if (line.length > 0) options.onLine?.(line);
35405
+ };
35406
+ if (child.stdout) {
35407
+ readline.createInterface({ input: child.stdout, crlfDelay: Infinity }).on("line", onLine);
35408
+ }
35409
+ if (child.stderr) {
35410
+ readline.createInterface({ input: child.stderr, crlfDelay: Infinity }).on("line", onLine);
35411
+ }
35412
+ child.on("error", (err) => {
35413
+ if (timer) clearTimeout(timer);
35414
+ if (!settled) {
35415
+ settled = true;
35416
+ reject(err);
35417
+ }
35418
+ });
35419
+ child.on("close", (code, signal) => {
35420
+ if (timer) clearTimeout(timer);
35421
+ if (!settled) {
35422
+ settled = true;
35423
+ resolve36({ code, signal });
35424
+ }
35425
+ });
35426
+ });
35427
+ }
35428
+
35429
+ // ../bridge/src/agents/install/commands/run-npm-global-install.ts
35430
+ async function runNpmGlobalInstall(packageName, env, options) {
35431
+ const result = await runStreamingCommand("npm", ["install", "-g", packageName], {
35432
+ env: bridgeAgentPathEnv(env),
35433
+ timeoutMs: options?.timeoutMs ?? 3e5,
35434
+ onLine: options?.onLine
35435
+ });
35436
+ if (result.code !== 0) {
35437
+ throw new Error(`npm install -g ${packageName} failed (exit ${result.code ?? "signal"})`);
35438
+ }
35439
+ }
35440
+
35441
+ // ../bridge/src/agents/providers/claude-code/install.ts
35442
+ var claudeCodeInstall = {
35443
+ detectCommand: "claude",
35444
+ tokenEnvVar: "ANTHROPIC_API_KEY",
35445
+ async run(ctx) {
35446
+ ctx.onProgress?.("Installing Anthropic Claude Code");
35447
+ await runNpmGlobalInstall(
35448
+ "@anthropic-ai/claude-code",
35449
+ { ...ctx.env, ANTHROPIC_API_KEY: ctx.authToken },
35450
+ { onLine: (line) => ctx.onProgress?.("Installing Anthropic Claude Code", line) }
35451
+ );
35452
+ }
35453
+ };
35454
+
35455
+ // ../bridge/src/agents/providers/claude-code/definition.ts
35456
+ var DEFAULT_COMMAND = ["npx", "--yes", "@agentclientprotocol/claude-agent-acp"];
35457
+ var claudeCodeProvider = {
35458
+ type: "claude-code",
35459
+ displayName: "Claude Code",
35460
+ defaultCommand: DEFAULT_COMMAND,
35461
+ authErrorHints: claudeCodeAuthErrorHints,
35462
+ detectPresence: detectLocalAgentPresence,
35463
+ install: claudeCodeInstall,
35464
+ createClient: (options) => createClaudeCodeAcpClient({
35465
+ ...options,
35466
+ afterSessionEstablished: options.afterSessionEstablished ?? applyClaudeCodeAfterSessionEstablished
35467
+ }),
35468
+ buildSpawnCommand: (base, sessionMode) => buildClaudeCodeAcpSpawnCommand([...base], sessionMode)
35469
+ };
35470
+
35471
+ // ../bridge/src/agents/providers/codex/apply-permission.ts
35472
+ function flattenSelectOptions3(options) {
35473
+ if (options == null || options.length === 0) return [];
35474
+ const first2 = options[0];
35475
+ if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
35476
+ return options.flatMap(
35477
+ (g) => Array.isArray(g.options) ? g.options : []
35478
+ );
35479
+ }
35480
+ return options;
35481
+ }
35482
+ function pickModeConfigOption2(configOptions) {
35483
+ if (configOptions == null || configOptions.length === 0) return null;
35484
+ const byCategory = configOptions.find((o) => o.category === "mode");
35485
+ if (byCategory) return byCategory;
35486
+ return configOptions.find((o) => o.id === "mode") ?? null;
35487
+ }
35488
+ async function applyCodexPermissionFromAcpSession(params) {
35489
+ const { sessionId, agentConfig, configOptions, modes, setSessionConfigOption, setSessionMode, logDebug: logDebug2 } = params;
35490
+ const desiredMode = getCodexPermissionModeFromAgentConfig(agentConfig);
35491
+ if (desiredMode == null) return;
35492
+ const modeOpt = pickModeConfigOption2(configOptions ?? null);
35493
+ if (modeOpt != null) {
35494
+ const flat = flattenSelectOptions3(modeOpt.options);
35495
+ const allowed = flat.some((o) => o.value === desiredMode);
35496
+ if (allowed && modeOpt.currentValue !== desiredMode) {
35497
+ try {
35498
+ logDebug2(
35499
+ `[Agent] Codex: sending ACP session/set_config_option (mode) configId=${JSON.stringify(modeOpt.id)} value=${JSON.stringify(desiredMode)} was=${JSON.stringify(modeOpt.currentValue)} sessionId=${sessionId.slice(0, 8)}\u2026`
35500
+ );
35501
+ await setSessionConfigOption({ sessionId, configId: modeOpt.id, value: desiredMode });
35502
+ } catch (e) {
35503
+ logDebug2(`[Agent] Codex: session/set_config_option failed: ${e instanceof Error ? e.message : String(e)}`);
35504
+ }
35505
+ }
35506
+ return;
35507
+ }
35508
+ if (modes?.availableModes?.length) {
35509
+ const allowed = modes.availableModes.some((m) => m.id === desiredMode);
35510
+ if (allowed && desiredMode !== modes.currentModeId) {
35511
+ try {
35512
+ logDebug2(
35513
+ `[Agent] Codex: sending ACP session/set_mode modeId=${JSON.stringify(desiredMode)} was=${JSON.stringify(modes.currentModeId ?? null)} sessionId=${sessionId.slice(0, 8)}\u2026`
35514
+ );
35515
+ await setSessionMode({ sessionId, modeId: desiredMode });
35516
+ } catch (e) {
35517
+ logDebug2(`[Agent] Codex: session/set_mode failed: ${e instanceof Error ? e.message : String(e)}`);
35518
+ }
35519
+ }
35520
+ }
35521
+ }
35522
+
35523
+ // ../bridge/src/agents/providers/codex/after-session.ts
35524
+ var applyCodexAfterSessionEstablished = wrapPermissionAfterSession(
35525
+ applyCodexPermissionFromAcpSession
35526
+ );
35527
+
35528
+ // ../bridge/src/agents/providers/codex/client.ts
35455
35529
  var CODEX_ACP_PACKAGE = "@agentclientprotocol/codex-acp";
35456
35530
  var LEGACY_CODEX_ACP_PACKAGE = "@zed-industries/codex-acp";
35457
35531
  async function detectLocalAgentPresence2() {
35458
35532
  return isCommandOnPath("codex");
35459
35533
  }
35460
35534
  var DEFAULT_CODEX_ACP_COMMAND = ["npx", "--yes", CODEX_ACP_PACKAGE];
35461
- function isCodexAcpCommand(command) {
35462
- return command.some(
35463
- (a) => a === CODEX_ACP_PACKAGE || a === LEGACY_CODEX_ACP_PACKAGE || a.includes("codex-acp")
35464
- );
35465
- }
35466
35535
  function normalizeCodexAcpCommand(command) {
35467
35536
  return command.map((a) => a === LEGACY_CODEX_ACP_PACKAGE ? CODEX_ACP_PACKAGE : a);
35468
35537
  }
35469
- function buildCodexAcpSpawnCommand(base, _sessionMode, _agentConfig) {
35470
- return normalizeCodexAcpCommand(base);
35538
+ function buildCodexAcpSpawnCommand(base, _sessionMode, _agentConfig) {
35539
+ return normalizeCodexAcpCommand(base);
35540
+ }
35541
+ async function createCodexAcpClient(options) {
35542
+ const base = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
35543
+ const command = buildCodexAcpSpawnCommand(base, options.sessionMode, options.agentConfig);
35544
+ return createSdkStdioAcpClient({
35545
+ ...options,
35546
+ command,
35547
+ /** Codex ACP can ignore `session/cancel`; mirror Claude Code's subprocess fallback. */
35548
+ killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 2500
35549
+ });
35550
+ }
35551
+
35552
+ // ../bridge/src/agents/providers/codex/install.ts
35553
+ var codexInstall = {
35554
+ detectCommand: "codex",
35555
+ tokenEnvVar: "OPENAI_API_KEY",
35556
+ async run(ctx) {
35557
+ ctx.onProgress?.("Installing Codex");
35558
+ await runNpmGlobalInstall(
35559
+ "@openai/codex",
35560
+ { ...ctx.env, OPENAI_API_KEY: ctx.authToken },
35561
+ { onLine: (line) => ctx.onProgress?.("Installing Codex", line) }
35562
+ );
35563
+ }
35564
+ };
35565
+
35566
+ // ../bridge/src/agents/providers/codex/definition.ts
35567
+ var codexProvider = {
35568
+ type: "codex-acp",
35569
+ displayName: "Codex",
35570
+ defaultCommand: DEFAULT_CODEX_ACP_COMMAND,
35571
+ authErrorHints: codexAuthErrorHints,
35572
+ detectPresence: detectLocalAgentPresence2,
35573
+ install: codexInstall,
35574
+ createClient: (options) => createCodexAcpClient({
35575
+ ...options,
35576
+ afterSessionEstablished: options.afterSessionEstablished ?? applyCodexAfterSessionEstablished
35577
+ }),
35578
+ buildSpawnCommand: (base, sessionMode, agentConfig) => buildCodexAcpSpawnCommand([...base], sessionMode, agentConfig)
35579
+ };
35580
+
35581
+ // ../bridge/src/agents/providers/cursor/cleanup-session-plans.ts
35582
+ import * as fs17 from "node:fs";
35583
+
35584
+ // ../bridge/src/paths/session-plans-paths.ts
35585
+ import * as os6 from "node:os";
35586
+ import * as path25 from "node:path";
35587
+ function getSessionPlansRootDir() {
35588
+ return path25.join(os6.homedir(), ".buildautomaton", "plans");
35589
+ }
35590
+ function sanitizeSessionPlansKey(sessionId) {
35591
+ const t = sessionId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 220);
35592
+ return t || "session";
35471
35593
  }
35472
- async function createCodexAcpClient(options) {
35473
- const base = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
35474
- const command = buildCodexAcpSpawnCommand(base, options.sessionMode, options.agentConfig);
35475
- return createSdkStdioAcpClient({
35476
- ...options,
35477
- command,
35478
- /** Codex ACP can ignore `session/cancel`; mirror Claude Code's subprocess fallback. */
35479
- killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 2500
35480
- });
35594
+ function getSessionPlansDir(sessionId) {
35595
+ return path25.join(getSessionPlansRootDir(), sanitizeSessionPlansKey(sessionId));
35481
35596
  }
35482
35597
 
35483
- // ../bridge/src/agents/acp/clients/cursor/cursor-acp-client.ts
35484
- var cursor_acp_client_exports = {};
35485
- __export(cursor_acp_client_exports, {
35486
- BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE3,
35487
- buildCursorAcpSpawnCommand: () => buildCursorAcpSpawnCommand,
35488
- createCursorAcpClient: () => createCursorAcpClient,
35489
- detectLocalAgentPresence: () => detectLocalAgentPresence3
35490
- });
35598
+ // ../bridge/src/agents/providers/cursor/cleanup-session-plans.ts
35599
+ function cleanupSessionPlans(sessionId) {
35600
+ const id = typeof sessionId === "string" ? sessionId.trim() : "";
35601
+ if (!id) return;
35602
+ const dir = getSessionPlansDir(id);
35603
+ try {
35604
+ fs17.rmSync(dir, { recursive: true, force: true });
35605
+ } catch {
35606
+ }
35607
+ }
35491
35608
 
35492
- // ../bridge/src/agents/acp/clients/cursor/cursor-spawn-command.ts
35609
+ // ../bridge/src/agents/providers/cursor/cursor-spawn-command.ts
35493
35610
  function buildCursorAcpSpawnCommand(base, sessionMode) {
35494
35611
  if (!sessionMode) return [...base];
35495
35612
  const m = sessionMode.trim();
@@ -35497,7 +35614,7 @@ function buildCursorAcpSpawnCommand(base, sessionMode) {
35497
35614
  return [...base, "--mode", m];
35498
35615
  }
35499
35616
 
35500
- // ../bridge/src/agents/acp/clients/cursor/create-cursor-acp-session-context.ts
35617
+ // ../bridge/src/agents/providers/cursor/create-cursor-acp-session-context.ts
35501
35618
  init_log();
35502
35619
  function createCursorAcpSessionContext(options) {
35503
35620
  const suppressLoadReplayRef = { value: false };
@@ -35516,14 +35633,15 @@ function createCursorAcpSessionContext(options) {
35516
35633
  onAcpConfigOptionsUpdated: options.onAcpConfigOptionsUpdated,
35517
35634
  logDebug,
35518
35635
  getStderrText: () => options.stderrCapture.getText(),
35519
- pendingPlanExecute: { value: false }
35636
+ pendingPlanExecute: { value: false },
35637
+ afterSessionEstablished: options.afterSessionEstablished
35520
35638
  };
35521
35639
  }
35522
35640
 
35523
- // ../bridge/src/agents/acp/clients/cursor/send-cursor-prompt-with-plan-continue.ts
35641
+ // ../bridge/src/agents/providers/cursor/send-cursor-prompt-with-plan-continue.ts
35524
35642
  init_log();
35525
35643
 
35526
- // ../bridge/src/agents/acp/clients/cursor/cursor-plan-continue.ts
35644
+ // ../bridge/src/agents/providers/cursor/cursor-plan-continue.ts
35527
35645
  var CURSOR_PLAN_CONTINUE_PROMPT = "The user accepted the plan. Implement it now. Do not wait for another approval unless the plan needs material changes.";
35528
35646
  function isAcceptedCreatePlanRpcResult(result) {
35529
35647
  if (result == null || typeof result !== "object" || Array.isArray(result)) return false;
@@ -35547,7 +35665,7 @@ async function switchCursorSessionToAgentMode(transport, sessionId) {
35547
35665
  }
35548
35666
  }
35549
35667
 
35550
- // ../bridge/src/agents/acp/clients/cursor/send-cursor-prompt-with-plan-continue.ts
35668
+ // ../bridge/src/agents/providers/cursor/send-cursor-prompt-with-plan-continue.ts
35551
35669
  async function sendCursorPromptWithPlanContinue(params) {
35552
35670
  const { transport, sessionCtx, sessionId, prompt, images } = params;
35553
35671
  const first2 = await sendAcpPromptViaTransport(transport, sessionCtx, sessionId, prompt, images);
@@ -35562,7 +35680,7 @@ async function sendCursorPromptWithPlanContinue(params) {
35562
35680
  );
35563
35681
  }
35564
35682
 
35565
- // ../bridge/src/agents/acp/clients/cursor/cancel-pending-cursor-permission-requests.ts
35683
+ // ../bridge/src/agents/providers/cursor/cancel-pending-cursor-permission-requests.ts
35566
35684
  function cancelPendingCursorPermissionRequests(pendingRequests2, respond) {
35567
35685
  for (const [reqId, pending2] of [...pendingRequests2.entries()]) {
35568
35686
  if (pending2.method === "session/request_permission") {
@@ -35572,7 +35690,7 @@ function cancelPendingCursorPermissionRequests(pendingRequests2, respond) {
35572
35690
  }
35573
35691
  }
35574
35692
 
35575
- // ../bridge/src/agents/acp/clients/cursor/create-cursor-acp-handle.ts
35693
+ // ../bridge/src/agents/providers/cursor/create-cursor-acp-handle.ts
35576
35694
  function createCursorAcpHandle(options) {
35577
35695
  let teardownStarted = false;
35578
35696
  let cancelFallback = null;
@@ -35635,10 +35753,10 @@ function createCursorAcpHandle(options) {
35635
35753
  };
35636
35754
  }
35637
35755
 
35638
- // ../bridge/src/agents/acp/clients/cursor/cursor-acp-init.ts
35639
- import * as readline from "node:readline";
35756
+ // ../bridge/src/agents/providers/cursor/cursor-acp-init.ts
35757
+ import * as readline2 from "node:readline";
35640
35758
 
35641
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-cursor-create-plan.ts
35759
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-cursor-create-plan.ts
35642
35760
  function buildCursorCreatePlanToolCallUpdate(requestId, params) {
35643
35761
  const toolCallId = params.toolCallId ?? params.tool_call_id;
35644
35762
  if (typeof toolCallId !== "string" || !toolCallId.trim()) return null;
@@ -35673,7 +35791,7 @@ function queueCursorCreatePlanRequest(method, id, msg, deps) {
35673
35791
  deps.onRequest?.({ requestId, method, params });
35674
35792
  }
35675
35793
 
35676
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-cursor-task.ts
35794
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-cursor-task.ts
35677
35795
  function buildCursorTaskToolCallUpdate(params) {
35678
35796
  const toolCallId = params.toolCallId ?? params.tool_call_id;
35679
35797
  if (typeof toolCallId !== "string" || !toolCallId.trim()) return null;
@@ -35706,7 +35824,7 @@ function handleCursorIncomingCursorTask(id, msg, deps) {
35706
35824
  if (update) deps.onSessionUpdate?.(update);
35707
35825
  }
35708
35826
 
35709
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-cursor-methods.ts
35827
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-cursor-methods.ts
35710
35828
  var CURSOR_BRIDGE_METHODS = /* @__PURE__ */ new Set(["cursor/ask_question"]);
35711
35829
  var CURSOR_NOOP_METHODS = /* @__PURE__ */ new Set(["cursor/update_todos", "cursor/generate_image"]);
35712
35830
  function handleCursorIncomingCursorNotification(method, msg, onSessionUpdate) {
@@ -35742,7 +35860,7 @@ function handleCursorIncomingCursorMethods(method, id, msg, deps) {
35742
35860
  return false;
35743
35861
  }
35744
35862
 
35745
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-types.ts
35863
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-types.ts
35746
35864
  function parseIncomingJsonRpcRequestId(raw) {
35747
35865
  if (typeof raw === "number" && Number.isFinite(raw)) return raw;
35748
35866
  if (typeof raw === "string" && raw.length > 0) return raw;
@@ -35761,7 +35879,7 @@ function isCursorFsWriteMethod(method) {
35761
35879
  return lower.startsWith("fs/") && lower.includes("write");
35762
35880
  }
35763
35881
 
35764
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-fs-request.ts
35882
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-fs-request.ts
35765
35883
  function pathFromFsParams(params) {
35766
35884
  for (const key of ["path", "filePath", "file_path", "targetPath", "target_path"]) {
35767
35885
  const value = params[key];
@@ -35818,7 +35936,7 @@ function handleCursorIncomingFsRequest(method, id, msg, deps) {
35818
35936
  return false;
35819
35937
  }
35820
35938
 
35821
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-permission-request.ts
35939
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-permission-request.ts
35822
35940
  function handleCursorIncomingPermissionRequest(id, method, msg, deps) {
35823
35941
  const params = msg.params ?? {};
35824
35942
  if (deps.onRequest) {
@@ -35834,39 +35952,23 @@ function handleCursorIncomingPermissionRequest(id, method, msg, deps) {
35834
35952
  return true;
35835
35953
  }
35836
35954
 
35837
- // ../bridge/src/agents/acp/clients/cursor/write-create-plan-file.ts
35838
- import * as fs17 from "node:fs";
35955
+ // ../bridge/src/agents/providers/cursor/write-create-plan-file.ts
35956
+ import * as fs18 from "node:fs";
35839
35957
  import * as path26 from "node:path";
35840
35958
  import { pathToFileURL as pathToFileURL2 } from "node:url";
35841
-
35842
- // ../bridge/src/paths/session-plans-paths.ts
35843
- import * as os6 from "node:os";
35844
- import * as path25 from "node:path";
35845
- function getSessionPlansRootDir() {
35846
- return path25.join(os6.homedir(), ".buildautomaton", "plans");
35847
- }
35848
- function sanitizeSessionPlansKey(sessionId) {
35849
- const t = sessionId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 220);
35850
- return t || "session";
35851
- }
35852
- function getSessionPlansDir(sessionId) {
35853
- return path25.join(getSessionPlansRootDir(), sanitizeSessionPlansKey(sessionId));
35854
- }
35855
-
35856
- // ../bridge/src/agents/acp/clients/cursor/write-create-plan-file.ts
35857
35959
  function sanitizePlanFileBase(toolCallId) {
35858
35960
  const t = toolCallId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 120);
35859
35961
  return t || "plan";
35860
35962
  }
35861
35963
  function writeCreatePlanFile(params) {
35862
35964
  const dir = getSessionPlansDir(params.cloudSessionId);
35863
- fs17.mkdirSync(dir, { recursive: true });
35965
+ fs18.mkdirSync(dir, { recursive: true });
35864
35966
  const filePath = path26.join(dir, `${sanitizePlanFileBase(params.toolCallId)}.md`);
35865
- fs17.writeFileSync(filePath, params.planMarkdown, "utf8");
35967
+ fs18.writeFileSync(filePath, params.planMarkdown, "utf8");
35866
35968
  return pathToFileURL2(filePath).href;
35867
35969
  }
35868
35970
 
35869
- // ../bridge/src/agents/acp/clients/cursor/enrich-create-plan-rpc-result.ts
35971
+ // ../bridge/src/agents/providers/cursor/enrich-create-plan-rpc-result.ts
35870
35972
  function asRecord2(v) {
35871
35973
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
35872
35974
  }
@@ -35895,7 +35997,7 @@ function enrichCreatePlanRpcResult(result, pendingParams, cloudSessionId) {
35895
35997
  return { outcome: { outcome: "accepted" } };
35896
35998
  }
35897
35999
 
35898
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-resolve-request.ts
36000
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-resolve-request.ts
35899
36001
  function resolveCursorIncomingRequest(pendingRequests2, respond, requestId, result, cloudSessionId, pendingPlanExecute) {
35900
36002
  const pending2 = pendingRequests2.get(requestId);
35901
36003
  let payload = result;
@@ -35922,7 +36024,7 @@ function formatSessionUpdateKindForLog(kind) {
35922
36024
  return kind.split("_").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
35923
36025
  }
35924
36026
 
35925
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-session-update.ts
36027
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-session-update.ts
35926
36028
  function handleCursorIncomingSessionUpdate(msg, deps) {
35927
36029
  const params = msg.params;
35928
36030
  const update = params?.update;
@@ -35943,7 +36045,7 @@ function handleCursorIncomingSessionUpdate(msg, deps) {
35943
36045
  return true;
35944
36046
  }
35945
36047
 
35946
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-parse.ts
36048
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-parse.ts
35947
36049
  function safeJsonParse(value) {
35948
36050
  try {
35949
36051
  const parsed = JSON.parse(value);
@@ -35953,7 +36055,7 @@ function safeJsonParse(value) {
35953
36055
  }
35954
36056
  }
35955
36057
 
35956
- // ../bridge/src/agents/acp/clients/cursor/cursor-acp-incoming-line-handler.ts
36058
+ // ../bridge/src/agents/providers/cursor/cursor-acp-incoming-line-handler.ts
35957
36059
  function createCursorAcpIncomingLineHandler(deps) {
35958
36060
  const respondDeps = {
35959
36061
  dbgFs: deps.dbgFs,
@@ -36009,7 +36111,7 @@ function createCursorAcpIncomingLineHandler(deps) {
36009
36111
  };
36010
36112
  }
36011
36113
 
36012
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-acp-transport.ts
36114
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-acp-transport.ts
36013
36115
  function createCursorJsonRpcAcpTransport(deps) {
36014
36116
  const { send, cancelSessionNotification, skipBrowserAuthenticate } = deps;
36015
36117
  return {
@@ -36027,12 +36129,12 @@ function createCursorJsonRpcAcpTransport(deps) {
36027
36129
  };
36028
36130
  }
36029
36131
 
36030
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-stdin-write.ts
36132
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-stdin-write.ts
36031
36133
  function writeJsonRpcLine(stdin, payload, callback) {
36032
36134
  stdin.write(JSON.stringify(payload) + "\n", callback);
36033
36135
  }
36034
36136
 
36035
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-inbound-respond.ts
36137
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-inbound-respond.ts
36036
36138
  function createCursorJsonRpcInboundRespond(stdin) {
36037
36139
  function respond(id, result) {
36038
36140
  writeJsonRpcLine(stdin, { jsonrpc: "2.0", id, result });
@@ -36053,7 +36155,7 @@ function createCursorJsonRpcInboundRespond(stdin) {
36053
36155
  return { respond, respondJsonRpcError, cancelSessionNotification };
36054
36156
  }
36055
36157
 
36056
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-outbound.ts
36158
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-outbound.ts
36057
36159
  function createCursorJsonRpcOutboundPending() {
36058
36160
  const pending2 = /* @__PURE__ */ new Map();
36059
36161
  let nextId = 1;
@@ -36079,7 +36181,7 @@ function createCursorJsonRpcOutboundPending() {
36079
36181
  return { allocateId, register, settleResponse, rejectOnWriteError };
36080
36182
  }
36081
36183
 
36082
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-wire.ts
36184
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-wire.ts
36083
36185
  function createCursorJsonRpcWriter(stdin) {
36084
36186
  const inbound = createCursorJsonRpcInboundRespond(stdin);
36085
36187
  const outbound = createCursorJsonRpcOutboundPending();
@@ -36101,7 +36203,7 @@ function createCursorJsonRpcWriter(stdin) {
36101
36203
  };
36102
36204
  }
36103
36205
 
36104
- // ../bridge/src/agents/acp/clients/cursor/cursor-acp-init.ts
36206
+ // ../bridge/src/agents/providers/cursor/cursor-acp-init.ts
36105
36207
  var CURSOR_ACP_CLIENT_INFO = {
36106
36208
  protocolVersion: 1,
36107
36209
  clientCapabilities: {
@@ -36121,7 +36223,7 @@ async function initCursorAcpWire(options) {
36121
36223
  settleResponse: wire.settleResponse,
36122
36224
  pendingRequests: pendingRequests2
36123
36225
  });
36124
- const rl = readline.createInterface({ input: options.child.stdout });
36226
+ const rl = readline2.createInterface({ input: options.child.stdout });
36125
36227
  rl.on("line", (line) => incoming.handleLine(line));
36126
36228
  const transport = createCursorJsonRpcAcpTransport({
36127
36229
  send: wire.send,
@@ -36132,11 +36234,11 @@ async function initCursorAcpWire(options) {
36132
36234
  return { wire, transport, established, incoming, pendingRequests: pendingRequests2 };
36133
36235
  }
36134
36236
 
36135
- // ../bridge/src/agents/acp/clients/cursor/spawn-cursor-acp-process.ts
36136
- import { spawn as spawn2 } from "node:child_process";
36237
+ // ../bridge/src/agents/providers/cursor/spawn-cursor-acp-process.ts
36238
+ import { spawn as spawn3 } from "node:child_process";
36137
36239
  function spawnCursorAcpProcess(options) {
36138
36240
  const isWindows = process.platform === "win32";
36139
- const child = spawn2(options.command[0], options.command.slice(1), {
36241
+ const child = spawn3(options.command[0], options.command.slice(1), {
36140
36242
  cwd: options.cwd,
36141
36243
  stdio: ["pipe", "pipe", "pipe"],
36142
36244
  env: bridgeInstalledAgentAuthProcessEnv(process.env),
@@ -36150,13 +36252,12 @@ function spawnCursorAcpProcess(options) {
36150
36252
  return { child, stderrCapture };
36151
36253
  }
36152
36254
 
36153
- // ../bridge/src/agents/acp/clients/cursor/cursor-local-agent.ts
36154
- var BACKEND_LOCAL_AGENT_TYPE3 = "cursor-cli";
36255
+ // ../bridge/src/agents/providers/cursor/cursor-local-agent.ts
36155
36256
  async function detectLocalAgentPresence3() {
36156
36257
  return isCommandOnPath("agent");
36157
36258
  }
36158
36259
 
36159
- // ../bridge/src/agents/acp/clients/cursor/cursor-acp-client.ts
36260
+ // ../bridge/src/agents/providers/cursor/cursor-acp-client.ts
36160
36261
  async function createCursorAcpClient(options) {
36161
36262
  const command = buildCursorAcpSpawnCommand(options.command, options.sessionMode);
36162
36263
  const {
@@ -36168,7 +36269,8 @@ async function createCursorAcpClient(options) {
36168
36269
  persistedAcpSessionId,
36169
36270
  onAcpSessionEstablished,
36170
36271
  onAcpConfigOptionsUpdated,
36171
- onAgentSubprocessExit
36272
+ onAgentSubprocessExit,
36273
+ afterSessionEstablished
36172
36274
  } = options;
36173
36275
  const dbgFs = process.env.BUILDAUTOMATON_DEBUG_ACP_FS === "1";
36174
36276
  const spawnEnv = bridgeInstalledAgentAuthProcessEnv(process.env);
@@ -36189,6 +36291,7 @@ async function createCursorAcpClient(options) {
36189
36291
  onAcpSessionEstablished,
36190
36292
  onAcpConfigOptionsUpdated,
36191
36293
  onFileChange,
36294
+ afterSessionEstablished,
36192
36295
  stderrCapture
36193
36296
  });
36194
36297
  return new Promise((resolve36, reject) => {
@@ -36224,17 +36327,43 @@ async function createCursorAcpClient(options) {
36224
36327
  });
36225
36328
  }
36226
36329
 
36227
- // ../bridge/src/agents/acp/clients/kiro-acp-client.ts
36228
- var kiro_acp_client_exports = {};
36229
- __export(kiro_acp_client_exports, {
36230
- BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE4,
36231
- DEFAULT_KIRO_ACP_COMMAND: () => DEFAULT_KIRO_ACP_COMMAND,
36232
- buildKiroAcpSpawnCommand: () => buildKiroAcpSpawnCommand,
36233
- createKiroAcpClient: () => createKiroAcpClient,
36234
- detectLocalAgentPresence: () => detectLocalAgentPresence4,
36235
- isKiroAcpCommand: () => isKiroAcpCommand
36236
- });
36237
- var BACKEND_LOCAL_AGENT_TYPE4 = "kiro-acp";
36330
+ // ../bridge/src/agents/providers/cursor/install.ts
36331
+ var cursorInstall = {
36332
+ detectCommand: "agent",
36333
+ alternateDetectCommands: ["cursor-agent"],
36334
+ tokenEnvVar: "CURSOR_API_KEY",
36335
+ async run(ctx) {
36336
+ ctx.onProgress?.("Installing Cursor CLI");
36337
+ const result = await runStreamingCommand(
36338
+ "bash",
36339
+ ["-lc", "curl -fsSL https://cursor.com/install | bash"],
36340
+ {
36341
+ timeoutMs: 3e5,
36342
+ env: { ...bridgeAgentPathEnv(ctx.env), CURSOR_API_KEY: ctx.authToken },
36343
+ onLine: (line) => ctx.onProgress?.("Installing Cursor CLI", line)
36344
+ }
36345
+ );
36346
+ if (result.code !== 0) {
36347
+ throw new Error(`Cursor CLI install failed (exit ${result.code ?? "signal"})`);
36348
+ }
36349
+ }
36350
+ };
36351
+
36352
+ // ../bridge/src/agents/providers/cursor/definition.ts
36353
+ var cursorProvider = {
36354
+ type: "cursor-cli",
36355
+ displayName: "Cursor",
36356
+ defaultCommand: ["agent", "acp"],
36357
+ authErrorHints: cursorAuthErrorHints,
36358
+ detectPresence: detectLocalAgentPresence3,
36359
+ install: cursorInstall,
36360
+ createClient: createCursorAcpClient,
36361
+ buildSpawnCommand: (base, sessionMode) => buildCursorAcpSpawnCommand([...base], sessionMode),
36362
+ onPromptTurnFinished: cleanupSessionPlans,
36363
+ onSessionClosed: cleanupSessionPlans
36364
+ };
36365
+
36366
+ // ../bridge/src/agents/providers/kiro/client.ts
36238
36367
  async function detectLocalAgentPresence4() {
36239
36368
  return isCommandOnPath("kiro-cli");
36240
36369
  }
@@ -36255,80 +36384,108 @@ async function createKiroAcpClient(options) {
36255
36384
  return createSdkStdioAcpClient({ ...options, command });
36256
36385
  }
36257
36386
 
36258
- // ../bridge/src/agents/acp/resolve-agent-command.ts
36259
- var AGENT_TYPE_DEFAULT_COMMANDS = {
36260
- [BACKEND_LOCAL_AGENT_TYPE3]: ["agent", "acp"],
36261
- [BACKEND_LOCAL_AGENT_TYPE2]: [...DEFAULT_CODEX_ACP_COMMAND],
36262
- /** ACP stdio agent; `@anthropic-ai/claude-code` is the interactive CLI and does not speak ACP on stdout. */
36263
- [BACKEND_LOCAL_AGENT_TYPE]: ["npx", "--yes", "@agentclientprotocol/claude-agent-acp"],
36264
- /** [Kiro CLI ACP](https://kiro.dev/docs/cli/acp/) — use full path to `kiro-cli` in PATH if the IDE cannot find it. */
36265
- [BACKEND_LOCAL_AGENT_TYPE4]: [...DEFAULT_KIRO_ACP_COMMAND]
36387
+ // ../bridge/src/agents/providers/kiro/ext-notifications.ts
36388
+ function createKiroSdkExtNotificationHandler(options) {
36389
+ const { onSessionUpdate } = options;
36390
+ return async (method, params) => {
36391
+ if (method === "_kiro.dev/metadata") {
36392
+ const p = params && typeof params === "object" ? params : {};
36393
+ const pct = p.contextUsagePercentage;
36394
+ if (typeof pct !== "number" || !Number.isFinite(pct) || !onSessionUpdate) return;
36395
+ onSessionUpdate({
36396
+ sessionUpdate: "context_usage",
36397
+ contextUsagePercentage: pct
36398
+ });
36399
+ return;
36400
+ }
36401
+ };
36402
+ }
36403
+
36404
+ // ../bridge/src/agents/providers/kiro/definition.ts
36405
+ var kiroProvider = {
36406
+ type: "kiro-acp",
36407
+ displayName: "Kiro",
36408
+ defaultCommand: DEFAULT_KIRO_ACP_COMMAND,
36409
+ authErrorHints: kiroAuthErrorHints,
36410
+ detectPresence: detectLocalAgentPresence4,
36411
+ createClient: (options) => createKiroAcpClient({
36412
+ ...options,
36413
+ createExtNotificationHandler: options.createExtNotificationHandler ?? createKiroSdkExtNotificationHandler
36414
+ }),
36415
+ buildSpawnCommand: (base, sessionMode) => buildKiroAcpSpawnCommand([...base], sessionMode)
36416
+ };
36417
+
36418
+ // ../bridge/src/agents/providers/opencode/install.ts
36419
+ var opencodeInstall = {
36420
+ detectCommand: "opencode",
36421
+ tokenEnvVar: "OPENCODE_API_KEY",
36422
+ async run(ctx) {
36423
+ ctx.onProgress?.("Installing OpenCode");
36424
+ await runNpmGlobalInstall(
36425
+ "opencode-ai",
36426
+ { ...ctx.env, OPENCODE_API_KEY: ctx.authToken },
36427
+ { onLine: (line) => ctx.onProgress?.("Installing OpenCode", line) }
36428
+ );
36429
+ }
36266
36430
  };
36267
- var AGENT_TYPE_DISPLAY_NAMES = {
36268
- [BACKEND_LOCAL_AGENT_TYPE3]: "Cursor",
36269
- [BACKEND_LOCAL_AGENT_TYPE2]: "Codex",
36270
- [BACKEND_LOCAL_AGENT_TYPE]: "Claude Code",
36271
- [BACKEND_LOCAL_AGENT_TYPE4]: "Kiro"
36431
+
36432
+ // ../bridge/src/agents/providers/opencode/definition.ts
36433
+ var opencodeProvider = {
36434
+ type: "opencode",
36435
+ displayName: "OpenCode",
36436
+ defaultCommand: [],
36437
+ authErrorHints: [],
36438
+ install: opencodeInstall,
36439
+ buildSpawnCommand: (base) => [...base]
36272
36440
  };
36273
- function getAgentTypeDisplayName(agentType) {
36274
- if (agentType == null || agentType === "") return "Unknown agent";
36275
- const known = AGENT_TYPE_DISPLAY_NAMES[agentType];
36276
- if (known) return known;
36277
- return agentType.split(/[-_]/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
36441
+
36442
+ // ../bridge/src/agents/providers/registry.ts
36443
+ var AGENT_PROVIDERS = [
36444
+ cursorProvider,
36445
+ codexProvider,
36446
+ kiroProvider,
36447
+ claudeCodeProvider,
36448
+ opencodeProvider
36449
+ ];
36450
+ var byType = new Map(AGENT_PROVIDERS.map((p) => [p.type, p]));
36451
+ function getAgentProvider(agentType) {
36452
+ if (agentType == null || agentType === "") return void 0;
36453
+ return byType.get(agentType);
36278
36454
  }
36279
- function useCursorAcp(agentType, command) {
36280
- if (agentType === BACKEND_LOCAL_AGENT_TYPE3) return true;
36281
- return command[0] === "agent" && command[1] === "acp";
36455
+ function listAutoDetectAgentProviders() {
36456
+ return AGENT_PROVIDERS.filter((p) => p.detectPresence != null);
36282
36457
  }
36283
- function useCodexAcp(agentType, command) {
36284
- if (agentType === BACKEND_LOCAL_AGENT_TYPE2) return true;
36285
- return isCodexAcpCommand(command);
36458
+ function notifyAgentProvidersSessionClosed(sessionId) {
36459
+ for (const p of AGENT_PROVIDERS) p.onSessionClosed?.(sessionId);
36286
36460
  }
36287
- function useKiroAcp(agentType, command) {
36288
- if (agentType === BACKEND_LOCAL_AGENT_TYPE4) return true;
36289
- return isKiroAcpCommand(command);
36461
+ function notifyAgentProvidersPromptTurnFinished(sessionId) {
36462
+ for (const p of AGENT_PROVIDERS) p.onPromptTurnFinished?.(sessionId);
36290
36463
  }
36464
+
36465
+ // ../bridge/src/agents/acp/resolve-agent-command.ts
36291
36466
  function resolveAgentCommand(preferredAgentType) {
36292
- if (!preferredAgentType) return null;
36293
- const command = AGENT_TYPE_DEFAULT_COMMANDS[preferredAgentType];
36294
- if (!command?.length) return null;
36295
- if (useCursorAcp(preferredAgentType, command)) {
36296
- return {
36297
- command,
36298
- label: preferredAgentType,
36299
- createClient: createCursorAcpClient,
36300
- spawnCommandForSession: (sessionMode, _agentConfig) => buildCursorAcpSpawnCommand(command, sessionMode)
36301
- };
36302
- }
36303
- if (useCodexAcp(preferredAgentType, command)) {
36304
- return {
36305
- command,
36306
- label: preferredAgentType,
36307
- createClient: createCodexAcpClient,
36308
- spawnCommandForSession: (sessionMode, agentConfig) => buildCodexAcpSpawnCommand(command, sessionMode, agentConfig)
36309
- };
36310
- }
36311
- if (useKiroAcp(preferredAgentType, command)) {
36312
- return {
36313
- command,
36314
- label: preferredAgentType,
36315
- createClient: createKiroAcpClient,
36316
- spawnCommandForSession: (sessionMode, _agentConfig) => buildKiroAcpSpawnCommand(command, sessionMode)
36317
- };
36318
- }
36467
+ const provider = getAgentProvider(preferredAgentType);
36468
+ if (!provider?.createClient || provider.defaultCommand.length === 0) return null;
36469
+ const command = [...provider.defaultCommand];
36319
36470
  return {
36320
36471
  command,
36321
- label: preferredAgentType,
36322
- createClient: createClaudeCodeAcpClient,
36323
- spawnCommandForSession: (sessionMode) => buildClaudeCodeAcpSpawnCommand(command, sessionMode)
36472
+ label: provider.type,
36473
+ createClient: provider.createClient,
36474
+ spawnCommandForSession: (sessionMode, agentConfig) => provider.buildSpawnCommand(command, sessionMode, agentConfig)
36324
36475
  };
36325
36476
  }
36477
+ function getAgentTypeDisplayName(agentType) {
36478
+ if (agentType == null || agentType === "") return "Unknown agent";
36479
+ const known = getAgentProvider(agentType)?.displayName;
36480
+ if (known) return known;
36481
+ return agentType.split(/[-_]/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
36482
+ }
36326
36483
 
36327
36484
  // ../bridge/src/agents/acp/session-file-change-path-kind.ts
36328
36485
  import { existsSync as existsSync4, statSync } from "node:fs";
36329
36486
 
36330
36487
  // ../bridge/src/git/git-exec.ts
36331
- import { execFile as execFile2, execFileSync as execFileSync2, spawn as spawn3 } from "node:child_process";
36488
+ import { execFile as execFile2, execFileSync as execFileSync2, spawn as spawn4 } from "node:child_process";
36332
36489
  import { promisify as promisify3 } from "node:util";
36333
36490
 
36334
36491
  // ../bridge/src/git/git-runtime.ts
@@ -37462,7 +37619,7 @@ async function ensureAcpClient(options) {
37462
37619
  if (!state.acpStartPromise) {
37463
37620
  let statOk = false;
37464
37621
  try {
37465
- const st = await fs18.promises.stat(targetSessionParentPath);
37622
+ const st = await fs19.promises.stat(targetSessionParentPath);
37466
37623
  statOk = st.isDirectory();
37467
37624
  if (!statOk) {
37468
37625
  state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
@@ -37934,18 +38091,6 @@ function reportPostTurnEnrichment(options) {
37934
38091
  });
37935
38092
  }
37936
38093
 
37937
- // ../bridge/src/agents/acp/clients/cursor/cleanup-session-plans.ts
37938
- import * as fs19 from "node:fs";
37939
- function cleanupSessionPlans(sessionId) {
37940
- const id = typeof sessionId === "string" ? sessionId.trim() : "";
37941
- if (!id) return;
37942
- const dir = getSessionPlansDir(id);
37943
- try {
37944
- fs19.rmSync(dir, { recursive: true, force: true });
37945
- } catch {
37946
- }
37947
- }
37948
-
37949
38094
  // ../bridge/src/agents/acp/prompts/finalize-and-send-prompt-result.ts
37950
38095
  async function finalizeAndSendPromptResult(params) {
37951
38096
  const {
@@ -37962,7 +38107,7 @@ async function finalizeAndSendPromptResult(params) {
37962
38107
  sendSessionUpdate,
37963
38108
  log: log2
37964
38109
  } = params;
37965
- cleanupSessionPlans(sessionId);
38110
+ notifyAgentProvidersPromptTurnFinished(sessionId);
37966
38111
  const planningTodosSubmit = isPlanningSession && plugin?.cloud?.maybeSubmitPlanningTodos ? await plugin.cloud.maybeSubmitPlanningTodos({
37967
38112
  sessionId,
37968
38113
  runId,
@@ -38506,7 +38651,7 @@ function decryptCliE2eeMessageField(payload, field, e2ee) {
38506
38651
  }
38507
38652
 
38508
38653
  // ../bridge/src/e2ee/key-command.ts
38509
- import * as readline2 from "node:readline";
38654
+ import * as readline3 from "node:readline";
38510
38655
  function installE2eCertificateKeyCommand({
38511
38656
  log: log2,
38512
38657
  onOpenCertificate,
@@ -38517,7 +38662,7 @@ function installE2eCertificateKeyCommand({
38517
38662
  return () => {
38518
38663
  };
38519
38664
  }
38520
- readline2.emitKeypressEvents(process.stdin);
38665
+ readline3.emitKeypressEvents(process.stdin);
38521
38666
  process.stdin.setRawMode(true);
38522
38667
  process.stdin.resume();
38523
38668
  const onKeypress = (str, key) => {
@@ -38692,25 +38837,20 @@ import * as path35 from "node:path";
38692
38837
  // ../bridge/src/agents/detect-local-agent-types.ts
38693
38838
  init_yield_to_event_loop();
38694
38839
  init_cli_process_interrupt();
38695
- var LOCAL_AGENT_ACP_MODULES = [
38696
- cursor_acp_client_exports,
38697
- codex_acp_client_exports,
38698
- kiro_acp_client_exports,
38699
- claude_code_acp_client_exports
38700
- ];
38701
38840
  async function detectLocalAgentTypes() {
38702
38841
  try {
38703
38842
  if (isCliImmediateShutdownRequested()) return [];
38843
+ const providers = listAutoDetectAgentProviders();
38704
38844
  const out = [];
38705
- for (let i = 0; i < LOCAL_AGENT_ACP_MODULES.length; i++) {
38845
+ for (let i = 0; i < providers.length; i++) {
38706
38846
  if (isCliImmediateShutdownRequested()) return out;
38707
38847
  if (i > 0) {
38708
38848
  await yieldToEventLoop();
38709
38849
  if (isCliImmediateShutdownRequested()) return out;
38710
38850
  }
38711
- const mod = LOCAL_AGENT_ACP_MODULES[i];
38851
+ const provider = providers[i];
38712
38852
  try {
38713
- if (await mod.detectLocalAgentPresence()) out.push(mod.BACKEND_LOCAL_AGENT_TYPE);
38853
+ if (await provider.detectPresence?.()) out.push(provider.type);
38714
38854
  } catch {
38715
38855
  }
38716
38856
  }
@@ -39705,7 +39845,7 @@ function pipedStdoutStderrFor(attemptStdio) {
39705
39845
  }
39706
39846
 
39707
39847
  // ../bridge/src/preview-environments/manager/shell-spawn/try-spawn-piped-via-sh.ts
39708
- import { spawn as spawn4 } from "node:child_process";
39848
+ import { spawn as spawn5 } from "node:child_process";
39709
39849
  function trySpawnPipedViaSh(command, env, cwd, signal, lifecycle) {
39710
39850
  const attempts = [
39711
39851
  { stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
@@ -39729,9 +39869,9 @@ function trySpawnPipedViaSh(command, env, cwd, signal, lifecycle) {
39729
39869
  if (process.platform === "win32") {
39730
39870
  opts.windowsHide = true;
39731
39871
  const com = process.env.ComSpec || "cmd.exe";
39732
- proc = spawn4(com, ["/d", "/s", "/c", command], opts);
39872
+ proc = spawn5(com, ["/d", "/s", "/c", command], opts);
39733
39873
  } else {
39734
- proc = spawn4("/bin/sh", ["-c", command], opts);
39874
+ proc = spawn5("/bin/sh", ["-c", command], opts);
39735
39875
  }
39736
39876
  if (attempt.endStdin) {
39737
39877
  proc.stdin?.end();
@@ -39751,7 +39891,7 @@ function trySpawnPipedViaSh(command, env, cwd, signal, lifecycle) {
39751
39891
  }
39752
39892
 
39753
39893
  // ../bridge/src/preview-environments/manager/shell-spawn/try-spawn-shell-true-piped.ts
39754
- import { spawn as spawn5 } from "node:child_process";
39894
+ import { spawn as spawn6 } from "node:child_process";
39755
39895
  function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal, lifecycle) {
39756
39896
  try {
39757
39897
  const opts = mergePreviewEnvironmentSpawnOptions(
@@ -39767,7 +39907,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal, lifecycle)
39767
39907
  if (process.platform === "win32") {
39768
39908
  opts.windowsHide = true;
39769
39909
  }
39770
- return spawn5(command, opts);
39910
+ return spawn6(command, opts);
39771
39911
  } catch (e) {
39772
39912
  if (isSpawnEbadf(e)) return null;
39773
39913
  throw e;
@@ -39775,7 +39915,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal, lifecycle)
39775
39915
  }
39776
39916
 
39777
39917
  // ../bridge/src/preview-environments/manager/shell-spawn/try-spawn-merged-log-file.ts
39778
- import { spawn as spawn6 } from "node:child_process";
39918
+ import { spawn as spawn7 } from "node:child_process";
39779
39919
  import fs23 from "node:fs";
39780
39920
  import { tmpdir } from "node:os";
39781
39921
  import path36 from "node:path";
@@ -39793,7 +39933,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal, lifecycle) {
39793
39933
  try {
39794
39934
  let proc;
39795
39935
  if (process.platform === "win32") {
39796
- proc = spawn6(
39936
+ proc = spawn7(
39797
39937
  process.env.ComSpec || "cmd.exe",
39798
39938
  ["/d", "/s", "/c", command],
39799
39939
  mergePreviewEnvironmentSpawnOptions(
@@ -39802,7 +39942,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal, lifecycle) {
39802
39942
  )
39803
39943
  );
39804
39944
  } else {
39805
- proc = spawn6(
39945
+ proc = spawn7(
39806
39946
  "/bin/sh",
39807
39947
  ["-c", command],
39808
39948
  mergePreviewEnvironmentSpawnOptions({ env, cwd, stdio, ...signal ? { signal } : {} }, lifecycle)
@@ -39827,7 +39967,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal, lifecycle) {
39827
39967
  }
39828
39968
 
39829
39969
  // ../bridge/src/preview-environments/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
39830
- import { spawn as spawn7 } from "node:child_process";
39970
+ import { spawn as spawn8 } from "node:child_process";
39831
39971
  import fs24 from "node:fs";
39832
39972
  import { tmpdir as tmpdir2 } from "node:os";
39833
39973
  import path37 from "node:path";
@@ -39850,7 +39990,7 @@ cd ${shSingleQuote(cwd)}
39850
39990
  /bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
39851
39991
  `
39852
39992
  );
39853
- const proc = spawn7(
39993
+ const proc = spawn8(
39854
39994
  "/bin/sh",
39855
39995
  [runnerPath],
39856
39996
  mergePreviewEnvironmentSpawnOptions(
@@ -39884,7 +40024,7 @@ CD /D ${q(cwd)}\r
39884
40024
  ${command} >> ${q(logPath)} 2>&1\r
39885
40025
  `
39886
40026
  );
39887
- const proc = spawn7(
40027
+ const proc = spawn8(
39888
40028
  com,
39889
40029
  ["/d", "/s", "/c", q(runnerPath)],
39890
40030
  mergePreviewEnvironmentSpawnOptions(
@@ -39906,7 +40046,7 @@ ${command} >> ${q(logPath)} 2>&1\r
39906
40046
  }
39907
40047
 
39908
40048
  // ../bridge/src/preview-environments/manager/shell-spawn/try-spawn-inherit.ts
39909
- import { spawn as spawn8 } from "node:child_process";
40049
+ import { spawn as spawn9 } from "node:child_process";
39910
40050
  function trySpawnInheritStdio(command, env, cwd, signal, lifecycle) {
39911
40051
  const opts = mergePreviewEnvironmentSpawnOptions(
39912
40052
  {
@@ -39921,9 +40061,9 @@ function trySpawnInheritStdio(command, env, cwd, signal, lifecycle) {
39921
40061
  if (process.platform === "win32") {
39922
40062
  opts.windowsHide = true;
39923
40063
  const com = process.env.ComSpec || "cmd.exe";
39924
- proc = spawn8(com, ["/d", "/s", "/c", command], opts);
40064
+ proc = spawn9(com, ["/d", "/s", "/c", command], opts);
39925
40065
  } else {
39926
- proc = spawn8("/bin/sh", ["-c", command], opts);
40066
+ proc = spawn9("/bin/sh", ["-c", command], opts);
39927
40067
  }
39928
40068
  return { proc, pipedStdoutStderr: false };
39929
40069
  }
@@ -40398,7 +40538,7 @@ function createBridgeHeartbeatController(params) {
40398
40538
  }
40399
40539
 
40400
40540
  // ../bridge/src/cli/cli-version.ts
40401
- var CLI_VERSION = "0.1.90".length > 0 ? "0.1.90" : "0.0.0-dev";
40541
+ var CLI_VERSION = "0.1.92".length > 0 ? "0.1.92" : "0.0.0-dev";
40402
40542
 
40403
40543
  // ../bridge/src/connection/identify/send-bridge-identify.ts
40404
40544
  function sendBridgeIdentify(ws, params) {
@@ -40674,7 +40814,7 @@ var import_debug = __toESM(require_src(), 1);
40674
40814
  var import_promise_deferred = __toESM(require_dist2(), 1);
40675
40815
  var import_promise_deferred2 = __toESM(require_dist2(), 1);
40676
40816
  import { Buffer as Buffer2 } from "node:buffer";
40677
- import { spawn as spawn9 } from "child_process";
40817
+ import { spawn as spawn10 } from "child_process";
40678
40818
  import { normalize as normalize3 } from "node:path";
40679
40819
  import { EventEmitter } from "node:events";
40680
40820
  var __defProp2 = Object.defineProperty;
@@ -42062,7 +42202,7 @@ var init_git_executor_chain = __esm2({
42062
42202
  rejection = reason || rejection;
42063
42203
  }
42064
42204
  });
42065
- const spawned = spawn9(command, args, spawnOptions);
42205
+ const spawned = spawn10(command, args, spawnOptions);
42066
42206
  spawned.stdout.on(
42067
42207
  "data",
42068
42208
  onDataReceived(stdOut, "stdOut", logger, outputLogger.step("stdOut"))
@@ -45665,7 +45805,7 @@ import * as path42 from "node:path";
45665
45805
 
45666
45806
  // ../bridge/src/git/changes/lines/count-lines.ts
45667
45807
  import { createReadStream } from "node:fs";
45668
- import * as readline3 from "node:readline";
45808
+ import * as readline4 from "node:readline";
45669
45809
  function countLinesInText(text) {
45670
45810
  return splitTextIntoDiffLines(text).length;
45671
45811
  }
@@ -45674,7 +45814,7 @@ async function countTextFileLines(filePath) {
45674
45814
  const maxBytes = 512e3;
45675
45815
  let lines = 0;
45676
45816
  const stream = createReadStream(filePath, { encoding: "utf8" });
45677
- const rl = readline3.createInterface({ input: stream, crlfDelay: Infinity });
45817
+ const rl = readline4.createInterface({ input: stream, crlfDelay: Infinity });
45678
45818
  for await (const _line of rl) {
45679
45819
  lines += 1;
45680
45820
  bytes += Buffer.byteLength(String(_line), "utf8") + 1;
@@ -50326,159 +50466,24 @@ var handleInstalledAgentAuthSync = (msg) => {
50326
50466
  );
50327
50467
  };
50328
50468
 
50329
- // ../bridge/src/agents/install/run-streaming-command.ts
50330
- import { spawn as spawn10 } from "node:child_process";
50331
- import * as readline4 from "node:readline";
50332
- function runStreamingCommand(command, args, options) {
50333
- return new Promise((resolve36, reject) => {
50334
- const child = spawn10(command, args, {
50335
- env: options.env,
50336
- stdio: ["ignore", "pipe", "pipe"]
50337
- });
50338
- let settled = false;
50339
- const timer = options.timeoutMs != null ? setTimeout(() => {
50340
- child.kill("SIGKILL");
50341
- if (!settled) {
50342
- settled = true;
50343
- reject(new Error(`Command timed out after ${options.timeoutMs}ms`));
50344
- }
50345
- }, options.timeoutMs) : null;
50346
- const onLine = (line) => {
50347
- if (line.length > 0) options.onLine?.(line);
50348
- };
50349
- if (child.stdout) {
50350
- readline4.createInterface({ input: child.stdout, crlfDelay: Infinity }).on("line", onLine);
50351
- }
50352
- if (child.stderr) {
50353
- readline4.createInterface({ input: child.stderr, crlfDelay: Infinity }).on("line", onLine);
50354
- }
50355
- child.on("error", (err) => {
50356
- if (timer) clearTimeout(timer);
50357
- if (!settled) {
50358
- settled = true;
50359
- reject(err);
50360
- }
50361
- });
50362
- child.on("close", (code, signal) => {
50363
- if (timer) clearTimeout(timer);
50364
- if (!settled) {
50365
- settled = true;
50366
- resolve36({ code, signal });
50367
- }
50368
- });
50369
- });
50370
- }
50371
-
50372
- // ../bridge/src/agents/install/commands/run-npm-global-install.ts
50373
- async function runNpmGlobalInstall(packageName, env, options) {
50374
- const result = await runStreamingCommand("npm", ["install", "-g", packageName], {
50375
- env: bridgeAgentPathEnv(env),
50376
- timeoutMs: options?.timeoutMs ?? 3e5,
50377
- onLine: options?.onLine
50378
- });
50379
- if (result.code !== 0) {
50380
- throw new Error(`npm install -g ${packageName} failed (exit ${result.code ?? "signal"})`);
50381
- }
50382
- }
50383
-
50384
- // ../bridge/src/agents/install/commands/claude-code.ts
50385
- var claudeCodeInstallCommand = {
50386
- agentType: "claude-code",
50387
- detectCommand: "claude",
50388
- async install(ctx) {
50389
- ctx.onProgress?.("Installing Anthropic Claude Code");
50390
- await runNpmGlobalInstall(
50391
- "@anthropic-ai/claude-code",
50392
- { ...ctx.env, ANTHROPIC_API_KEY: ctx.authToken },
50393
- { onLine: (line) => ctx.onProgress?.("Installing Anthropic Claude Code", line) }
50394
- );
50395
- }
50396
- };
50397
-
50398
- // ../bridge/src/agents/install/commands/codex-acp.ts
50399
- var codexAcpInstallCommand = {
50400
- agentType: "codex-acp",
50401
- detectCommand: "codex",
50402
- async install(ctx) {
50403
- ctx.onProgress?.("Installing Codex");
50404
- await runNpmGlobalInstall(
50405
- "@openai/codex",
50406
- { ...ctx.env, OPENAI_API_KEY: ctx.authToken },
50407
- { onLine: (line) => ctx.onProgress?.("Installing Codex", line) }
50408
- );
50409
- }
50410
- };
50411
-
50412
- // ../bridge/src/agents/install/commands/cursor-cli.ts
50413
- var cursorCliInstallCommand = {
50414
- agentType: "cursor-cli",
50415
- detectCommand: "agent",
50416
- alternateDetectCommands: ["cursor-agent"],
50417
- async install(ctx) {
50418
- ctx.onProgress?.("Installing Cursor CLI");
50419
- const result = await runStreamingCommand(
50420
- "bash",
50421
- ["-lc", "curl -fsSL https://cursor.com/install | bash"],
50422
- {
50423
- timeoutMs: 3e5,
50424
- env: { ...bridgeAgentPathEnv(ctx.env), CURSOR_API_KEY: ctx.authToken },
50425
- onLine: (line) => ctx.onProgress?.("Installing Cursor CLI", line)
50426
- }
50427
- );
50428
- if (result.code !== 0) {
50429
- throw new Error(`Cursor CLI install failed (exit ${result.code ?? "signal"})`);
50430
- }
50431
- }
50432
- };
50433
-
50434
- // ../bridge/src/agents/install/commands/opencode.ts
50435
- var opencodeInstallCommand = {
50436
- agentType: "opencode",
50437
- detectCommand: "opencode",
50438
- async install(ctx) {
50439
- ctx.onProgress?.("Installing OpenCode");
50440
- await runNpmGlobalInstall(
50441
- "opencode-ai",
50442
- { ...ctx.env, OPENCODE_API_KEY: ctx.authToken },
50443
- { onLine: (line) => ctx.onProgress?.("Installing OpenCode", line) }
50444
- );
50445
- }
50446
- };
50447
-
50448
- // ../bridge/src/agents/install/commands/index.ts
50449
- var COMMANDS = [
50450
- claudeCodeInstallCommand,
50451
- codexAcpInstallCommand,
50452
- cursorCliInstallCommand,
50453
- opencodeInstallCommand
50454
- ];
50455
- var byType = new Map(COMMANDS.map((c) => [c.agentType, c]));
50456
- function getAgentInstallCommand(agentType) {
50457
- return byType.get(agentType);
50458
- }
50459
-
50460
50469
  // ../bridge/src/agents/install/install-local-agent.ts
50461
50470
  async function installLocalAgentOnBridge(params) {
50462
- const spec = INSTALLABLE_BRIDGE_AGENTS.find((a) => a.value === params.agentType);
50463
- if (!spec) return { success: false, error: `Unsupported agent type: ${params.agentType}` };
50464
- const command = getAgentInstallCommand(params.agentType);
50465
- if (!command) return { success: false, error: `No install command for ${params.agentType}` };
50466
- params.onProgress?.(`Configuring ${spec.label} credentials`);
50471
+ const provider = getAgentProvider(params.agentType);
50472
+ const install = provider?.install;
50473
+ if (!provider || !install) return { success: false, error: `Unsupported agent type: ${params.agentType}` };
50474
+ params.onProgress?.(`Configuring ${provider.displayName} credentials`);
50467
50475
  try {
50468
- await command.install({
50476
+ await install.run({
50469
50477
  authToken: params.authToken,
50470
50478
  onProgress: params.onProgress,
50471
- env: { ...process.env, [spec.tokenEnvVar]: params.authToken }
50479
+ env: { ...process.env, [install.tokenEnvVar]: params.authToken }
50472
50480
  });
50473
50481
  } catch (e) {
50474
50482
  const msg = e instanceof Error ? e.message : String(e);
50475
50483
  return { success: false, error: msg };
50476
50484
  }
50477
50485
  ensureBridgeAgentPathInProcessEnv();
50478
- const detectNames = [
50479
- command.detectCommand,
50480
- ...command.alternateDetectCommands ?? []
50481
- ];
50486
+ const detectNames = [install.detectCommand, ...install.alternateDetectCommands ?? []];
50482
50487
  let found = false;
50483
50488
  for (const name of detectNames) {
50484
50489
  if (await waitForCommandOnPath(name)) {
@@ -50487,7 +50492,7 @@ async function installLocalAgentOnBridge(params) {
50487
50492
  }
50488
50493
  }
50489
50494
  if (!found) {
50490
- return { success: false, error: `${command.detectCommand} not found on PATH after install` };
50495
+ return { success: false, error: `${install.detectCommand} not found on PATH after install` };
50491
50496
  }
50492
50497
  return { success: true };
50493
50498
  }
@@ -51645,7 +51650,7 @@ var handleRenameSessionBranchMessage = (msg, deps) => {
51645
51650
  var handleSessionArchivedMessage = (msg, deps) => {
51646
51651
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
51647
51652
  if (!sessionId) return;
51648
- cleanupSessionPlans(sessionId);
51653
+ notifyAgentProvidersSessionClosed(sessionId);
51649
51654
  void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
51650
51655
  };
51651
51656
 
@@ -51653,7 +51658,7 @@ var handleSessionArchivedMessage = (msg, deps) => {
51653
51658
  var handleSessionDiscardedMessage = (msg, deps) => {
51654
51659
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
51655
51660
  if (!sessionId) return;
51656
- cleanupSessionPlans(sessionId);
51661
+ notifyAgentProvidersSessionClosed(sessionId);
51657
51662
  void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
51658
51663
  };
51659
51664
 
@@ -52332,12 +52337,12 @@ function createMainBridgeAuthRefreshHandler(params, connect) {
52332
52337
  // ../bridge/src/connection/ws/main-bridge-ws-close-handler.ts
52333
52338
  function createMainBridgeCloseHandler(params, connect) {
52334
52339
  const { state, logFn, bridgeHeartbeat } = params;
52335
- return (code, reason) => {
52340
+ return (code, reason, closedWs) => {
52341
+ if (state.currentWs !== closedWs) return;
52336
52342
  bridgeHeartbeat?.stop();
52337
52343
  try {
52338
- const was = state.currentWs;
52339
52344
  state.currentWs = null;
52340
- if (was) was.removeAllListeners();
52345
+ closedWs.removeAllListeners();
52341
52346
  const willReconnect = !state.closedByUser;
52342
52347
  if (willReconnect) {
52343
52348
  const duplicateResult = evaluateDuplicateBridgeDisconnect(
@@ -53458,7 +53463,7 @@ function createPendingAuthOnMessage(params) {
53458
53463
  }
53459
53464
 
53460
53465
  // src/cli-version.ts
53461
- var CLI_VERSION2 = "0.1.90".length > 0 ? "0.1.90" : "0.0.0-dev";
53466
+ var CLI_VERSION2 = "0.1.92".length > 0 ? "0.1.92" : "0.0.0-dev";
53462
53467
 
53463
53468
  // src/auth/pending/pending-auth-on-open.ts
53464
53469
  function createPendingAuthOnOpen(params) {