@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/cli.js CHANGED
@@ -24125,7 +24125,6 @@ function isCliSqliteLockError(e) {
24125
24125
  var CliSqliteInterrupted;
24126
24126
  var init_sqlite_errors = __esm({
24127
24127
  "../bridge/src/sqlite/sqlite-errors.ts"() {
24128
- "use strict";
24129
24128
  CliSqliteInterrupted = class extends Error {
24130
24129
  name = "CliSqliteInterrupted";
24131
24130
  constructor() {
@@ -30449,7 +30448,7 @@ var {
30449
30448
  } = import_index.default;
30450
30449
 
30451
30450
  // src/cli-version.ts
30452
- var CLI_VERSION = "0.1.90".length > 0 ? "0.1.90" : "0.0.0-dev";
30451
+ var CLI_VERSION = "0.1.92".length > 0 ? "0.1.92" : "0.0.0-dev";
30453
30452
 
30454
30453
  // src/cli/defaults.ts
30455
30454
  var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
@@ -30605,6 +30604,7 @@ function createWsBridge(options) {
30605
30604
  });
30606
30605
  ws.on("open", () => {
30607
30606
  disposeClientPing();
30607
+ if (isActiveSocket && !isActiveSocket(ws)) return;
30608
30608
  if (clientPingIntervalMs != null && clientPingIntervalMs > 0) {
30609
30609
  clearClientPing = attachWebSocketClientPing(ws, clientPingIntervalMs);
30610
30610
  }
@@ -30624,10 +30624,12 @@ function createWsBridge(options) {
30624
30624
  });
30625
30625
  ws.on("close", (code, reason) => {
30626
30626
  disposeClientPing();
30627
- onClose?.(code, reason.toString());
30627
+ if (isActiveSocket && !isActiveSocket(ws)) return;
30628
+ onClose?.(code, reason.toString(), ws);
30628
30629
  });
30629
30630
  ws.on("error", (err) => {
30630
30631
  disposeClientPing();
30632
+ if (isActiveSocket && !isActiveSocket(ws)) return;
30631
30633
  onError2?.(err);
30632
30634
  });
30633
30635
  return ws;
@@ -30732,7 +30734,7 @@ function formatSpawnError(err, command) {
30732
30734
  }
30733
30735
 
30734
30736
  // ../bridge/src/agents/acp/clients/kill-process-tree.ts
30735
- var import_tree_kill = __toESM(require_tree_kill());
30737
+ var import_tree_kill = __toESM(require_tree_kill(), 1);
30736
30738
  import { promisify } from "node:util";
30737
30739
  var treeKillAsync = promisify(import_tree_kill.default);
30738
30740
  var ACP_PROCESS_TREE_KILL_GRACE_MS = 2500;
@@ -30801,29 +30803,50 @@ function forceAcpSubprocessDisconnect(child) {
30801
30803
  killChildProcessTree(child, "SIGKILL");
30802
30804
  }
30803
30805
 
30804
- // ../bridge/src/agents/local-agent-auth.ts
30805
- var LOCAL_AGENT_AUTH_ERROR_HINTS = {
30806
- "kiro-acp": [/not logged in/i, /kiro-cli\s+login/i, /log in with kiro-cli/i],
30807
- "cursor-cli": [/cursor_login/i, /authenticate.*cursor/i, /not logged in.*cursor/i, /run:\s*agent\s+login/i],
30808
- "codex-acp": [
30809
- /authentication failed/i,
30810
- /not authenticated/i,
30811
- /invalid.*api key/i,
30812
- /sign in.*openai/i,
30813
- /login.*openai/i,
30814
- /unauthorized/i
30815
- ],
30816
- "claude-code": [
30817
- /ANTHROPIC_API_KEY/i,
30818
- /not authenticated/i,
30819
- /authentication failed/i,
30820
- /claude\s+login/i,
30821
- /please run.*claude.*login/i
30822
- ]
30806
+ // ../bridge/src/agents/providers/claude-code/auth.ts
30807
+ var claudeCodeAuthErrorHints = [
30808
+ /ANTHROPIC_API_KEY/i,
30809
+ /not authenticated/i,
30810
+ /authentication failed/i,
30811
+ /claude\s+login/i,
30812
+ /please run.*claude.*login/i
30813
+ ];
30814
+
30815
+ // ../bridge/src/agents/providers/codex/auth.ts
30816
+ var codexAuthErrorHints = [
30817
+ /authentication failed/i,
30818
+ /not authenticated/i,
30819
+ /invalid.*api key/i,
30820
+ /sign in.*openai/i,
30821
+ /login.*openai/i,
30822
+ /unauthorized/i
30823
+ ];
30824
+
30825
+ // ../bridge/src/agents/providers/cursor/auth.ts
30826
+ var cursorAuthErrorHints = [
30827
+ /cursor_login/i,
30828
+ /authenticate.*cursor/i,
30829
+ /not logged in.*cursor/i,
30830
+ /run:\s*agent\s+login/i
30831
+ ];
30832
+
30833
+ // ../bridge/src/agents/providers/kiro/auth.ts
30834
+ var kiroAuthErrorHints = [
30835
+ /not logged in/i,
30836
+ /kiro-cli\s+login/i,
30837
+ /log in with kiro-cli/i
30838
+ ];
30839
+
30840
+ // ../bridge/src/agents/providers/auth.ts
30841
+ var AUTH_ERROR_HINTS = {
30842
+ "claude-code": claudeCodeAuthErrorHints,
30843
+ "codex-acp": codexAuthErrorHints,
30844
+ "cursor-cli": cursorAuthErrorHints,
30845
+ "kiro-acp": kiroAuthErrorHints
30823
30846
  };
30824
30847
  function localAgentErrorSuggestsAuth(agentType, errorText) {
30825
30848
  if (agentType == null || agentType === "" || errorText == null || !String(errorText).trim()) return false;
30826
- const hints = LOCAL_AGENT_AUTH_ERROR_HINTS[agentType];
30849
+ const hints = AUTH_ERROR_HINTS[agentType];
30827
30850
  if (!hints?.length) return false;
30828
30851
  return hints.some((re) => re.test(String(errorText)));
30829
30852
  }
@@ -36016,34 +36039,11 @@ function flattenSdkSessionNotificationParams(params) {
36016
36039
  return { sessionId: params.sessionId, ...params.update };
36017
36040
  }
36018
36041
 
36019
- // ../bridge/src/agents/acp/clients/kiro-sdk-ext-notifications.ts
36020
- function createKiroSdkExtNotificationHandler(options) {
36021
- const { onSessionUpdate } = options;
36022
- return async (method, params) => {
36023
- if (method === "_kiro.dev/metadata") {
36024
- const p = params && typeof params === "object" ? params : {};
36025
- const pct = p.contextUsagePercentage;
36026
- if (typeof pct !== "number" || !Number.isFinite(pct) || !onSessionUpdate) return;
36027
- onSessionUpdate({
36028
- sessionUpdate: "context_usage",
36029
- contextUsagePercentage: pct
36030
- });
36031
- return;
36032
- }
36033
- };
36034
- }
36035
-
36036
36042
  // ../bridge/src/agents/acp/clients/sdk/sdk-stdio-ext-notifications.ts
36037
36043
  var noopExtNotification = async () => {
36038
36044
  };
36039
- function createSdkStdioExtNotificationHandler(options) {
36040
- const { backendAgentType, onSessionUpdate } = options;
36041
- switch (backendAgentType) {
36042
- case "kiro-acp":
36043
- return createKiroSdkExtNotificationHandler({ onSessionUpdate });
36044
- default:
36045
- return noopExtNotification;
36046
- }
36045
+ function createSdkStdioExtNotificationHandler(_options) {
36046
+ return noopExtNotification;
36047
36047
  }
36048
36048
 
36049
36049
  // ../bridge/src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
@@ -36077,11 +36077,8 @@ function resolvePendingSdkStdioPermissionCancellations(pending2) {
36077
36077
 
36078
36078
  // ../bridge/src/agents/acp/clients/sdk/sdk-stdio-connection-client.ts
36079
36079
  function createSdkStdioConnectionClient(deps) {
36080
- const { backendAgentType, onSessionUpdate, onRequest, sessionCtx, pendingPermissionReplies } = deps;
36081
- const extNotification = createSdkStdioExtNotificationHandler({
36082
- backendAgentType,
36083
- onSessionUpdate
36084
- });
36080
+ const { onSessionUpdate, onRequest, sessionCtx, pendingPermissionReplies, createExtNotificationHandler } = deps;
36081
+ const extNotification = createExtNotificationHandler?.({ onSessionUpdate }) ?? createSdkStdioExtNotificationHandler({ onSessionUpdate });
36085
36082
  let permissionSeq = 0;
36086
36083
  return (_agent) => ({
36087
36084
  async requestPermission(params) {
@@ -36193,121 +36190,16 @@ function createSdkStdioSessionContext(options) {
36193
36190
  onAcpSessionEstablished: options.onAcpSessionEstablished,
36194
36191
  onAcpConfigOptionsUpdated: options.onAcpConfigOptionsUpdated,
36195
36192
  logDebug,
36196
- getStderrText: () => options.stderrCapture.getText()
36193
+ getStderrText: () => options.stderrCapture.getText(),
36194
+ afterSessionEstablished: options.afterSessionEstablished
36197
36195
  };
36198
36196
  }
36199
36197
 
36200
36198
  // ../bridge/src/agents/acp/clients/sdk/sdk-stdio-bootstrap-connection.ts
36201
36199
  import { Readable, Writable } from "node:stream";
36202
36200
 
36203
- // ../bridge/src/agents/acp/claude-acp-permission-from-session.ts
36204
- function flattenSelectOptions(options) {
36205
- if (options == null || options.length === 0) return [];
36206
- const first2 = options[0];
36207
- if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
36208
- return options.flatMap(
36209
- (g) => Array.isArray(g.options) ? g.options : []
36210
- );
36211
- }
36212
- return options;
36213
- }
36214
- function pickModeConfigOption(configOptions) {
36215
- if (configOptions == null || configOptions.length === 0) return null;
36216
- const byCategory = configOptions.find((o) => o.category === "mode");
36217
- if (byCategory) return byCategory;
36218
- return configOptions.find((o) => o.id === "mode") ?? null;
36219
- }
36220
- async function applyClaudePermissionFromAcpSession(params) {
36221
- const { sessionId, agentConfig, configOptions, modes, setSessionConfigOption, setSessionMode, logDebug: logDebug2 } = params;
36222
- const desiredMode = getClaudePermissionModeFromAgentConfig(agentConfig);
36223
- if (desiredMode == null) return;
36224
- const modeOpt = pickModeConfigOption(configOptions ?? null);
36225
- if (modeOpt != null) {
36226
- const flat = flattenSelectOptions(modeOpt.options);
36227
- const allowed = flat.some((o) => o.value === desiredMode);
36228
- if (allowed && modeOpt.currentValue !== desiredMode) {
36229
- try {
36230
- logDebug2(
36231
- `[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`
36232
- );
36233
- await setSessionConfigOption({ sessionId, configId: modeOpt.id, value: desiredMode });
36234
- } catch (e) {
36235
- logDebug2(
36236
- `[Agent] Claude Code: session/set_config_option failed: ${e instanceof Error ? e.message : String(e)}`
36237
- );
36238
- }
36239
- }
36240
- return;
36241
- }
36242
- if (modes?.availableModes?.length) {
36243
- const allowed = modes.availableModes.some((m) => m.id === desiredMode);
36244
- if (allowed && desiredMode !== modes.currentModeId) {
36245
- try {
36246
- logDebug2(
36247
- `[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`
36248
- );
36249
- await setSessionMode({ sessionId, modeId: desiredMode });
36250
- } catch (e) {
36251
- logDebug2(`[Agent] Claude Code: session/set_mode failed: ${e instanceof Error ? e.message : String(e)}`);
36252
- }
36253
- }
36254
- }
36255
- }
36256
-
36257
- // ../bridge/src/agents/acp/codex-acp-permission-from-session.ts
36258
- function flattenSelectOptions2(options) {
36259
- if (options == null || options.length === 0) return [];
36260
- const first2 = options[0];
36261
- if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
36262
- return options.flatMap(
36263
- (g) => Array.isArray(g.options) ? g.options : []
36264
- );
36265
- }
36266
- return options;
36267
- }
36268
- function pickModeConfigOption2(configOptions) {
36269
- if (configOptions == null || configOptions.length === 0) return null;
36270
- const byCategory = configOptions.find((o) => o.category === "mode");
36271
- if (byCategory) return byCategory;
36272
- return configOptions.find((o) => o.id === "mode") ?? null;
36273
- }
36274
- async function applyCodexPermissionFromAcpSession(params) {
36275
- const { sessionId, agentConfig, configOptions, modes, setSessionConfigOption, setSessionMode, logDebug: logDebug2 } = params;
36276
- const desiredMode = getCodexPermissionModeFromAgentConfig(agentConfig);
36277
- if (desiredMode == null) return;
36278
- const modeOpt = pickModeConfigOption2(configOptions ?? null);
36279
- if (modeOpt != null) {
36280
- const flat = flattenSelectOptions2(modeOpt.options);
36281
- const allowed = flat.some((o) => o.value === desiredMode);
36282
- if (allowed && modeOpt.currentValue !== desiredMode) {
36283
- try {
36284
- logDebug2(
36285
- `[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`
36286
- );
36287
- await setSessionConfigOption({ sessionId, configId: modeOpt.id, value: desiredMode });
36288
- } catch (e) {
36289
- logDebug2(`[Agent] Codex: session/set_config_option failed: ${e instanceof Error ? e.message : String(e)}`);
36290
- }
36291
- }
36292
- return;
36293
- }
36294
- if (modes?.availableModes?.length) {
36295
- const allowed = modes.availableModes.some((m) => m.id === desiredMode);
36296
- if (allowed && desiredMode !== modes.currentModeId) {
36297
- try {
36298
- logDebug2(
36299
- `[Agent] Codex: sending ACP session/set_mode modeId=${JSON.stringify(desiredMode)} was=${JSON.stringify(modes.currentModeId ?? null)} sessionId=${sessionId.slice(0, 8)}\u2026`
36300
- );
36301
- await setSessionMode({ sessionId, modeId: desiredMode });
36302
- } catch (e) {
36303
- logDebug2(`[Agent] Codex: session/set_mode failed: ${e instanceof Error ? e.message : String(e)}`);
36304
- }
36305
- }
36306
- }
36307
- }
36308
-
36309
36201
  // ../bridge/src/agents/acp/apply-acp-model-from-agent-session.ts
36310
- function flattenSelectOptions3(options) {
36202
+ function flattenSelectOptions(options) {
36311
36203
  if (options == null || options.length === 0) return [];
36312
36204
  const first2 = options[0];
36313
36205
  if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
@@ -36332,7 +36224,7 @@ async function applyAcpModelFromAcpSession(params) {
36332
36224
  if (desired == null) return;
36333
36225
  const modelOpt = pickModelConfigOption(configOptions ?? null);
36334
36226
  if (modelOpt == null) return;
36335
- const flat = flattenSelectOptions3(modelOpt.options);
36227
+ const flat = flattenSelectOptions(modelOpt.options);
36336
36228
  const allowed = flat.some((o) => o.value === desired);
36337
36229
  if (!allowed) return;
36338
36230
  if (modelOpt.currentValue === desired) return;
@@ -36454,38 +36346,13 @@ async function bootstrapAcpWireSession(transport, ctx, initializeRequest) {
36454
36346
  configOptions: established.configOptions,
36455
36347
  modes: established.modes
36456
36348
  });
36457
- if (ctx.backendAgentType === "claude-code") {
36458
- const cfg = ctx.agentConfig != null && typeof ctx.agentConfig === "object" && !Array.isArray(ctx.agentConfig) ? ctx.agentConfig : null;
36459
- const configOptionsTyped = established.configOptions;
36460
- const modesTyped = established.modes;
36461
- await applyClaudePermissionFromAcpSession({
36462
- sessionId,
36463
- agentConfig: cfg,
36464
- configOptions: configOptionsForPermission(ctx.getActiveConfigOptions, configOptionsTyped),
36465
- modes: modesTyped,
36466
- setSessionConfigOption: transport.setSessionConfigOption ? (p) => transport.setSessionConfigOption(p) : async () => {
36467
- },
36468
- setSessionMode: transport.setSessionMode ? (p) => transport.setSessionMode(p) : async () => {
36469
- },
36470
- logDebug: ctx.logDebug
36471
- });
36472
- }
36473
- if (ctx.backendAgentType === "codex-acp") {
36474
- const cfg = ctx.agentConfig != null && typeof ctx.agentConfig === "object" && !Array.isArray(ctx.agentConfig) ? ctx.agentConfig : null;
36475
- const configOptionsTyped = established.configOptions;
36476
- const modesTyped = established.modes;
36477
- await applyCodexPermissionFromAcpSession({
36478
- sessionId,
36479
- agentConfig: cfg,
36480
- configOptions: configOptionsForPermission(ctx.getActiveConfigOptions, configOptionsTyped),
36481
- modes: modesTyped,
36482
- setSessionConfigOption: transport.setSessionConfigOption ? (p) => transport.setSessionConfigOption(p) : async () => {
36483
- },
36484
- setSessionMode: transport.setSessionMode ? (p) => transport.setSessionMode(p) : async () => {
36485
- },
36486
- logDebug: ctx.logDebug
36487
- });
36488
- }
36349
+ await ctx.afterSessionEstablished?.({
36350
+ sessionId,
36351
+ transport,
36352
+ ctx,
36353
+ configOptions: established.configOptions,
36354
+ modes: established.modes
36355
+ });
36489
36356
  const cfgAll = ctx.agentConfig != null && typeof ctx.agentConfig === "object" && !Array.isArray(ctx.agentConfig) ? ctx.agentConfig : null;
36490
36357
  const configOptionsForModel = established.configOptions;
36491
36358
  if (transport.setSessionConfigOption) {
@@ -36538,7 +36405,8 @@ async function bootstrapSdkStdioConnection(options) {
36538
36405
  onSessionUpdate: options.onSessionUpdate,
36539
36406
  onRequest: options.onRequest,
36540
36407
  sessionCtx: options.sessionCtx,
36541
- pendingPermissionReplies: options.pendingPermissionReplies
36408
+ pendingPermissionReplies: options.pendingPermissionReplies,
36409
+ createExtNotificationHandler: options.createExtNotificationHandler
36542
36410
  });
36543
36411
  const connection = new ClientSideConnection2(client, stream);
36544
36412
  connection.signal.addEventListener("abort", () => {
@@ -36638,7 +36506,9 @@ async function createSdkStdioAcpClient(options) {
36638
36506
  persistedAcpSessionId,
36639
36507
  onAcpSessionEstablished,
36640
36508
  onAcpConfigOptionsUpdated,
36641
- getActiveConfigOptions
36509
+ getActiveConfigOptions,
36510
+ afterSessionEstablished,
36511
+ createExtNotificationHandler
36642
36512
  } = options;
36643
36513
  const { child, stderrCapture } = spawnSdkStdioProcess({
36644
36514
  command,
@@ -36655,6 +36525,7 @@ async function createSdkStdioAcpClient(options) {
36655
36525
  onAcpSessionEstablished,
36656
36526
  onAcpConfigOptionsUpdated,
36657
36527
  onFileChange,
36528
+ afterSessionEstablished,
36658
36529
  stderrCapture
36659
36530
  });
36660
36531
  return new Promise((resolve39, reject) => {
@@ -36679,7 +36550,8 @@ async function createSdkStdioAcpClient(options) {
36679
36550
  onSessionUpdate,
36680
36551
  onRequest,
36681
36552
  pendingPermissionReplies,
36682
- protocolVersion: PROTOCOL_VERSION2
36553
+ protocolVersion: PROTOCOL_VERSION2,
36554
+ createExtNotificationHandler
36683
36555
  });
36684
36556
  init.settleResolve(
36685
36557
  resolve39,
@@ -38339,7 +38211,7 @@ async function cancelRun(ctx, runId) {
38339
38211
  }
38340
38212
 
38341
38213
  // ../bridge/src/agents/acp/ensure-acp-client.ts
38342
- import * as fs18 from "node:fs";
38214
+ import * as fs19 from "node:fs";
38343
38215
  import * as path29 from "node:path";
38344
38216
 
38345
38217
  // ../bridge/src/paths/session-layout-paths.ts
@@ -38367,14 +38239,86 @@ function errorMessage(err) {
38367
38239
  return String(err);
38368
38240
  }
38369
38241
 
38370
- // ../bridge/src/agents/acp/clients/claude-code-acp-client.ts
38371
- var claude_code_acp_client_exports = {};
38372
- __export(claude_code_acp_client_exports, {
38373
- BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE,
38374
- buildClaudeCodeAcpSpawnCommand: () => buildClaudeCodeAcpSpawnCommand,
38375
- createClaudeCodeAcpClient: () => createClaudeCodeAcpClient,
38376
- detectLocalAgentPresence: () => detectLocalAgentPresence
38377
- });
38242
+ // ../bridge/src/agents/providers/claude-code/apply-permission.ts
38243
+ function flattenSelectOptions2(options) {
38244
+ if (options == null || options.length === 0) return [];
38245
+ const first2 = options[0];
38246
+ if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
38247
+ return options.flatMap(
38248
+ (g) => Array.isArray(g.options) ? g.options : []
38249
+ );
38250
+ }
38251
+ return options;
38252
+ }
38253
+ function pickModeConfigOption(configOptions) {
38254
+ if (configOptions == null || configOptions.length === 0) return null;
38255
+ const byCategory = configOptions.find((o) => o.category === "mode");
38256
+ if (byCategory) return byCategory;
38257
+ return configOptions.find((o) => o.id === "mode") ?? null;
38258
+ }
38259
+ async function applyClaudePermissionFromAcpSession(params) {
38260
+ const { sessionId, agentConfig, configOptions, modes, setSessionConfigOption, setSessionMode, logDebug: logDebug2 } = params;
38261
+ const desiredMode = getClaudePermissionModeFromAgentConfig(agentConfig);
38262
+ if (desiredMode == null) return;
38263
+ const modeOpt = pickModeConfigOption(configOptions ?? null);
38264
+ if (modeOpt != null) {
38265
+ const flat = flattenSelectOptions2(modeOpt.options);
38266
+ const allowed = flat.some((o) => o.value === desiredMode);
38267
+ if (allowed && modeOpt.currentValue !== desiredMode) {
38268
+ try {
38269
+ logDebug2(
38270
+ `[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`
38271
+ );
38272
+ await setSessionConfigOption({ sessionId, configId: modeOpt.id, value: desiredMode });
38273
+ } catch (e) {
38274
+ logDebug2(
38275
+ `[Agent] Claude Code: session/set_config_option failed: ${e instanceof Error ? e.message : String(e)}`
38276
+ );
38277
+ }
38278
+ }
38279
+ return;
38280
+ }
38281
+ if (modes?.availableModes?.length) {
38282
+ const allowed = modes.availableModes.some((m) => m.id === desiredMode);
38283
+ if (allowed && desiredMode !== modes.currentModeId) {
38284
+ try {
38285
+ logDebug2(
38286
+ `[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`
38287
+ );
38288
+ await setSessionMode({ sessionId, modeId: desiredMode });
38289
+ } catch (e) {
38290
+ logDebug2(`[Agent] Claude Code: session/set_mode failed: ${e instanceof Error ? e.message : String(e)}`);
38291
+ }
38292
+ }
38293
+ }
38294
+ }
38295
+
38296
+ // ../bridge/src/agents/providers/shared/wrap-permission-after-session.ts
38297
+ function wrapPermissionAfterSession(apply) {
38298
+ return async ({ sessionId, transport, ctx, configOptions, modes }) => {
38299
+ const raw = ctx.agentConfig;
38300
+ const agentConfig = raw != null && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
38301
+ await apply({
38302
+ sessionId,
38303
+ agentConfig,
38304
+ configOptions: configOptionsForPermission(
38305
+ ctx.getActiveConfigOptions,
38306
+ configOptions
38307
+ ),
38308
+ modes,
38309
+ setSessionConfigOption: transport.setSessionConfigOption ? (p) => transport.setSessionConfigOption(p) : async () => {
38310
+ },
38311
+ setSessionMode: transport.setSessionMode ? (p) => transport.setSessionMode(p) : async () => {
38312
+ },
38313
+ logDebug: ctx.logDebug
38314
+ });
38315
+ };
38316
+ }
38317
+
38318
+ // ../bridge/src/agents/providers/claude-code/after-session.ts
38319
+ var applyClaudeCodeAfterSessionEstablished = wrapPermissionAfterSession(
38320
+ applyClaudePermissionFromAcpSession
38321
+ );
38378
38322
 
38379
38323
  // ../bridge/src/agents/acp/clients/detect-command-on-path.ts
38380
38324
  init_cli_process_interrupt();
@@ -38446,8 +38390,7 @@ async function execProbeShutdownAware(file2, args, timeoutMs) {
38446
38390
  }
38447
38391
  }
38448
38392
 
38449
- // ../bridge/src/agents/acp/clients/claude-code-acp-client.ts
38450
- var BACKEND_LOCAL_AGENT_TYPE = "claude-code";
38393
+ // ../bridge/src/agents/providers/claude-code/client.ts
38451
38394
  var CLAUDE_ACP_ADAPTER_NPX_ARGS = ["--yes", "@agentclientprotocol/claude-agent-acp"];
38452
38395
  async function detectLocalAgentPresence() {
38453
38396
  return execProbeShutdownAware("npx", [...CLAUDE_ACP_ADAPTER_NPX_ARGS, "--help"], 8e3);
@@ -38466,58 +38409,230 @@ async function createClaudeCodeAcpClient(options) {
38466
38409
  });
38467
38410
  }
38468
38411
 
38469
- // ../bridge/src/agents/acp/clients/codex-acp-client.ts
38470
- var codex_acp_client_exports = {};
38471
- __export(codex_acp_client_exports, {
38472
- BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE2,
38473
- CODEX_ACP_PACKAGE: () => CODEX_ACP_PACKAGE,
38474
- DEFAULT_CODEX_ACP_COMMAND: () => DEFAULT_CODEX_ACP_COMMAND,
38475
- LEGACY_CODEX_ACP_PACKAGE: () => LEGACY_CODEX_ACP_PACKAGE,
38476
- buildCodexAcpSpawnCommand: () => buildCodexAcpSpawnCommand,
38477
- createCodexAcpClient: () => createCodexAcpClient,
38478
- detectLocalAgentPresence: () => detectLocalAgentPresence2,
38479
- isCodexAcpCommand: () => isCodexAcpCommand,
38480
- normalizeCodexAcpCommand: () => normalizeCodexAcpCommand
38481
- });
38482
- var BACKEND_LOCAL_AGENT_TYPE2 = "codex-acp";
38412
+ // ../bridge/src/agents/install/run-streaming-command.ts
38413
+ import { spawn as spawn2 } from "node:child_process";
38414
+ import * as readline from "node:readline";
38415
+ function runStreamingCommand(command, args, options) {
38416
+ return new Promise((resolve39, reject) => {
38417
+ const child = spawn2(command, args, {
38418
+ env: options.env,
38419
+ stdio: ["ignore", "pipe", "pipe"]
38420
+ });
38421
+ let settled = false;
38422
+ const timer = options.timeoutMs != null ? setTimeout(() => {
38423
+ child.kill("SIGKILL");
38424
+ if (!settled) {
38425
+ settled = true;
38426
+ reject(new Error(`Command timed out after ${options.timeoutMs}ms`));
38427
+ }
38428
+ }, options.timeoutMs) : null;
38429
+ const onLine = (line) => {
38430
+ if (line.length > 0) options.onLine?.(line);
38431
+ };
38432
+ if (child.stdout) {
38433
+ readline.createInterface({ input: child.stdout, crlfDelay: Infinity }).on("line", onLine);
38434
+ }
38435
+ if (child.stderr) {
38436
+ readline.createInterface({ input: child.stderr, crlfDelay: Infinity }).on("line", onLine);
38437
+ }
38438
+ child.on("error", (err) => {
38439
+ if (timer) clearTimeout(timer);
38440
+ if (!settled) {
38441
+ settled = true;
38442
+ reject(err);
38443
+ }
38444
+ });
38445
+ child.on("close", (code, signal) => {
38446
+ if (timer) clearTimeout(timer);
38447
+ if (!settled) {
38448
+ settled = true;
38449
+ resolve39({ code, signal });
38450
+ }
38451
+ });
38452
+ });
38453
+ }
38454
+
38455
+ // ../bridge/src/agents/install/commands/run-npm-global-install.ts
38456
+ async function runNpmGlobalInstall(packageName, env, options) {
38457
+ const result = await runStreamingCommand("npm", ["install", "-g", packageName], {
38458
+ env: bridgeAgentPathEnv(env),
38459
+ timeoutMs: options?.timeoutMs ?? 3e5,
38460
+ onLine: options?.onLine
38461
+ });
38462
+ if (result.code !== 0) {
38463
+ throw new Error(`npm install -g ${packageName} failed (exit ${result.code ?? "signal"})`);
38464
+ }
38465
+ }
38466
+
38467
+ // ../bridge/src/agents/providers/claude-code/install.ts
38468
+ var claudeCodeInstall = {
38469
+ detectCommand: "claude",
38470
+ tokenEnvVar: "ANTHROPIC_API_KEY",
38471
+ async run(ctx) {
38472
+ ctx.onProgress?.("Installing Anthropic Claude Code");
38473
+ await runNpmGlobalInstall(
38474
+ "@anthropic-ai/claude-code",
38475
+ { ...ctx.env, ANTHROPIC_API_KEY: ctx.authToken },
38476
+ { onLine: (line) => ctx.onProgress?.("Installing Anthropic Claude Code", line) }
38477
+ );
38478
+ }
38479
+ };
38480
+
38481
+ // ../bridge/src/agents/providers/claude-code/definition.ts
38482
+ var DEFAULT_COMMAND = ["npx", "--yes", "@agentclientprotocol/claude-agent-acp"];
38483
+ var claudeCodeProvider = {
38484
+ type: "claude-code",
38485
+ displayName: "Claude Code",
38486
+ defaultCommand: DEFAULT_COMMAND,
38487
+ authErrorHints: claudeCodeAuthErrorHints,
38488
+ detectPresence: detectLocalAgentPresence,
38489
+ install: claudeCodeInstall,
38490
+ createClient: (options) => createClaudeCodeAcpClient({
38491
+ ...options,
38492
+ afterSessionEstablished: options.afterSessionEstablished ?? applyClaudeCodeAfterSessionEstablished
38493
+ }),
38494
+ buildSpawnCommand: (base, sessionMode) => buildClaudeCodeAcpSpawnCommand([...base], sessionMode)
38495
+ };
38496
+
38497
+ // ../bridge/src/agents/providers/codex/apply-permission.ts
38498
+ function flattenSelectOptions3(options) {
38499
+ if (options == null || options.length === 0) return [];
38500
+ const first2 = options[0];
38501
+ if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
38502
+ return options.flatMap(
38503
+ (g) => Array.isArray(g.options) ? g.options : []
38504
+ );
38505
+ }
38506
+ return options;
38507
+ }
38508
+ function pickModeConfigOption2(configOptions) {
38509
+ if (configOptions == null || configOptions.length === 0) return null;
38510
+ const byCategory = configOptions.find((o) => o.category === "mode");
38511
+ if (byCategory) return byCategory;
38512
+ return configOptions.find((o) => o.id === "mode") ?? null;
38513
+ }
38514
+ async function applyCodexPermissionFromAcpSession(params) {
38515
+ const { sessionId, agentConfig, configOptions, modes, setSessionConfigOption, setSessionMode, logDebug: logDebug2 } = params;
38516
+ const desiredMode = getCodexPermissionModeFromAgentConfig(agentConfig);
38517
+ if (desiredMode == null) return;
38518
+ const modeOpt = pickModeConfigOption2(configOptions ?? null);
38519
+ if (modeOpt != null) {
38520
+ const flat = flattenSelectOptions3(modeOpt.options);
38521
+ const allowed = flat.some((o) => o.value === desiredMode);
38522
+ if (allowed && modeOpt.currentValue !== desiredMode) {
38523
+ try {
38524
+ logDebug2(
38525
+ `[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`
38526
+ );
38527
+ await setSessionConfigOption({ sessionId, configId: modeOpt.id, value: desiredMode });
38528
+ } catch (e) {
38529
+ logDebug2(`[Agent] Codex: session/set_config_option failed: ${e instanceof Error ? e.message : String(e)}`);
38530
+ }
38531
+ }
38532
+ return;
38533
+ }
38534
+ if (modes?.availableModes?.length) {
38535
+ const allowed = modes.availableModes.some((m) => m.id === desiredMode);
38536
+ if (allowed && desiredMode !== modes.currentModeId) {
38537
+ try {
38538
+ logDebug2(
38539
+ `[Agent] Codex: sending ACP session/set_mode modeId=${JSON.stringify(desiredMode)} was=${JSON.stringify(modes.currentModeId ?? null)} sessionId=${sessionId.slice(0, 8)}\u2026`
38540
+ );
38541
+ await setSessionMode({ sessionId, modeId: desiredMode });
38542
+ } catch (e) {
38543
+ logDebug2(`[Agent] Codex: session/set_mode failed: ${e instanceof Error ? e.message : String(e)}`);
38544
+ }
38545
+ }
38546
+ }
38547
+ }
38548
+
38549
+ // ../bridge/src/agents/providers/codex/after-session.ts
38550
+ var applyCodexAfterSessionEstablished = wrapPermissionAfterSession(
38551
+ applyCodexPermissionFromAcpSession
38552
+ );
38553
+
38554
+ // ../bridge/src/agents/providers/codex/client.ts
38483
38555
  var CODEX_ACP_PACKAGE = "@agentclientprotocol/codex-acp";
38484
38556
  var LEGACY_CODEX_ACP_PACKAGE = "@zed-industries/codex-acp";
38485
38557
  async function detectLocalAgentPresence2() {
38486
38558
  return isCommandOnPath("codex");
38487
38559
  }
38488
38560
  var DEFAULT_CODEX_ACP_COMMAND = ["npx", "--yes", CODEX_ACP_PACKAGE];
38489
- function isCodexAcpCommand(command) {
38490
- return command.some(
38491
- (a) => a === CODEX_ACP_PACKAGE || a === LEGACY_CODEX_ACP_PACKAGE || a.includes("codex-acp")
38492
- );
38493
- }
38494
38561
  function normalizeCodexAcpCommand(command) {
38495
38562
  return command.map((a) => a === LEGACY_CODEX_ACP_PACKAGE ? CODEX_ACP_PACKAGE : a);
38496
38563
  }
38497
- function buildCodexAcpSpawnCommand(base, _sessionMode, _agentConfig) {
38498
- return normalizeCodexAcpCommand(base);
38564
+ function buildCodexAcpSpawnCommand(base, _sessionMode, _agentConfig) {
38565
+ return normalizeCodexAcpCommand(base);
38566
+ }
38567
+ async function createCodexAcpClient(options) {
38568
+ const base = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
38569
+ const command = buildCodexAcpSpawnCommand(base, options.sessionMode, options.agentConfig);
38570
+ return createSdkStdioAcpClient({
38571
+ ...options,
38572
+ command,
38573
+ /** Codex ACP can ignore `session/cancel`; mirror Claude Code's subprocess fallback. */
38574
+ killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 2500
38575
+ });
38576
+ }
38577
+
38578
+ // ../bridge/src/agents/providers/codex/install.ts
38579
+ var codexInstall = {
38580
+ detectCommand: "codex",
38581
+ tokenEnvVar: "OPENAI_API_KEY",
38582
+ async run(ctx) {
38583
+ ctx.onProgress?.("Installing Codex");
38584
+ await runNpmGlobalInstall(
38585
+ "@openai/codex",
38586
+ { ...ctx.env, OPENAI_API_KEY: ctx.authToken },
38587
+ { onLine: (line) => ctx.onProgress?.("Installing Codex", line) }
38588
+ );
38589
+ }
38590
+ };
38591
+
38592
+ // ../bridge/src/agents/providers/codex/definition.ts
38593
+ var codexProvider = {
38594
+ type: "codex-acp",
38595
+ displayName: "Codex",
38596
+ defaultCommand: DEFAULT_CODEX_ACP_COMMAND,
38597
+ authErrorHints: codexAuthErrorHints,
38598
+ detectPresence: detectLocalAgentPresence2,
38599
+ install: codexInstall,
38600
+ createClient: (options) => createCodexAcpClient({
38601
+ ...options,
38602
+ afterSessionEstablished: options.afterSessionEstablished ?? applyCodexAfterSessionEstablished
38603
+ }),
38604
+ buildSpawnCommand: (base, sessionMode, agentConfig) => buildCodexAcpSpawnCommand([...base], sessionMode, agentConfig)
38605
+ };
38606
+
38607
+ // ../bridge/src/agents/providers/cursor/cleanup-session-plans.ts
38608
+ import * as fs17 from "node:fs";
38609
+
38610
+ // ../bridge/src/paths/session-plans-paths.ts
38611
+ import * as os6 from "node:os";
38612
+ import * as path25 from "node:path";
38613
+ function getSessionPlansRootDir() {
38614
+ return path25.join(os6.homedir(), ".buildautomaton", "plans");
38615
+ }
38616
+ function sanitizeSessionPlansKey(sessionId) {
38617
+ const t = sessionId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 220);
38618
+ return t || "session";
38499
38619
  }
38500
- async function createCodexAcpClient(options) {
38501
- const base = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
38502
- const command = buildCodexAcpSpawnCommand(base, options.sessionMode, options.agentConfig);
38503
- return createSdkStdioAcpClient({
38504
- ...options,
38505
- command,
38506
- /** Codex ACP can ignore `session/cancel`; mirror Claude Code's subprocess fallback. */
38507
- killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 2500
38508
- });
38620
+ function getSessionPlansDir(sessionId) {
38621
+ return path25.join(getSessionPlansRootDir(), sanitizeSessionPlansKey(sessionId));
38509
38622
  }
38510
38623
 
38511
- // ../bridge/src/agents/acp/clients/cursor/cursor-acp-client.ts
38512
- var cursor_acp_client_exports = {};
38513
- __export(cursor_acp_client_exports, {
38514
- BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE3,
38515
- buildCursorAcpSpawnCommand: () => buildCursorAcpSpawnCommand,
38516
- createCursorAcpClient: () => createCursorAcpClient,
38517
- detectLocalAgentPresence: () => detectLocalAgentPresence3
38518
- });
38624
+ // ../bridge/src/agents/providers/cursor/cleanup-session-plans.ts
38625
+ function cleanupSessionPlans(sessionId) {
38626
+ const id = typeof sessionId === "string" ? sessionId.trim() : "";
38627
+ if (!id) return;
38628
+ const dir = getSessionPlansDir(id);
38629
+ try {
38630
+ fs17.rmSync(dir, { recursive: true, force: true });
38631
+ } catch {
38632
+ }
38633
+ }
38519
38634
 
38520
- // ../bridge/src/agents/acp/clients/cursor/cursor-spawn-command.ts
38635
+ // ../bridge/src/agents/providers/cursor/cursor-spawn-command.ts
38521
38636
  function buildCursorAcpSpawnCommand(base, sessionMode) {
38522
38637
  if (!sessionMode) return [...base];
38523
38638
  const m = sessionMode.trim();
@@ -38525,7 +38640,7 @@ function buildCursorAcpSpawnCommand(base, sessionMode) {
38525
38640
  return [...base, "--mode", m];
38526
38641
  }
38527
38642
 
38528
- // ../bridge/src/agents/acp/clients/cursor/create-cursor-acp-session-context.ts
38643
+ // ../bridge/src/agents/providers/cursor/create-cursor-acp-session-context.ts
38529
38644
  init_log();
38530
38645
  function createCursorAcpSessionContext(options) {
38531
38646
  const suppressLoadReplayRef = { value: false };
@@ -38544,14 +38659,15 @@ function createCursorAcpSessionContext(options) {
38544
38659
  onAcpConfigOptionsUpdated: options.onAcpConfigOptionsUpdated,
38545
38660
  logDebug,
38546
38661
  getStderrText: () => options.stderrCapture.getText(),
38547
- pendingPlanExecute: { value: false }
38662
+ pendingPlanExecute: { value: false },
38663
+ afterSessionEstablished: options.afterSessionEstablished
38548
38664
  };
38549
38665
  }
38550
38666
 
38551
- // ../bridge/src/agents/acp/clients/cursor/send-cursor-prompt-with-plan-continue.ts
38667
+ // ../bridge/src/agents/providers/cursor/send-cursor-prompt-with-plan-continue.ts
38552
38668
  init_log();
38553
38669
 
38554
- // ../bridge/src/agents/acp/clients/cursor/cursor-plan-continue.ts
38670
+ // ../bridge/src/agents/providers/cursor/cursor-plan-continue.ts
38555
38671
  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.";
38556
38672
  function isAcceptedCreatePlanRpcResult(result) {
38557
38673
  if (result == null || typeof result !== "object" || Array.isArray(result)) return false;
@@ -38575,7 +38691,7 @@ async function switchCursorSessionToAgentMode(transport, sessionId) {
38575
38691
  }
38576
38692
  }
38577
38693
 
38578
- // ../bridge/src/agents/acp/clients/cursor/send-cursor-prompt-with-plan-continue.ts
38694
+ // ../bridge/src/agents/providers/cursor/send-cursor-prompt-with-plan-continue.ts
38579
38695
  async function sendCursorPromptWithPlanContinue(params) {
38580
38696
  const { transport, sessionCtx, sessionId, prompt, images } = params;
38581
38697
  const first2 = await sendAcpPromptViaTransport(transport, sessionCtx, sessionId, prompt, images);
@@ -38590,7 +38706,7 @@ async function sendCursorPromptWithPlanContinue(params) {
38590
38706
  );
38591
38707
  }
38592
38708
 
38593
- // ../bridge/src/agents/acp/clients/cursor/cancel-pending-cursor-permission-requests.ts
38709
+ // ../bridge/src/agents/providers/cursor/cancel-pending-cursor-permission-requests.ts
38594
38710
  function cancelPendingCursorPermissionRequests(pendingRequests2, respond) {
38595
38711
  for (const [reqId, pending2] of [...pendingRequests2.entries()]) {
38596
38712
  if (pending2.method === "session/request_permission") {
@@ -38600,7 +38716,7 @@ function cancelPendingCursorPermissionRequests(pendingRequests2, respond) {
38600
38716
  }
38601
38717
  }
38602
38718
 
38603
- // ../bridge/src/agents/acp/clients/cursor/create-cursor-acp-handle.ts
38719
+ // ../bridge/src/agents/providers/cursor/create-cursor-acp-handle.ts
38604
38720
  function createCursorAcpHandle(options) {
38605
38721
  let teardownStarted = false;
38606
38722
  let cancelFallback = null;
@@ -38663,10 +38779,10 @@ function createCursorAcpHandle(options) {
38663
38779
  };
38664
38780
  }
38665
38781
 
38666
- // ../bridge/src/agents/acp/clients/cursor/cursor-acp-init.ts
38667
- import * as readline from "node:readline";
38782
+ // ../bridge/src/agents/providers/cursor/cursor-acp-init.ts
38783
+ import * as readline2 from "node:readline";
38668
38784
 
38669
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-cursor-create-plan.ts
38785
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-cursor-create-plan.ts
38670
38786
  function buildCursorCreatePlanToolCallUpdate(requestId, params) {
38671
38787
  const toolCallId = params.toolCallId ?? params.tool_call_id;
38672
38788
  if (typeof toolCallId !== "string" || !toolCallId.trim()) return null;
@@ -38701,7 +38817,7 @@ function queueCursorCreatePlanRequest(method, id, msg, deps) {
38701
38817
  deps.onRequest?.({ requestId, method, params });
38702
38818
  }
38703
38819
 
38704
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-cursor-task.ts
38820
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-cursor-task.ts
38705
38821
  function buildCursorTaskToolCallUpdate(params) {
38706
38822
  const toolCallId = params.toolCallId ?? params.tool_call_id;
38707
38823
  if (typeof toolCallId !== "string" || !toolCallId.trim()) return null;
@@ -38734,7 +38850,7 @@ function handleCursorIncomingCursorTask(id, msg, deps) {
38734
38850
  if (update) deps.onSessionUpdate?.(update);
38735
38851
  }
38736
38852
 
38737
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-cursor-methods.ts
38853
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-cursor-methods.ts
38738
38854
  var CURSOR_BRIDGE_METHODS = /* @__PURE__ */ new Set(["cursor/ask_question"]);
38739
38855
  var CURSOR_NOOP_METHODS = /* @__PURE__ */ new Set(["cursor/update_todos", "cursor/generate_image"]);
38740
38856
  function handleCursorIncomingCursorNotification(method, msg, onSessionUpdate) {
@@ -38770,7 +38886,7 @@ function handleCursorIncomingCursorMethods(method, id, msg, deps) {
38770
38886
  return false;
38771
38887
  }
38772
38888
 
38773
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-types.ts
38889
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-types.ts
38774
38890
  function parseIncomingJsonRpcRequestId(raw) {
38775
38891
  if (typeof raw === "number" && Number.isFinite(raw)) return raw;
38776
38892
  if (typeof raw === "string" && raw.length > 0) return raw;
@@ -38789,7 +38905,7 @@ function isCursorFsWriteMethod(method) {
38789
38905
  return lower.startsWith("fs/") && lower.includes("write");
38790
38906
  }
38791
38907
 
38792
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-fs-request.ts
38908
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-fs-request.ts
38793
38909
  function pathFromFsParams(params) {
38794
38910
  for (const key of ["path", "filePath", "file_path", "targetPath", "target_path"]) {
38795
38911
  const value = params[key];
@@ -38846,7 +38962,7 @@ function handleCursorIncomingFsRequest(method, id, msg, deps) {
38846
38962
  return false;
38847
38963
  }
38848
38964
 
38849
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-permission-request.ts
38965
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-permission-request.ts
38850
38966
  function handleCursorIncomingPermissionRequest(id, method, msg, deps) {
38851
38967
  const params = msg.params ?? {};
38852
38968
  if (deps.onRequest) {
@@ -38862,39 +38978,23 @@ function handleCursorIncomingPermissionRequest(id, method, msg, deps) {
38862
38978
  return true;
38863
38979
  }
38864
38980
 
38865
- // ../bridge/src/agents/acp/clients/cursor/write-create-plan-file.ts
38866
- import * as fs17 from "node:fs";
38981
+ // ../bridge/src/agents/providers/cursor/write-create-plan-file.ts
38982
+ import * as fs18 from "node:fs";
38867
38983
  import * as path26 from "node:path";
38868
38984
  import { pathToFileURL as pathToFileURL2 } from "node:url";
38869
-
38870
- // ../bridge/src/paths/session-plans-paths.ts
38871
- import * as os6 from "node:os";
38872
- import * as path25 from "node:path";
38873
- function getSessionPlansRootDir() {
38874
- return path25.join(os6.homedir(), ".buildautomaton", "plans");
38875
- }
38876
- function sanitizeSessionPlansKey(sessionId) {
38877
- const t = sessionId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 220);
38878
- return t || "session";
38879
- }
38880
- function getSessionPlansDir(sessionId) {
38881
- return path25.join(getSessionPlansRootDir(), sanitizeSessionPlansKey(sessionId));
38882
- }
38883
-
38884
- // ../bridge/src/agents/acp/clients/cursor/write-create-plan-file.ts
38885
38985
  function sanitizePlanFileBase(toolCallId) {
38886
38986
  const t = toolCallId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 120);
38887
38987
  return t || "plan";
38888
38988
  }
38889
38989
  function writeCreatePlanFile(params) {
38890
38990
  const dir = getSessionPlansDir(params.cloudSessionId);
38891
- fs17.mkdirSync(dir, { recursive: true });
38991
+ fs18.mkdirSync(dir, { recursive: true });
38892
38992
  const filePath = path26.join(dir, `${sanitizePlanFileBase(params.toolCallId)}.md`);
38893
- fs17.writeFileSync(filePath, params.planMarkdown, "utf8");
38993
+ fs18.writeFileSync(filePath, params.planMarkdown, "utf8");
38894
38994
  return pathToFileURL2(filePath).href;
38895
38995
  }
38896
38996
 
38897
- // ../bridge/src/agents/acp/clients/cursor/enrich-create-plan-rpc-result.ts
38997
+ // ../bridge/src/agents/providers/cursor/enrich-create-plan-rpc-result.ts
38898
38998
  function asRecord2(v) {
38899
38999
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
38900
39000
  }
@@ -38923,7 +39023,7 @@ function enrichCreatePlanRpcResult(result, pendingParams, cloudSessionId) {
38923
39023
  return { outcome: { outcome: "accepted" } };
38924
39024
  }
38925
39025
 
38926
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-resolve-request.ts
39026
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-resolve-request.ts
38927
39027
  function resolveCursorIncomingRequest(pendingRequests2, respond, requestId, result, cloudSessionId, pendingPlanExecute) {
38928
39028
  const pending2 = pendingRequests2.get(requestId);
38929
39029
  let payload = result;
@@ -38950,7 +39050,7 @@ function formatSessionUpdateKindForLog(kind) {
38950
39050
  return kind.split("_").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
38951
39051
  }
38952
39052
 
38953
- // ../bridge/src/agents/acp/clients/cursor/cursor-incoming-session-update.ts
39053
+ // ../bridge/src/agents/providers/cursor/cursor-incoming-session-update.ts
38954
39054
  function handleCursorIncomingSessionUpdate(msg, deps) {
38955
39055
  const params = msg.params;
38956
39056
  const update = params?.update;
@@ -38971,7 +39071,7 @@ function handleCursorIncomingSessionUpdate(msg, deps) {
38971
39071
  return true;
38972
39072
  }
38973
39073
 
38974
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-parse.ts
39074
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-parse.ts
38975
39075
  function safeJsonParse(value) {
38976
39076
  try {
38977
39077
  const parsed = JSON.parse(value);
@@ -38981,7 +39081,7 @@ function safeJsonParse(value) {
38981
39081
  }
38982
39082
  }
38983
39083
 
38984
- // ../bridge/src/agents/acp/clients/cursor/cursor-acp-incoming-line-handler.ts
39084
+ // ../bridge/src/agents/providers/cursor/cursor-acp-incoming-line-handler.ts
38985
39085
  function createCursorAcpIncomingLineHandler(deps) {
38986
39086
  const respondDeps = {
38987
39087
  dbgFs: deps.dbgFs,
@@ -39037,7 +39137,7 @@ function createCursorAcpIncomingLineHandler(deps) {
39037
39137
  };
39038
39138
  }
39039
39139
 
39040
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-acp-transport.ts
39140
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-acp-transport.ts
39041
39141
  function createCursorJsonRpcAcpTransport(deps) {
39042
39142
  const { send, cancelSessionNotification, skipBrowserAuthenticate } = deps;
39043
39143
  return {
@@ -39055,12 +39155,12 @@ function createCursorJsonRpcAcpTransport(deps) {
39055
39155
  };
39056
39156
  }
39057
39157
 
39058
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-stdin-write.ts
39158
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-stdin-write.ts
39059
39159
  function writeJsonRpcLine(stdin, payload, callback) {
39060
39160
  stdin.write(JSON.stringify(payload) + "\n", callback);
39061
39161
  }
39062
39162
 
39063
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-inbound-respond.ts
39163
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-inbound-respond.ts
39064
39164
  function createCursorJsonRpcInboundRespond(stdin) {
39065
39165
  function respond(id, result) {
39066
39166
  writeJsonRpcLine(stdin, { jsonrpc: "2.0", id, result });
@@ -39081,7 +39181,7 @@ function createCursorJsonRpcInboundRespond(stdin) {
39081
39181
  return { respond, respondJsonRpcError, cancelSessionNotification };
39082
39182
  }
39083
39183
 
39084
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-outbound.ts
39184
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-outbound.ts
39085
39185
  function createCursorJsonRpcOutboundPending() {
39086
39186
  const pending2 = /* @__PURE__ */ new Map();
39087
39187
  let nextId = 1;
@@ -39107,7 +39207,7 @@ function createCursorJsonRpcOutboundPending() {
39107
39207
  return { allocateId, register, settleResponse, rejectOnWriteError };
39108
39208
  }
39109
39209
 
39110
- // ../bridge/src/agents/acp/clients/cursor/cursor-json-rpc-wire.ts
39210
+ // ../bridge/src/agents/providers/cursor/cursor-json-rpc-wire.ts
39111
39211
  function createCursorJsonRpcWriter(stdin) {
39112
39212
  const inbound = createCursorJsonRpcInboundRespond(stdin);
39113
39213
  const outbound = createCursorJsonRpcOutboundPending();
@@ -39129,7 +39229,7 @@ function createCursorJsonRpcWriter(stdin) {
39129
39229
  };
39130
39230
  }
39131
39231
 
39132
- // ../bridge/src/agents/acp/clients/cursor/cursor-acp-init.ts
39232
+ // ../bridge/src/agents/providers/cursor/cursor-acp-init.ts
39133
39233
  var CURSOR_ACP_CLIENT_INFO = {
39134
39234
  protocolVersion: 1,
39135
39235
  clientCapabilities: {
@@ -39149,7 +39249,7 @@ async function initCursorAcpWire(options) {
39149
39249
  settleResponse: wire.settleResponse,
39150
39250
  pendingRequests: pendingRequests2
39151
39251
  });
39152
- const rl = readline.createInterface({ input: options.child.stdout });
39252
+ const rl = readline2.createInterface({ input: options.child.stdout });
39153
39253
  rl.on("line", (line) => incoming.handleLine(line));
39154
39254
  const transport = createCursorJsonRpcAcpTransport({
39155
39255
  send: wire.send,
@@ -39160,11 +39260,11 @@ async function initCursorAcpWire(options) {
39160
39260
  return { wire, transport, established, incoming, pendingRequests: pendingRequests2 };
39161
39261
  }
39162
39262
 
39163
- // ../bridge/src/agents/acp/clients/cursor/spawn-cursor-acp-process.ts
39164
- import { spawn as spawn2 } from "node:child_process";
39263
+ // ../bridge/src/agents/providers/cursor/spawn-cursor-acp-process.ts
39264
+ import { spawn as spawn3 } from "node:child_process";
39165
39265
  function spawnCursorAcpProcess(options) {
39166
39266
  const isWindows = process.platform === "win32";
39167
- const child = spawn2(options.command[0], options.command.slice(1), {
39267
+ const child = spawn3(options.command[0], options.command.slice(1), {
39168
39268
  cwd: options.cwd,
39169
39269
  stdio: ["pipe", "pipe", "pipe"],
39170
39270
  env: bridgeInstalledAgentAuthProcessEnv(process.env),
@@ -39178,13 +39278,12 @@ function spawnCursorAcpProcess(options) {
39178
39278
  return { child, stderrCapture };
39179
39279
  }
39180
39280
 
39181
- // ../bridge/src/agents/acp/clients/cursor/cursor-local-agent.ts
39182
- var BACKEND_LOCAL_AGENT_TYPE3 = "cursor-cli";
39281
+ // ../bridge/src/agents/providers/cursor/cursor-local-agent.ts
39183
39282
  async function detectLocalAgentPresence3() {
39184
39283
  return isCommandOnPath("agent");
39185
39284
  }
39186
39285
 
39187
- // ../bridge/src/agents/acp/clients/cursor/cursor-acp-client.ts
39286
+ // ../bridge/src/agents/providers/cursor/cursor-acp-client.ts
39188
39287
  async function createCursorAcpClient(options) {
39189
39288
  const command = buildCursorAcpSpawnCommand(options.command, options.sessionMode);
39190
39289
  const {
@@ -39196,7 +39295,8 @@ async function createCursorAcpClient(options) {
39196
39295
  persistedAcpSessionId,
39197
39296
  onAcpSessionEstablished,
39198
39297
  onAcpConfigOptionsUpdated,
39199
- onAgentSubprocessExit
39298
+ onAgentSubprocessExit,
39299
+ afterSessionEstablished
39200
39300
  } = options;
39201
39301
  const dbgFs = process.env.BUILDAUTOMATON_DEBUG_ACP_FS === "1";
39202
39302
  const spawnEnv = bridgeInstalledAgentAuthProcessEnv(process.env);
@@ -39217,6 +39317,7 @@ async function createCursorAcpClient(options) {
39217
39317
  onAcpSessionEstablished,
39218
39318
  onAcpConfigOptionsUpdated,
39219
39319
  onFileChange,
39320
+ afterSessionEstablished,
39220
39321
  stderrCapture
39221
39322
  });
39222
39323
  return new Promise((resolve39, reject) => {
@@ -39252,17 +39353,43 @@ async function createCursorAcpClient(options) {
39252
39353
  });
39253
39354
  }
39254
39355
 
39255
- // ../bridge/src/agents/acp/clients/kiro-acp-client.ts
39256
- var kiro_acp_client_exports = {};
39257
- __export(kiro_acp_client_exports, {
39258
- BACKEND_LOCAL_AGENT_TYPE: () => BACKEND_LOCAL_AGENT_TYPE4,
39259
- DEFAULT_KIRO_ACP_COMMAND: () => DEFAULT_KIRO_ACP_COMMAND,
39260
- buildKiroAcpSpawnCommand: () => buildKiroAcpSpawnCommand,
39261
- createKiroAcpClient: () => createKiroAcpClient,
39262
- detectLocalAgentPresence: () => detectLocalAgentPresence4,
39263
- isKiroAcpCommand: () => isKiroAcpCommand
39264
- });
39265
- var BACKEND_LOCAL_AGENT_TYPE4 = "kiro-acp";
39356
+ // ../bridge/src/agents/providers/cursor/install.ts
39357
+ var cursorInstall = {
39358
+ detectCommand: "agent",
39359
+ alternateDetectCommands: ["cursor-agent"],
39360
+ tokenEnvVar: "CURSOR_API_KEY",
39361
+ async run(ctx) {
39362
+ ctx.onProgress?.("Installing Cursor CLI");
39363
+ const result = await runStreamingCommand(
39364
+ "bash",
39365
+ ["-lc", "curl -fsSL https://cursor.com/install | bash"],
39366
+ {
39367
+ timeoutMs: 3e5,
39368
+ env: { ...bridgeAgentPathEnv(ctx.env), CURSOR_API_KEY: ctx.authToken },
39369
+ onLine: (line) => ctx.onProgress?.("Installing Cursor CLI", line)
39370
+ }
39371
+ );
39372
+ if (result.code !== 0) {
39373
+ throw new Error(`Cursor CLI install failed (exit ${result.code ?? "signal"})`);
39374
+ }
39375
+ }
39376
+ };
39377
+
39378
+ // ../bridge/src/agents/providers/cursor/definition.ts
39379
+ var cursorProvider = {
39380
+ type: "cursor-cli",
39381
+ displayName: "Cursor",
39382
+ defaultCommand: ["agent", "acp"],
39383
+ authErrorHints: cursorAuthErrorHints,
39384
+ detectPresence: detectLocalAgentPresence3,
39385
+ install: cursorInstall,
39386
+ createClient: createCursorAcpClient,
39387
+ buildSpawnCommand: (base, sessionMode) => buildCursorAcpSpawnCommand([...base], sessionMode),
39388
+ onPromptTurnFinished: cleanupSessionPlans,
39389
+ onSessionClosed: cleanupSessionPlans
39390
+ };
39391
+
39392
+ // ../bridge/src/agents/providers/kiro/client.ts
39266
39393
  async function detectLocalAgentPresence4() {
39267
39394
  return isCommandOnPath("kiro-cli");
39268
39395
  }
@@ -39283,80 +39410,108 @@ async function createKiroAcpClient(options) {
39283
39410
  return createSdkStdioAcpClient({ ...options, command });
39284
39411
  }
39285
39412
 
39286
- // ../bridge/src/agents/acp/resolve-agent-command.ts
39287
- var AGENT_TYPE_DEFAULT_COMMANDS = {
39288
- [BACKEND_LOCAL_AGENT_TYPE3]: ["agent", "acp"],
39289
- [BACKEND_LOCAL_AGENT_TYPE2]: [...DEFAULT_CODEX_ACP_COMMAND],
39290
- /** ACP stdio agent; `@anthropic-ai/claude-code` is the interactive CLI and does not speak ACP on stdout. */
39291
- [BACKEND_LOCAL_AGENT_TYPE]: ["npx", "--yes", "@agentclientprotocol/claude-agent-acp"],
39292
- /** [Kiro CLI ACP](https://kiro.dev/docs/cli/acp/) — use full path to `kiro-cli` in PATH if the IDE cannot find it. */
39293
- [BACKEND_LOCAL_AGENT_TYPE4]: [...DEFAULT_KIRO_ACP_COMMAND]
39413
+ // ../bridge/src/agents/providers/kiro/ext-notifications.ts
39414
+ function createKiroSdkExtNotificationHandler(options) {
39415
+ const { onSessionUpdate } = options;
39416
+ return async (method, params) => {
39417
+ if (method === "_kiro.dev/metadata") {
39418
+ const p = params && typeof params === "object" ? params : {};
39419
+ const pct = p.contextUsagePercentage;
39420
+ if (typeof pct !== "number" || !Number.isFinite(pct) || !onSessionUpdate) return;
39421
+ onSessionUpdate({
39422
+ sessionUpdate: "context_usage",
39423
+ contextUsagePercentage: pct
39424
+ });
39425
+ return;
39426
+ }
39427
+ };
39428
+ }
39429
+
39430
+ // ../bridge/src/agents/providers/kiro/definition.ts
39431
+ var kiroProvider = {
39432
+ type: "kiro-acp",
39433
+ displayName: "Kiro",
39434
+ defaultCommand: DEFAULT_KIRO_ACP_COMMAND,
39435
+ authErrorHints: kiroAuthErrorHints,
39436
+ detectPresence: detectLocalAgentPresence4,
39437
+ createClient: (options) => createKiroAcpClient({
39438
+ ...options,
39439
+ createExtNotificationHandler: options.createExtNotificationHandler ?? createKiroSdkExtNotificationHandler
39440
+ }),
39441
+ buildSpawnCommand: (base, sessionMode) => buildKiroAcpSpawnCommand([...base], sessionMode)
39442
+ };
39443
+
39444
+ // ../bridge/src/agents/providers/opencode/install.ts
39445
+ var opencodeInstall = {
39446
+ detectCommand: "opencode",
39447
+ tokenEnvVar: "OPENCODE_API_KEY",
39448
+ async run(ctx) {
39449
+ ctx.onProgress?.("Installing OpenCode");
39450
+ await runNpmGlobalInstall(
39451
+ "opencode-ai",
39452
+ { ...ctx.env, OPENCODE_API_KEY: ctx.authToken },
39453
+ { onLine: (line) => ctx.onProgress?.("Installing OpenCode", line) }
39454
+ );
39455
+ }
39294
39456
  };
39295
- var AGENT_TYPE_DISPLAY_NAMES = {
39296
- [BACKEND_LOCAL_AGENT_TYPE3]: "Cursor",
39297
- [BACKEND_LOCAL_AGENT_TYPE2]: "Codex",
39298
- [BACKEND_LOCAL_AGENT_TYPE]: "Claude Code",
39299
- [BACKEND_LOCAL_AGENT_TYPE4]: "Kiro"
39457
+
39458
+ // ../bridge/src/agents/providers/opencode/definition.ts
39459
+ var opencodeProvider = {
39460
+ type: "opencode",
39461
+ displayName: "OpenCode",
39462
+ defaultCommand: [],
39463
+ authErrorHints: [],
39464
+ install: opencodeInstall,
39465
+ buildSpawnCommand: (base) => [...base]
39300
39466
  };
39301
- function getAgentTypeDisplayName(agentType) {
39302
- if (agentType == null || agentType === "") return "Unknown agent";
39303
- const known = AGENT_TYPE_DISPLAY_NAMES[agentType];
39304
- if (known) return known;
39305
- return agentType.split(/[-_]/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
39467
+
39468
+ // ../bridge/src/agents/providers/registry.ts
39469
+ var AGENT_PROVIDERS = [
39470
+ cursorProvider,
39471
+ codexProvider,
39472
+ kiroProvider,
39473
+ claudeCodeProvider,
39474
+ opencodeProvider
39475
+ ];
39476
+ var byType = new Map(AGENT_PROVIDERS.map((p) => [p.type, p]));
39477
+ function getAgentProvider(agentType) {
39478
+ if (agentType == null || agentType === "") return void 0;
39479
+ return byType.get(agentType);
39306
39480
  }
39307
- function useCursorAcp(agentType, command) {
39308
- if (agentType === BACKEND_LOCAL_AGENT_TYPE3) return true;
39309
- return command[0] === "agent" && command[1] === "acp";
39481
+ function listAutoDetectAgentProviders() {
39482
+ return AGENT_PROVIDERS.filter((p) => p.detectPresence != null);
39310
39483
  }
39311
- function useCodexAcp(agentType, command) {
39312
- if (agentType === BACKEND_LOCAL_AGENT_TYPE2) return true;
39313
- return isCodexAcpCommand(command);
39484
+ function notifyAgentProvidersSessionClosed(sessionId) {
39485
+ for (const p of AGENT_PROVIDERS) p.onSessionClosed?.(sessionId);
39314
39486
  }
39315
- function useKiroAcp(agentType, command) {
39316
- if (agentType === BACKEND_LOCAL_AGENT_TYPE4) return true;
39317
- return isKiroAcpCommand(command);
39487
+ function notifyAgentProvidersPromptTurnFinished(sessionId) {
39488
+ for (const p of AGENT_PROVIDERS) p.onPromptTurnFinished?.(sessionId);
39318
39489
  }
39490
+
39491
+ // ../bridge/src/agents/acp/resolve-agent-command.ts
39319
39492
  function resolveAgentCommand(preferredAgentType) {
39320
- if (!preferredAgentType) return null;
39321
- const command = AGENT_TYPE_DEFAULT_COMMANDS[preferredAgentType];
39322
- if (!command?.length) return null;
39323
- if (useCursorAcp(preferredAgentType, command)) {
39324
- return {
39325
- command,
39326
- label: preferredAgentType,
39327
- createClient: createCursorAcpClient,
39328
- spawnCommandForSession: (sessionMode, _agentConfig) => buildCursorAcpSpawnCommand(command, sessionMode)
39329
- };
39330
- }
39331
- if (useCodexAcp(preferredAgentType, command)) {
39332
- return {
39333
- command,
39334
- label: preferredAgentType,
39335
- createClient: createCodexAcpClient,
39336
- spawnCommandForSession: (sessionMode, agentConfig) => buildCodexAcpSpawnCommand(command, sessionMode, agentConfig)
39337
- };
39338
- }
39339
- if (useKiroAcp(preferredAgentType, command)) {
39340
- return {
39341
- command,
39342
- label: preferredAgentType,
39343
- createClient: createKiroAcpClient,
39344
- spawnCommandForSession: (sessionMode, _agentConfig) => buildKiroAcpSpawnCommand(command, sessionMode)
39345
- };
39346
- }
39493
+ const provider = getAgentProvider(preferredAgentType);
39494
+ if (!provider?.createClient || provider.defaultCommand.length === 0) return null;
39495
+ const command = [...provider.defaultCommand];
39347
39496
  return {
39348
39497
  command,
39349
- label: preferredAgentType,
39350
- createClient: createClaudeCodeAcpClient,
39351
- spawnCommandForSession: (sessionMode) => buildClaudeCodeAcpSpawnCommand(command, sessionMode)
39498
+ label: provider.type,
39499
+ createClient: provider.createClient,
39500
+ spawnCommandForSession: (sessionMode, agentConfig) => provider.buildSpawnCommand(command, sessionMode, agentConfig)
39352
39501
  };
39353
39502
  }
39503
+ function getAgentTypeDisplayName(agentType) {
39504
+ if (agentType == null || agentType === "") return "Unknown agent";
39505
+ const known = getAgentProvider(agentType)?.displayName;
39506
+ if (known) return known;
39507
+ return agentType.split(/[-_]/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
39508
+ }
39354
39509
 
39355
39510
  // ../bridge/src/agents/acp/session-file-change-path-kind.ts
39356
39511
  import { existsSync as existsSync4, statSync } from "node:fs";
39357
39512
 
39358
39513
  // ../bridge/src/git/git-exec.ts
39359
- import { execFile as execFile2, execFileSync as execFileSync2, spawn as spawn3 } from "node:child_process";
39514
+ import { execFile as execFile2, execFileSync as execFileSync2, spawn as spawn4 } from "node:child_process";
39360
39515
  import { promisify as promisify3 } from "node:util";
39361
39516
 
39362
39517
  // ../bridge/src/git/git-runtime.ts
@@ -40490,7 +40645,7 @@ async function ensureAcpClient(options) {
40490
40645
  if (!state.acpStartPromise) {
40491
40646
  let statOk = false;
40492
40647
  try {
40493
- const st = await fs18.promises.stat(targetSessionParentPath);
40648
+ const st = await fs19.promises.stat(targetSessionParentPath);
40494
40649
  statOk = st.isDirectory();
40495
40650
  if (!statOk) {
40496
40651
  state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
@@ -40962,18 +41117,6 @@ function reportPostTurnEnrichment(options) {
40962
41117
  });
40963
41118
  }
40964
41119
 
40965
- // ../bridge/src/agents/acp/clients/cursor/cleanup-session-plans.ts
40966
- import * as fs19 from "node:fs";
40967
- function cleanupSessionPlans(sessionId) {
40968
- const id = typeof sessionId === "string" ? sessionId.trim() : "";
40969
- if (!id) return;
40970
- const dir = getSessionPlansDir(id);
40971
- try {
40972
- fs19.rmSync(dir, { recursive: true, force: true });
40973
- } catch {
40974
- }
40975
- }
40976
-
40977
41120
  // ../bridge/src/agents/acp/prompts/finalize-and-send-prompt-result.ts
40978
41121
  async function finalizeAndSendPromptResult(params) {
40979
41122
  const {
@@ -40990,7 +41133,7 @@ async function finalizeAndSendPromptResult(params) {
40990
41133
  sendSessionUpdate,
40991
41134
  log: log2
40992
41135
  } = params;
40993
- cleanupSessionPlans(sessionId);
41136
+ notifyAgentProvidersPromptTurnFinished(sessionId);
40994
41137
  const planningTodosSubmit = isPlanningSession && plugin?.cloud?.maybeSubmitPlanningTodos ? await plugin.cloud.maybeSubmitPlanningTodos({
40995
41138
  sessionId,
40996
41139
  runId,
@@ -41714,7 +41857,7 @@ async function loadOrCreateE2eCertificates(directory) {
41714
41857
  }
41715
41858
 
41716
41859
  // ../bridge/src/e2ee/key-command.ts
41717
- import * as readline2 from "node:readline";
41860
+ import * as readline3 from "node:readline";
41718
41861
  function installE2eCertificateKeyCommand({
41719
41862
  log: log2,
41720
41863
  onOpenCertificate,
@@ -41725,7 +41868,7 @@ function installE2eCertificateKeyCommand({
41725
41868
  return () => {
41726
41869
  };
41727
41870
  }
41728
- readline2.emitKeypressEvents(process.stdin);
41871
+ readline3.emitKeypressEvents(process.stdin);
41729
41872
  process.stdin.setRawMode(true);
41730
41873
  process.stdin.resume();
41731
41874
  const onKeypress = (str, key) => {
@@ -41900,25 +42043,20 @@ import * as path36 from "node:path";
41900
42043
  // ../bridge/src/agents/detect-local-agent-types.ts
41901
42044
  init_yield_to_event_loop();
41902
42045
  init_cli_process_interrupt();
41903
- var LOCAL_AGENT_ACP_MODULES = [
41904
- cursor_acp_client_exports,
41905
- codex_acp_client_exports,
41906
- kiro_acp_client_exports,
41907
- claude_code_acp_client_exports
41908
- ];
41909
42046
  async function detectLocalAgentTypes() {
41910
42047
  try {
41911
42048
  if (isCliImmediateShutdownRequested()) return [];
42049
+ const providers = listAutoDetectAgentProviders();
41912
42050
  const out = [];
41913
- for (let i = 0; i < LOCAL_AGENT_ACP_MODULES.length; i++) {
42051
+ for (let i = 0; i < providers.length; i++) {
41914
42052
  if (isCliImmediateShutdownRequested()) return out;
41915
42053
  if (i > 0) {
41916
42054
  await yieldToEventLoop();
41917
42055
  if (isCliImmediateShutdownRequested()) return out;
41918
42056
  }
41919
- const mod = LOCAL_AGENT_ACP_MODULES[i];
42057
+ const provider = providers[i];
41920
42058
  try {
41921
- if (await mod.detectLocalAgentPresence()) out.push(mod.BACKEND_LOCAL_AGENT_TYPE);
42059
+ if (await provider.detectPresence?.()) out.push(provider.type);
41922
42060
  } catch {
41923
42061
  }
41924
42062
  }
@@ -42913,7 +43051,7 @@ function pipedStdoutStderrFor(attemptStdio) {
42913
43051
  }
42914
43052
 
42915
43053
  // ../bridge/src/preview-environments/manager/shell-spawn/try-spawn-piped-via-sh.ts
42916
- import { spawn as spawn4 } from "node:child_process";
43054
+ import { spawn as spawn5 } from "node:child_process";
42917
43055
  function trySpawnPipedViaSh(command, env, cwd, signal, lifecycle) {
42918
43056
  const attempts = [
42919
43057
  { stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
@@ -42937,9 +43075,9 @@ function trySpawnPipedViaSh(command, env, cwd, signal, lifecycle) {
42937
43075
  if (process.platform === "win32") {
42938
43076
  opts.windowsHide = true;
42939
43077
  const com = process.env.ComSpec || "cmd.exe";
42940
- proc = spawn4(com, ["/d", "/s", "/c", command], opts);
43078
+ proc = spawn5(com, ["/d", "/s", "/c", command], opts);
42941
43079
  } else {
42942
- proc = spawn4("/bin/sh", ["-c", command], opts);
43080
+ proc = spawn5("/bin/sh", ["-c", command], opts);
42943
43081
  }
42944
43082
  if (attempt.endStdin) {
42945
43083
  proc.stdin?.end();
@@ -42959,7 +43097,7 @@ function trySpawnPipedViaSh(command, env, cwd, signal, lifecycle) {
42959
43097
  }
42960
43098
 
42961
43099
  // ../bridge/src/preview-environments/manager/shell-spawn/try-spawn-shell-true-piped.ts
42962
- import { spawn as spawn5 } from "node:child_process";
43100
+ import { spawn as spawn6 } from "node:child_process";
42963
43101
  function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal, lifecycle) {
42964
43102
  try {
42965
43103
  const opts = mergePreviewEnvironmentSpawnOptions(
@@ -42975,7 +43113,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal, lifecycle)
42975
43113
  if (process.platform === "win32") {
42976
43114
  opts.windowsHide = true;
42977
43115
  }
42978
- return spawn5(command, opts);
43116
+ return spawn6(command, opts);
42979
43117
  } catch (e) {
42980
43118
  if (isSpawnEbadf(e)) return null;
42981
43119
  throw e;
@@ -42983,7 +43121,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal, lifecycle)
42983
43121
  }
42984
43122
 
42985
43123
  // ../bridge/src/preview-environments/manager/shell-spawn/try-spawn-merged-log-file.ts
42986
- import { spawn as spawn6 } from "node:child_process";
43124
+ import { spawn as spawn7 } from "node:child_process";
42987
43125
  import fs24 from "node:fs";
42988
43126
  import { tmpdir } from "node:os";
42989
43127
  import path37 from "node:path";
@@ -43001,7 +43139,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal, lifecycle) {
43001
43139
  try {
43002
43140
  let proc;
43003
43141
  if (process.platform === "win32") {
43004
- proc = spawn6(
43142
+ proc = spawn7(
43005
43143
  process.env.ComSpec || "cmd.exe",
43006
43144
  ["/d", "/s", "/c", command],
43007
43145
  mergePreviewEnvironmentSpawnOptions(
@@ -43010,7 +43148,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal, lifecycle) {
43010
43148
  )
43011
43149
  );
43012
43150
  } else {
43013
- proc = spawn6(
43151
+ proc = spawn7(
43014
43152
  "/bin/sh",
43015
43153
  ["-c", command],
43016
43154
  mergePreviewEnvironmentSpawnOptions({ env, cwd, stdio, ...signal ? { signal } : {} }, lifecycle)
@@ -43035,7 +43173,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal, lifecycle) {
43035
43173
  }
43036
43174
 
43037
43175
  // ../bridge/src/preview-environments/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
43038
- import { spawn as spawn7 } from "node:child_process";
43176
+ import { spawn as spawn8 } from "node:child_process";
43039
43177
  import fs25 from "node:fs";
43040
43178
  import { tmpdir as tmpdir2 } from "node:os";
43041
43179
  import path38 from "node:path";
@@ -43058,7 +43196,7 @@ cd ${shSingleQuote(cwd)}
43058
43196
  /bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
43059
43197
  `
43060
43198
  );
43061
- const proc = spawn7(
43199
+ const proc = spawn8(
43062
43200
  "/bin/sh",
43063
43201
  [runnerPath],
43064
43202
  mergePreviewEnvironmentSpawnOptions(
@@ -43092,7 +43230,7 @@ CD /D ${q(cwd)}\r
43092
43230
  ${command} >> ${q(logPath)} 2>&1\r
43093
43231
  `
43094
43232
  );
43095
- const proc = spawn7(
43233
+ const proc = spawn8(
43096
43234
  com,
43097
43235
  ["/d", "/s", "/c", q(runnerPath)],
43098
43236
  mergePreviewEnvironmentSpawnOptions(
@@ -43114,7 +43252,7 @@ ${command} >> ${q(logPath)} 2>&1\r
43114
43252
  }
43115
43253
 
43116
43254
  // ../bridge/src/preview-environments/manager/shell-spawn/try-spawn-inherit.ts
43117
- import { spawn as spawn8 } from "node:child_process";
43255
+ import { spawn as spawn9 } from "node:child_process";
43118
43256
  function trySpawnInheritStdio(command, env, cwd, signal, lifecycle) {
43119
43257
  const opts = mergePreviewEnvironmentSpawnOptions(
43120
43258
  {
@@ -43129,9 +43267,9 @@ function trySpawnInheritStdio(command, env, cwd, signal, lifecycle) {
43129
43267
  if (process.platform === "win32") {
43130
43268
  opts.windowsHide = true;
43131
43269
  const com = process.env.ComSpec || "cmd.exe";
43132
- proc = spawn8(com, ["/d", "/s", "/c", command], opts);
43270
+ proc = spawn9(com, ["/d", "/s", "/c", command], opts);
43133
43271
  } else {
43134
- proc = spawn8("/bin/sh", ["-c", command], opts);
43272
+ proc = spawn9("/bin/sh", ["-c", command], opts);
43135
43273
  }
43136
43274
  return { proc, pipedStdoutStderr: false };
43137
43275
  }
@@ -43606,7 +43744,7 @@ function createBridgeHeartbeatController(params) {
43606
43744
  }
43607
43745
 
43608
43746
  // ../bridge/src/cli/cli-version.ts
43609
- var CLI_VERSION2 = "0.1.90".length > 0 ? "0.1.90" : "0.0.0-dev";
43747
+ var CLI_VERSION2 = "0.1.92".length > 0 ? "0.1.92" : "0.0.0-dev";
43610
43748
 
43611
43749
  // ../bridge/src/connection/identify/send-bridge-identify.ts
43612
43750
  function sendBridgeIdentify(ws, params) {
@@ -43882,7 +44020,7 @@ var import_debug = __toESM(require_src(), 1);
43882
44020
  var import_promise_deferred = __toESM(require_dist2(), 1);
43883
44021
  var import_promise_deferred2 = __toESM(require_dist2(), 1);
43884
44022
  import { Buffer as Buffer2 } from "node:buffer";
43885
- import { spawn as spawn9 } from "child_process";
44023
+ import { spawn as spawn10 } from "child_process";
43886
44024
  import { normalize as normalize3 } from "node:path";
43887
44025
  import { EventEmitter } from "node:events";
43888
44026
  var __defProp2 = Object.defineProperty;
@@ -45270,7 +45408,7 @@ var init_git_executor_chain = __esm2({
45270
45408
  rejection = reason || rejection;
45271
45409
  }
45272
45410
  });
45273
- const spawned = spawn9(command, args, spawnOptions);
45411
+ const spawned = spawn10(command, args, spawnOptions);
45274
45412
  spawned.stdout.on(
45275
45413
  "data",
45276
45414
  onDataReceived(stdOut, "stdOut", logger, outputLogger.step("stdOut"))
@@ -48873,7 +49011,7 @@ import * as path43 from "node:path";
48873
49011
 
48874
49012
  // ../bridge/src/git/changes/lines/count-lines.ts
48875
49013
  import { createReadStream } from "node:fs";
48876
- import * as readline3 from "node:readline";
49014
+ import * as readline4 from "node:readline";
48877
49015
  function countLinesInText(text) {
48878
49016
  return splitTextIntoDiffLines(text).length;
48879
49017
  }
@@ -48882,7 +49020,7 @@ async function countTextFileLines(filePath) {
48882
49020
  const maxBytes = 512e3;
48883
49021
  let lines = 0;
48884
49022
  const stream = createReadStream(filePath, { encoding: "utf8" });
48885
- const rl = readline3.createInterface({ input: stream, crlfDelay: Infinity });
49023
+ const rl = readline4.createInterface({ input: stream, crlfDelay: Infinity });
48886
49024
  for await (const _line of rl) {
48887
49025
  lines += 1;
48888
49026
  bytes += Buffer.byteLength(String(_line), "utf8") + 1;
@@ -53534,159 +53672,24 @@ var handleInstalledAgentAuthSync = (msg) => {
53534
53672
  );
53535
53673
  };
53536
53674
 
53537
- // ../bridge/src/agents/install/run-streaming-command.ts
53538
- import { spawn as spawn10 } from "node:child_process";
53539
- import * as readline4 from "node:readline";
53540
- function runStreamingCommand(command, args, options) {
53541
- return new Promise((resolve39, reject) => {
53542
- const child = spawn10(command, args, {
53543
- env: options.env,
53544
- stdio: ["ignore", "pipe", "pipe"]
53545
- });
53546
- let settled = false;
53547
- const timer = options.timeoutMs != null ? setTimeout(() => {
53548
- child.kill("SIGKILL");
53549
- if (!settled) {
53550
- settled = true;
53551
- reject(new Error(`Command timed out after ${options.timeoutMs}ms`));
53552
- }
53553
- }, options.timeoutMs) : null;
53554
- const onLine = (line) => {
53555
- if (line.length > 0) options.onLine?.(line);
53556
- };
53557
- if (child.stdout) {
53558
- readline4.createInterface({ input: child.stdout, crlfDelay: Infinity }).on("line", onLine);
53559
- }
53560
- if (child.stderr) {
53561
- readline4.createInterface({ input: child.stderr, crlfDelay: Infinity }).on("line", onLine);
53562
- }
53563
- child.on("error", (err) => {
53564
- if (timer) clearTimeout(timer);
53565
- if (!settled) {
53566
- settled = true;
53567
- reject(err);
53568
- }
53569
- });
53570
- child.on("close", (code, signal) => {
53571
- if (timer) clearTimeout(timer);
53572
- if (!settled) {
53573
- settled = true;
53574
- resolve39({ code, signal });
53575
- }
53576
- });
53577
- });
53578
- }
53579
-
53580
- // ../bridge/src/agents/install/commands/run-npm-global-install.ts
53581
- async function runNpmGlobalInstall(packageName, env, options) {
53582
- const result = await runStreamingCommand("npm", ["install", "-g", packageName], {
53583
- env: bridgeAgentPathEnv(env),
53584
- timeoutMs: options?.timeoutMs ?? 3e5,
53585
- onLine: options?.onLine
53586
- });
53587
- if (result.code !== 0) {
53588
- throw new Error(`npm install -g ${packageName} failed (exit ${result.code ?? "signal"})`);
53589
- }
53590
- }
53591
-
53592
- // ../bridge/src/agents/install/commands/claude-code.ts
53593
- var claudeCodeInstallCommand = {
53594
- agentType: "claude-code",
53595
- detectCommand: "claude",
53596
- async install(ctx) {
53597
- ctx.onProgress?.("Installing Anthropic Claude Code");
53598
- await runNpmGlobalInstall(
53599
- "@anthropic-ai/claude-code",
53600
- { ...ctx.env, ANTHROPIC_API_KEY: ctx.authToken },
53601
- { onLine: (line) => ctx.onProgress?.("Installing Anthropic Claude Code", line) }
53602
- );
53603
- }
53604
- };
53605
-
53606
- // ../bridge/src/agents/install/commands/codex-acp.ts
53607
- var codexAcpInstallCommand = {
53608
- agentType: "codex-acp",
53609
- detectCommand: "codex",
53610
- async install(ctx) {
53611
- ctx.onProgress?.("Installing Codex");
53612
- await runNpmGlobalInstall(
53613
- "@openai/codex",
53614
- { ...ctx.env, OPENAI_API_KEY: ctx.authToken },
53615
- { onLine: (line) => ctx.onProgress?.("Installing Codex", line) }
53616
- );
53617
- }
53618
- };
53619
-
53620
- // ../bridge/src/agents/install/commands/cursor-cli.ts
53621
- var cursorCliInstallCommand = {
53622
- agentType: "cursor-cli",
53623
- detectCommand: "agent",
53624
- alternateDetectCommands: ["cursor-agent"],
53625
- async install(ctx) {
53626
- ctx.onProgress?.("Installing Cursor CLI");
53627
- const result = await runStreamingCommand(
53628
- "bash",
53629
- ["-lc", "curl -fsSL https://cursor.com/install | bash"],
53630
- {
53631
- timeoutMs: 3e5,
53632
- env: { ...bridgeAgentPathEnv(ctx.env), CURSOR_API_KEY: ctx.authToken },
53633
- onLine: (line) => ctx.onProgress?.("Installing Cursor CLI", line)
53634
- }
53635
- );
53636
- if (result.code !== 0) {
53637
- throw new Error(`Cursor CLI install failed (exit ${result.code ?? "signal"})`);
53638
- }
53639
- }
53640
- };
53641
-
53642
- // ../bridge/src/agents/install/commands/opencode.ts
53643
- var opencodeInstallCommand = {
53644
- agentType: "opencode",
53645
- detectCommand: "opencode",
53646
- async install(ctx) {
53647
- ctx.onProgress?.("Installing OpenCode");
53648
- await runNpmGlobalInstall(
53649
- "opencode-ai",
53650
- { ...ctx.env, OPENCODE_API_KEY: ctx.authToken },
53651
- { onLine: (line) => ctx.onProgress?.("Installing OpenCode", line) }
53652
- );
53653
- }
53654
- };
53655
-
53656
- // ../bridge/src/agents/install/commands/index.ts
53657
- var COMMANDS = [
53658
- claudeCodeInstallCommand,
53659
- codexAcpInstallCommand,
53660
- cursorCliInstallCommand,
53661
- opencodeInstallCommand
53662
- ];
53663
- var byType = new Map(COMMANDS.map((c) => [c.agentType, c]));
53664
- function getAgentInstallCommand(agentType) {
53665
- return byType.get(agentType);
53666
- }
53667
-
53668
53675
  // ../bridge/src/agents/install/install-local-agent.ts
53669
53676
  async function installLocalAgentOnBridge(params) {
53670
- const spec = INSTALLABLE_BRIDGE_AGENTS.find((a) => a.value === params.agentType);
53671
- if (!spec) return { success: false, error: `Unsupported agent type: ${params.agentType}` };
53672
- const command = getAgentInstallCommand(params.agentType);
53673
- if (!command) return { success: false, error: `No install command for ${params.agentType}` };
53674
- params.onProgress?.(`Configuring ${spec.label} credentials`);
53677
+ const provider = getAgentProvider(params.agentType);
53678
+ const install = provider?.install;
53679
+ if (!provider || !install) return { success: false, error: `Unsupported agent type: ${params.agentType}` };
53680
+ params.onProgress?.(`Configuring ${provider.displayName} credentials`);
53675
53681
  try {
53676
- await command.install({
53682
+ await install.run({
53677
53683
  authToken: params.authToken,
53678
53684
  onProgress: params.onProgress,
53679
- env: { ...process.env, [spec.tokenEnvVar]: params.authToken }
53685
+ env: { ...process.env, [install.tokenEnvVar]: params.authToken }
53680
53686
  });
53681
53687
  } catch (e) {
53682
53688
  const msg = e instanceof Error ? e.message : String(e);
53683
53689
  return { success: false, error: msg };
53684
53690
  }
53685
53691
  ensureBridgeAgentPathInProcessEnv();
53686
- const detectNames = [
53687
- command.detectCommand,
53688
- ...command.alternateDetectCommands ?? []
53689
- ];
53692
+ const detectNames = [install.detectCommand, ...install.alternateDetectCommands ?? []];
53690
53693
  let found = false;
53691
53694
  for (const name of detectNames) {
53692
53695
  if (await waitForCommandOnPath(name)) {
@@ -53695,7 +53698,7 @@ async function installLocalAgentOnBridge(params) {
53695
53698
  }
53696
53699
  }
53697
53700
  if (!found) {
53698
- return { success: false, error: `${command.detectCommand} not found on PATH after install` };
53701
+ return { success: false, error: `${install.detectCommand} not found on PATH after install` };
53699
53702
  }
53700
53703
  return { success: true };
53701
53704
  }
@@ -54853,7 +54856,7 @@ var handleRenameSessionBranchMessage = (msg, deps) => {
54853
54856
  var handleSessionArchivedMessage = (msg, deps) => {
54854
54857
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
54855
54858
  if (!sessionId) return;
54856
- cleanupSessionPlans(sessionId);
54859
+ notifyAgentProvidersSessionClosed(sessionId);
54857
54860
  void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
54858
54861
  };
54859
54862
 
@@ -54861,7 +54864,7 @@ var handleSessionArchivedMessage = (msg, deps) => {
54861
54864
  var handleSessionDiscardedMessage = (msg, deps) => {
54862
54865
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
54863
54866
  if (!sessionId) return;
54864
- cleanupSessionPlans(sessionId);
54867
+ notifyAgentProvidersSessionClosed(sessionId);
54865
54868
  void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
54866
54869
  };
54867
54870
 
@@ -55540,12 +55543,12 @@ function createMainBridgeAuthRefreshHandler(params, connect) {
55540
55543
  // ../bridge/src/connection/ws/main-bridge-ws-close-handler.ts
55541
55544
  function createMainBridgeCloseHandler(params, connect) {
55542
55545
  const { state, logFn, bridgeHeartbeat } = params;
55543
- return (code, reason) => {
55546
+ return (code, reason, closedWs) => {
55547
+ if (state.currentWs !== closedWs) return;
55544
55548
  bridgeHeartbeat?.stop();
55545
55549
  try {
55546
- const was = state.currentWs;
55547
55550
  state.currentWs = null;
55548
- if (was) was.removeAllListeners();
55551
+ closedWs.removeAllListeners();
55549
55552
  const willReconnect = !state.closedByUser;
55550
55553
  if (willReconnect) {
55551
55554
  const duplicateResult = evaluateDuplicateBridgeDisconnect(