@aiden-ade/sandbox-agent 0.1.26 → 0.1.27

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.
Files changed (77) hide show
  1. package/dist/core-agent.d.ts +2 -6
  2. package/dist/core-agent.d.ts.map +1 -1
  3. package/dist/core-agent.js +21 -140
  4. package/dist/core-agent.js.map +1 -1
  5. package/dist/index.cjs +787 -287
  6. package/dist/index.d.ts +0 -4
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +42 -107
  9. package/dist/index.js.map +1 -1
  10. package/dist/sandbox.d.ts +0 -6
  11. package/dist/sandbox.d.ts.map +1 -1
  12. package/dist/sandbox.js +10 -40
  13. package/dist/sandbox.js.map +1 -1
  14. package/dist/updater.d.ts.map +1 -1
  15. package/dist/updater.js +4 -0
  16. package/dist/updater.js.map +1 -1
  17. package/dist/version.d.ts +1 -1
  18. package/dist/version.d.ts.map +1 -1
  19. package/dist/version.js +1 -1
  20. package/dist/version.js.map +1 -1
  21. package/dist/web-presenter.d.ts +4 -7
  22. package/dist/web-presenter.d.ts.map +1 -1
  23. package/dist/web-presenter.js +4 -29
  24. package/dist/web-presenter.js.map +1 -1
  25. package/dist/ws-client.d.ts +0 -35
  26. package/dist/ws-client.d.ts.map +1 -1
  27. package/dist/ws-client.js +9 -32
  28. package/dist/ws-client.js.map +1 -1
  29. package/package.json +3 -3
  30. package/dist/__tests__/cli-executable.test.d.ts +0 -2
  31. package/dist/__tests__/cli-executable.test.d.ts.map +0 -1
  32. package/dist/__tests__/cli-executable.test.js +0 -83
  33. package/dist/__tests__/cli-executable.test.js.map +0 -1
  34. package/dist/__tests__/cli-help.test.d.ts +0 -2
  35. package/dist/__tests__/cli-help.test.d.ts.map +0 -1
  36. package/dist/__tests__/cli-help.test.js +0 -33
  37. package/dist/__tests__/cli-help.test.js.map +0 -1
  38. package/dist/__tests__/core-agent-workflow-context.test.d.ts +0 -2
  39. package/dist/__tests__/core-agent-workflow-context.test.d.ts.map +0 -1
  40. package/dist/__tests__/core-agent-workflow-context.test.js +0 -50
  41. package/dist/__tests__/core-agent-workflow-context.test.js.map +0 -1
  42. package/dist/__tests__/daemon-socket.integration.test.d.ts +0 -2
  43. package/dist/__tests__/daemon-socket.integration.test.d.ts.map +0 -1
  44. package/dist/__tests__/daemon-socket.integration.test.js +0 -156
  45. package/dist/__tests__/daemon-socket.integration.test.js.map +0 -1
  46. package/dist/__tests__/daemon.test.d.ts +0 -2
  47. package/dist/__tests__/daemon.test.d.ts.map +0 -1
  48. package/dist/__tests__/daemon.test.js +0 -1014
  49. package/dist/__tests__/daemon.test.js.map +0 -1
  50. package/dist/__tests__/generated-images.test.d.ts +0 -2
  51. package/dist/__tests__/generated-images.test.d.ts.map +0 -1
  52. package/dist/__tests__/generated-images.test.js +0 -50
  53. package/dist/__tests__/generated-images.test.js.map +0 -1
  54. package/dist/__tests__/sandbox-workflow-context.test.d.ts +0 -2
  55. package/dist/__tests__/sandbox-workflow-context.test.d.ts.map +0 -1
  56. package/dist/__tests__/sandbox-workflow-context.test.js +0 -66
  57. package/dist/__tests__/sandbox-workflow-context.test.js.map +0 -1
  58. package/dist/aiden-system-prompt.d.ts +0 -6
  59. package/dist/aiden-system-prompt.d.ts.map +0 -1
  60. package/dist/aiden-system-prompt.js +0 -6
  61. package/dist/aiden-system-prompt.js.map +0 -1
  62. package/dist/cli-executable.d.ts +0 -58
  63. package/dist/cli-executable.d.ts.map +0 -1
  64. package/dist/cli-executable.js +0 -301
  65. package/dist/cli-executable.js.map +0 -1
  66. package/dist/cli-help.d.ts +0 -7
  67. package/dist/cli-help.d.ts.map +0 -1
  68. package/dist/cli-help.js +0 -45
  69. package/dist/cli-help.js.map +0 -1
  70. package/dist/daemon.d.ts +0 -46
  71. package/dist/daemon.d.ts.map +0 -1
  72. package/dist/daemon.js +0 -876
  73. package/dist/daemon.js.map +0 -1
  74. package/dist/generated-images.d.ts +0 -4
  75. package/dist/generated-images.d.ts.map +0 -1
  76. package/dist/generated-images.js +0 -142
  77. package/dist/generated-images.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -4856,11 +4856,11 @@ function resolveCommand(args, env) {
4856
4856
 
4857
4857
  // src/daemon.ts
4858
4858
  var import_node_child_process3 = require("child_process");
4859
- var import_node_crypto2 = require("crypto");
4860
- var import_node_fs4 = require("fs");
4859
+ var import_node_crypto = require("crypto");
4860
+ var import_node_fs3 = require("fs");
4861
4861
  var import_node_http = __toESM(require("http"), 1);
4862
4862
  var import_node_os4 = require("os");
4863
- var import_node_path4 = require("path");
4863
+ var import_node_path3 = require("path");
4864
4864
 
4865
4865
  // ../shared/dist/node/provider-limits.js
4866
4866
  var import_node_child_process = require("child_process");
@@ -4974,8 +4974,8 @@ async function withTimeout(promise, timeoutMs, fallback) {
4974
4974
  try {
4975
4975
  return await Promise.race([
4976
4976
  promise,
4977
- new Promise((resolve2) => {
4978
- timer = setTimeout(() => resolve2(fallback), timeoutMs);
4977
+ new Promise((resolve3) => {
4978
+ timer = setTimeout(() => resolve3(fallback), timeoutMs);
4979
4979
  })
4980
4980
  ]);
4981
4981
  } finally {
@@ -4988,7 +4988,7 @@ async function collectCodexLimits(env) {
4988
4988
  const codexCommand = resolveExecutable("codex", runtimeEnv, "AIDEN_CODEX_PATH");
4989
4989
  if (!codexCommand)
4990
4990
  return [];
4991
- return withTimeout(new Promise((resolve2) => {
4991
+ return withTimeout(new Promise((resolve3) => {
4992
4992
  const child = (0, import_node_child_process.spawn)(codexCommand, ["-s", "read-only", "-a", "untrusted", "app-server"], {
4993
4993
  env: runtimeEnv,
4994
4994
  stdio: ["pipe", "pipe", "ignore"]
@@ -5000,7 +5000,7 @@ async function collectCodexLimits(env) {
5000
5000
  pending.clear();
5001
5001
  if (!child.killed)
5002
5002
  child.kill();
5003
- resolve2(windows);
5003
+ resolve3(windows);
5004
5004
  };
5005
5005
  const request = (method, params = {}) => {
5006
5006
  const id = nextId++;
@@ -7685,9 +7685,9 @@ var Socket2 = class extends Emitter {
7685
7685
  * @return a Promise that will be fulfilled when the server acknowledges the event
7686
7686
  */
7687
7687
  emitWithAck(ev, ...args) {
7688
- return new Promise((resolve2, reject) => {
7688
+ return new Promise((resolve3, reject) => {
7689
7689
  const fn = (arg1, arg2) => {
7690
- return arg1 ? reject(arg1) : resolve2(arg2);
7690
+ return arg1 ? reject(arg1) : resolve3(arg2);
7691
7691
  };
7692
7692
  fn.withError = true;
7693
7693
  args.push(fn);
@@ -8691,22 +8691,33 @@ var import_node_fs2 = require("fs");
8691
8691
  var import_node_os2 = require("os");
8692
8692
  var import_node_path2 = require("path");
8693
8693
  var PROVIDER_CLI_COMMANDS = {
8694
- claude_cli: { provider: "claude_cli", command: "claude", envVar: "AIDEN_CLAUDE_PATH" },
8695
- codex_app_server: { provider: "codex_app_server", command: "codex", envVar: "AIDEN_CODEX_PATH" },
8696
- copilot_cli: { provider: "copilot_cli", command: "copilot", envVar: "AIDEN_COPILOT_PATH" },
8694
+ claude_cli: { provider: "claude_cli", command: "claude", envVars: ["AIDEN_CLAUDE_PATH"] },
8695
+ codex_app_server: {
8696
+ provider: "codex_app_server",
8697
+ command: "codex",
8698
+ envVars: ["AIDEN_CODEX_PATH"]
8699
+ },
8700
+ copilot_cli: { provider: "copilot_cli", command: "copilot", envVars: ["AIDEN_COPILOT_PATH"] },
8697
8701
  cursor_agent_cli: {
8698
8702
  provider: "cursor_agent_cli",
8699
8703
  command: "cursor-agent",
8700
- envVar: "AIDEN_CURSOR_AGENT_PATH"
8704
+ envVars: ["AIDEN_CURSOR_AGENT_PATH"]
8705
+ },
8706
+ gemini_cli: {
8707
+ provider: "gemini_cli",
8708
+ command: "agy",
8709
+ envVars: ["AIDEN_ANTIGRAVITY_PATH", "AIDEN_GEMINI_PATH"]
8701
8710
  },
8702
- gemini_cli: { provider: "gemini_cli", command: "gemini", envVar: "AIDEN_GEMINI_PATH" },
8703
- kimi_cli: { provider: "kimi_cli", command: "kimi-cli", envVar: "AIDEN_KIMI_PATH" },
8704
- opencode_cli: { provider: "opencode_cli", command: "opencode", envVar: "AIDEN_OPENCODE_PATH" },
8705
- droid_cli: { provider: "droid_cli", command: "droid", envVar: "AIDEN_DROID_PATH" },
8706
- supatest_cli: { provider: "supatest_cli", command: "supatest", envVar: "AIDEN_SUPATEST_PATH" }
8711
+ kimi_cli: { provider: "kimi_cli", command: "kimi-cli", envVars: ["AIDEN_KIMI_PATH"] },
8712
+ opencode_cli: { provider: "opencode_cli", command: "opencode", envVars: ["AIDEN_OPENCODE_PATH"] },
8713
+ droid_cli: { provider: "droid_cli", command: "droid", envVars: ["AIDEN_DROID_PATH"] },
8714
+ supatest_cli: { provider: "supatest_cli", command: "supatest", envVars: ["AIDEN_SUPATEST_PATH"] }
8707
8715
  };
8708
8716
  var DISCOVERABLE_PROVIDER_KINDS = Object.keys(PROVIDER_CLI_COMMANDS);
8709
8717
  var PROVIDER_ALIASES = {
8718
+ agy: "gemini_cli",
8719
+ antigravity: "gemini_cli",
8720
+ antigravity_cli: "gemini_cli",
8710
8721
  codex: "codex_app_server",
8711
8722
  gemini: "gemini_cli"
8712
8723
  };
@@ -8716,8 +8727,11 @@ var BACKEND_CLI_COMMANDS = {
8716
8727
  codex_app_server: "codex",
8717
8728
  copilot_cli: "copilot",
8718
8729
  cursor_agent_cli: "cursor-agent",
8719
- gemini_cli: "gemini",
8720
- gemini: "gemini",
8730
+ agy: "agy",
8731
+ antigravity: "agy",
8732
+ antigravity_cli: "agy",
8733
+ gemini_cli: "agy",
8734
+ gemini: "agy",
8721
8735
  kimi_cli: "kimi-cli",
8722
8736
  opencode_cli: "opencode",
8723
8737
  droid_cli: "droid",
@@ -8905,7 +8919,7 @@ function resolveProviderCliCommand(provider, env = getDaemonCliEnvironment()) {
8905
8919
  const normalized = normalizeDiscoverableProvider(provider);
8906
8920
  if (!normalized) return null;
8907
8921
  const spec = PROVIDER_CLI_COMMANDS[normalized];
8908
- const override = process.env[spec.envVar]?.trim();
8922
+ const override = spec.envVars.map((name) => process.env[name]?.trim() || env[name]?.trim()).find(Boolean);
8909
8923
  if (override) {
8910
8924
  const resolved2 = resolveCliExecutable(override, env) ?? (isRunnableFile(override) ? override : null);
8911
8925
  if (resolved2) cacheResolvedCli(spec.command, resolved2);
@@ -8922,7 +8936,7 @@ function resolveBackendRuntimeCommand(backendKind, env = getDaemonCliEnvironment
8922
8936
  const cached = getCachedResolvedCli(command);
8923
8937
  if (cached && isRunnableFile(cached)) return cached;
8924
8938
  const spec = Object.values(PROVIDER_CLI_COMMANDS).find((entry) => entry.command === command);
8925
- const envOverride = spec ? env[spec.envVar] : void 0;
8939
+ const envOverride = spec ? spec.envVars.map((name) => env[name]?.trim()).find(Boolean) : void 0;
8926
8940
  const resolved = resolveCliExecutable(envOverride?.trim() || command, env);
8927
8941
  if (resolved) cacheResolvedCli(command, resolved);
8928
8942
  return resolved ?? void 0;
@@ -9321,6 +9335,64 @@ function isReadOnlyAgent(agentId) {
9321
9335
  return !agent.capabilities.canEditFiles;
9322
9336
  }
9323
9337
 
9338
+ // ../shared/dist/agent/generated-images.js
9339
+ var GENERATED_IMAGE_TOOL_NAMES = [
9340
+ "image_generation",
9341
+ "image_generate",
9342
+ "image_gen",
9343
+ "imagegen",
9344
+ "generate_image",
9345
+ "generate_images",
9346
+ "edit_image",
9347
+ "edit_images",
9348
+ "image_edit",
9349
+ "images_edit",
9350
+ "text_to_image",
9351
+ "images_generate"
9352
+ ];
9353
+ var BROWSER_SCREENSHOT_TOOL_NAMES = [
9354
+ "agent_browser_screenshot",
9355
+ "agent_browser_take_screenshot",
9356
+ "agent_browser_capture_screenshot",
9357
+ "browser_screenshot",
9358
+ "browser_take_screenshot",
9359
+ "browser_capture_screenshot",
9360
+ "screenshot"
9361
+ ];
9362
+ var BROWSER_RECORDING_TOOL_NAMES = [
9363
+ "agent_browser_record",
9364
+ "agent_browser_recording",
9365
+ "agent_browser_video",
9366
+ "agent_browser_capture_video",
9367
+ "agent_browser_screen_recording",
9368
+ "browser_record",
9369
+ "browser_recording",
9370
+ "browser_video",
9371
+ "browser_capture_video",
9372
+ "browser_screen_recording"
9373
+ ];
9374
+ function normalizeToolName(toolName) {
9375
+ return toolName?.toLowerCase().replace(/[^a-z0-9]+/g, "_") ?? "";
9376
+ }
9377
+ function isGeneratedImageToolName(toolName) {
9378
+ const normalized = normalizeToolName(toolName);
9379
+ if (!normalized)
9380
+ return false;
9381
+ return GENERATED_IMAGE_TOOL_NAMES.some((token) => normalized.includes(token));
9382
+ }
9383
+ function isBrowserScreenshotToolName(toolName) {
9384
+ const normalized = normalizeToolName(toolName);
9385
+ if (!normalized)
9386
+ return false;
9387
+ return BROWSER_SCREENSHOT_TOOL_NAMES.some((token) => normalized === token || normalized.endsWith(`_${token}`));
9388
+ }
9389
+ function isBrowserRecordingToolName(toolName) {
9390
+ const normalized = normalizeToolName(toolName);
9391
+ if (!normalized)
9392
+ return false;
9393
+ return BROWSER_RECORDING_TOOL_NAMES.some((token) => normalized === token || normalized.endsWith(`_${token}`));
9394
+ }
9395
+
9324
9396
  // ../shared/dist/agent/session-result.js
9325
9397
  var SESSION_RESUME_FAILED_MESSAGE = "Session resume failed. The previous session may have expired or is no longer available. Please start a new session.";
9326
9398
 
@@ -9621,6 +9693,12 @@ var import_path3 = require("path");
9621
9693
  var import_fs4 = require("fs");
9622
9694
  var import_os4 = require("os");
9623
9695
  var import_path4 = require("path");
9696
+ var import_fs5 = require("fs");
9697
+ var import_os5 = require("os");
9698
+ var import_path5 = require("path");
9699
+ var import_crypto2 = require("crypto");
9700
+ var import_fs6 = require("fs");
9701
+ var import_path6 = require("path");
9624
9702
  function shouldUseReadOnlyRuntimePermissions(config) {
9625
9703
  if (isReadOnlyAgent(config.agentId)) return true;
9626
9704
  switch (config.mode) {
@@ -9761,8 +9839,7 @@ function createGenericCliBackend(options) {
9761
9839
  const state = {
9762
9840
  process: null,
9763
9841
  iterations: 0,
9764
- summary: "",
9765
- allowContinuation: options.allowContinuation ?? false
9842
+ summary: ""
9766
9843
  };
9767
9844
  const prompt = options.augmentPrompt?.(context) ?? (context.config.mode === "plan" ? buildPlanModePrefix(buildPromptWithSystem(context.config, context.promptText)) : buildPromptWithSystem(context.config, context.promptText));
9768
9845
  const args = options.buildArgs?.(context, prompt) ?? options.args.map((arg) => arg === "{{prompt}}" ? prompt : arg);
@@ -9819,12 +9896,13 @@ function createGenericCliBackend(options) {
9819
9896
  }
9820
9897
  });
9821
9898
  let exitCode;
9899
+ let idleTimedOut = false;
9822
9900
  try {
9823
- const exitPromise = new Promise((resolve2, reject) => {
9901
+ const exitPromise = new Promise((resolve22, reject) => {
9824
9902
  child.on("error", (error) => {
9825
9903
  reject(formatCliSpawnError(options.command, error));
9826
9904
  });
9827
- child.on("exit", (code) => resolve2(code ?? 0));
9905
+ child.on("exit", (code) => resolve22(code ?? 0));
9828
9906
  const onAbort = () => {
9829
9907
  const pid = child.pid;
9830
9908
  if (!pid) return;
@@ -9840,53 +9918,67 @@ function createGenericCliBackend(options) {
9840
9918
  context.abortController.signal.addEventListener("abort", onAbort, { once: true });
9841
9919
  });
9842
9920
  if (options.keepStdinOpen) {
9843
- if (options.allowContinuation) {
9844
- const IDLE_MS = options.continuationIdleMs ?? 10 * 60 * 1e3;
9845
- let idleTimer = null;
9846
- const resetIdleTimer = () => {
9921
+ const resultPromise = new Promise((resolve22) => {
9922
+ state.onResultReceived = () => resolve22("result_received");
9923
+ if (state.resultReceived) resolve22("result_received");
9924
+ });
9925
+ const IDLE_MS = options.resultIdleTimeoutMs ?? 10 * 60 * 1e3;
9926
+ let idleTimer = null;
9927
+ const idlePromise = new Promise((resolve22) => {
9928
+ const armIdleTimer = () => {
9847
9929
  if (idleTimer) clearTimeout(idleTimer);
9848
9930
  idleTimer = setTimeout(() => {
9849
- if (state.stdin && !state.stdin.destroyed) {
9850
- state.stdin.end();
9851
- console.info(
9852
- `[${options.kind}] Continuation idle timeout (${IDLE_MS}ms) \u2014 closing stdin`
9853
- );
9931
+ if (presenter.pendingAskUserToolIds?.size) {
9932
+ resolve22("idle_timeout");
9933
+ } else {
9934
+ armIdleTimer();
9854
9935
  }
9855
9936
  }, IDLE_MS);
9856
9937
  };
9857
- state.onNewOutput = resetIdleTimer;
9858
- resetIdleTimer();
9859
- exitCode = await exitPromise;
9860
- if (idleTimer) clearTimeout(idleTimer);
9861
- } else {
9862
- const resultPromise = new Promise((resolve2) => {
9863
- state.onResultReceived = () => resolve2("result_received");
9864
- if (state.resultReceived) resolve2("result_received");
9865
- });
9866
- const raceResult = await Promise.race([exitPromise, resultPromise]);
9867
- if (raceResult === "result_received") {
9868
- const GRACE_MS = 5e3;
9869
- const gracedExit = await Promise.race([
9870
- exitPromise,
9871
- new Promise((resolve2) => setTimeout(() => resolve2(0), GRACE_MS))
9872
- ]);
9873
- exitCode = typeof gracedExit === "number" ? gracedExit : 0;
9874
- if (child.exitCode === null && !child.killed) {
9875
- console.warn(
9876
- `[${options.kind}] Process still alive ${GRACE_MS}ms after result event, killing`
9877
- );
9878
- try {
9879
- if (process.platform === "win32") {
9880
- child.kill("SIGKILL");
9881
- } else if (child.pid) {
9882
- process.kill(-child.pid, "SIGKILL");
9883
- }
9884
- } catch {
9938
+ state.onNewOutput = armIdleTimer;
9939
+ armIdleTimer();
9940
+ });
9941
+ const raceResult = await Promise.race([exitPromise, resultPromise, idlePromise]);
9942
+ if (idleTimer) clearTimeout(idleTimer);
9943
+ if (raceResult === "idle_timeout") {
9944
+ idleTimedOut = true;
9945
+ console.warn(
9946
+ `[${options.kind}] Idle timeout (${IDLE_MS}ms) waiting for a pending user answer \u2014 force-killing orphaned process`
9947
+ );
9948
+ try {
9949
+ if (process.platform === "win32") {
9950
+ child.kill("SIGKILL");
9951
+ } else if (child.pid) {
9952
+ process.kill(-child.pid, "SIGKILL");
9953
+ }
9954
+ } catch {
9955
+ }
9956
+ exitCode = await Promise.race([
9957
+ exitPromise,
9958
+ new Promise((resolve22) => setTimeout(() => resolve22(1), 5e3))
9959
+ ]);
9960
+ } else if (raceResult === "result_received") {
9961
+ const GRACE_MS = 5e3;
9962
+ const gracedExit = await Promise.race([
9963
+ exitPromise,
9964
+ new Promise((resolve22) => setTimeout(() => resolve22(0), GRACE_MS))
9965
+ ]);
9966
+ exitCode = typeof gracedExit === "number" ? gracedExit : 0;
9967
+ if (child.exitCode === null && !child.killed) {
9968
+ console.warn(
9969
+ `[${options.kind}] Process still alive ${GRACE_MS}ms after result event, killing`
9970
+ );
9971
+ try {
9972
+ if (process.platform === "win32") {
9973
+ child.kill("SIGKILL");
9974
+ } else if (child.pid) {
9975
+ process.kill(-child.pid, "SIGKILL");
9885
9976
  }
9977
+ } catch {
9886
9978
  }
9887
- } else {
9888
- exitCode = raceResult;
9889
9979
  }
9980
+ } else {
9981
+ exitCode = raceResult;
9890
9982
  }
9891
9983
  } else {
9892
9984
  exitCode = await exitPromise;
@@ -9910,6 +10002,31 @@ function createGenericCliBackend(options) {
9910
10002
  usage: state.usage
9911
10003
  };
9912
10004
  }
10005
+ if (idleTimedOut) {
10006
+ console.error(`[${options.kind}] Process force-killed after idle timeout`, {
10007
+ stderrLines: stderrLines.slice(-10),
10008
+ summaryPreview: state.summary.slice(0, 200),
10009
+ runtimeSessionId: state.runtimeSessionId
10010
+ });
10011
+ await options.afterExit?.(context, state);
10012
+ const hasRenderableTurn2 = state.summary.trim().length > 0 || state.iterations > 0;
10013
+ if (hasRenderableTurn2) {
10014
+ await presenter.onTurnComplete([]);
10015
+ }
10016
+ return {
10017
+ success: false,
10018
+ summary: state.summary.trim() || "Agent execution timed out",
10019
+ filesModified: [],
10020
+ planFilesCreated: [],
10021
+ iterations: Math.max(state.iterations, 1),
10022
+ error: "Agent timed out waiting for your answer to its question \u2014 no response arrived, so the run was stopped. Send your reply as a new message to continue.",
10023
+ providerSessionId: state.runtimeSessionId,
10024
+ runtimeSessionId: state.runtimeSessionId,
10025
+ backendKind: options.kind,
10026
+ supportTier: options.supportTier,
10027
+ usage: state.usage
10028
+ };
10029
+ }
9913
10030
  if (exitCode !== 0) {
9914
10031
  console.error(`[${options.kind}] Process exited with code ${exitCode}`, {
9915
10032
  stderrLines: stderrLines.slice(-10),
@@ -9982,11 +10099,6 @@ function parseClaudeStructuredLine(line, context, state) {
9982
10099
  );
9983
10100
  break;
9984
10101
  }
9985
- if (state.resultReceived && state.allowContinuation) {
9986
- console.info("[claude_cli] Continuation turn detected \u2014 restarting presenter");
9987
- state.resultReceived = false;
9988
- void presenter.onStart(context.config);
9989
- }
9990
10102
  state.onNewOutput?.();
9991
10103
  state.iterations += 1;
9992
10104
  const message = typeof parsed.message === "object" && parsed.message !== null ? parsed.message : null;
@@ -10115,31 +10227,10 @@ function parseClaudeStructuredLine(line, context, state) {
10115
10227
  break;
10116
10228
  }
10117
10229
  state.resultReceived = true;
10118
- if (state.allowContinuation) {
10119
- const continuationResult = {
10120
- success: !state.error,
10121
- summary: state.summary || state.error || "",
10122
- filesModified: [],
10123
- planFilesCreated: [],
10124
- iterations: Math.max(state.iterations, 1),
10125
- error: state.error ? classifyCliError(state.error, 0) : void 0,
10126
- providerSessionId: state.runtimeSessionId,
10127
- runtimeSessionId: state.runtimeSessionId,
10128
- backendKind: context.config.backendKind,
10129
- supportTier: context.config.supportTier,
10130
- usage: state.usage
10131
- };
10132
- void presenter.onComplete(continuationResult);
10133
- state.onNewOutput?.();
10134
- console.info(
10135
- "[claude_cli] Result received in continuation mode \u2014 keeping stdin open for background work"
10136
- );
10137
- } else {
10138
- state.onResultReceived?.();
10139
- if (state.stdin && !state.stdin.destroyed) {
10140
- state.stdin.end();
10141
- console.info("[claude_cli] Closed stdin after result event to signal CLI exit");
10142
- }
10230
+ state.onResultReceived?.();
10231
+ if (state.stdin && !state.stdin.destroyed) {
10232
+ state.stdin.end();
10233
+ console.info("[claude_cli] Closed stdin after result event to signal CLI exit");
10143
10234
  }
10144
10235
  break;
10145
10236
  }
@@ -11333,7 +11424,33 @@ function parseGeminiStructuredLine(line, context, state) {
11333
11424
  break;
11334
11425
  }
11335
11426
  }
11336
- function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
11427
+ function parseAntigravityLine(line, context, state) {
11428
+ try {
11429
+ JSON.parse(line);
11430
+ parseGeminiStructuredLine(line, context, state);
11431
+ return;
11432
+ } catch {
11433
+ }
11434
+ if (!line.trim()) return;
11435
+ state.iterations = Math.max(state.iterations, 1);
11436
+ state.summary += `${line}
11437
+ `;
11438
+ void context.presenter.onAssistantText(`${line}
11439
+ `);
11440
+ }
11441
+ function buildAntigravityCliArgs(context, prompt, defaultArgs = []) {
11442
+ const args = [];
11443
+ const selectedModel = context.config.selectedModel?.trim();
11444
+ const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
11445
+ if (selectedModel) args.push("--model", selectedModel);
11446
+ if (resumeId) args.push("--conversation", resumeId);
11447
+ if (!shouldUseReadOnlyRuntimePermissions(context.config)) {
11448
+ args.push("--dangerously-skip-permissions");
11449
+ }
11450
+ args.push("--print-timeout", "10m");
11451
+ return [...args, ...defaultArgs, "-p", prompt];
11452
+ }
11453
+ function createAntigravityCliBackend(command = "agy", defaultArgs = []) {
11337
11454
  return {
11338
11455
  kind: "gemini_cli",
11339
11456
  supportTier: "structured",
@@ -11348,19 +11465,9 @@ function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
11348
11465
  supportTier: "structured",
11349
11466
  command,
11350
11467
  args: [],
11351
- buildArgs: (ctx) => {
11352
- const args = ["-p", "", "--output-format", "stream-json"];
11353
- const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
11354
- const approvalMode = shouldUseReadOnlyRuntimePermissions(ctx.config) ? "plan" : "yolo";
11355
- args.push("--approval-mode", approvalMode);
11356
- if (resumeId) args.push("--resume", resumeId);
11357
- if (ctx.config.selectedModel?.trim())
11358
- args.push("--model", ctx.config.selectedModel.trim());
11359
- return [...args, ...defaultArgs];
11360
- },
11361
- promptViaStdin: true,
11468
+ buildArgs: (ctx, prompt) => buildAntigravityCliArgs(ctx, prompt, defaultArgs),
11362
11469
  augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
11363
- parseStructuredLine: parseGeminiStructuredLine
11470
+ parseStructuredLine: parseAntigravityLine
11364
11471
  }).run(context);
11365
11472
  } finally {
11366
11473
  cleanup();
@@ -11368,6 +11475,72 @@ function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
11368
11475
  }
11369
11476
  };
11370
11477
  }
11478
+ function createGeminiCliBackend(command = "agy", defaultArgs = []) {
11479
+ return createAntigravityCliBackend(command, defaultArgs);
11480
+ }
11481
+ var DEFAULT_SKILL_ROOTS = [
11482
+ (0, import_path4.join)((0, import_os4.homedir)(), ".agents", "skills"),
11483
+ (0, import_path4.join)((0, import_os4.homedir)(), ".claude", "skills"),
11484
+ (0, import_path4.join)((0, import_os4.homedir)(), ".config", "opencode", "skills")
11485
+ ];
11486
+ function parseFrontmatter(content) {
11487
+ if (!content.startsWith("---\n")) return {};
11488
+ const end = content.indexOf("\n---", 4);
11489
+ if (end === -1) return {};
11490
+ const result = {};
11491
+ for (const line of content.slice(4, end).split("\n")) {
11492
+ const match = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line.trim());
11493
+ if (!match) continue;
11494
+ const [, key, rawValue] = match;
11495
+ result[key] = rawValue.replace(/^["']|["']$/g, "").trim();
11496
+ }
11497
+ return result;
11498
+ }
11499
+ function listSkillFiles(root) {
11500
+ if (!(0, import_fs4.existsSync)(root)) return [];
11501
+ const files = [];
11502
+ for (const entry of (0, import_fs4.readdirSync)(root, { withFileTypes: true })) {
11503
+ if (!entry.isDirectory()) continue;
11504
+ const skillPath = (0, import_path4.join)(root, entry.name, "SKILL.md");
11505
+ if ((0, import_fs4.existsSync)(skillPath)) files.push(skillPath);
11506
+ }
11507
+ return files;
11508
+ }
11509
+ function loadInstalledSkills(roots = DEFAULT_SKILL_ROOTS) {
11510
+ const skills = /* @__PURE__ */ new Map();
11511
+ for (const root of roots) {
11512
+ for (const skillPath of listSkillFiles(root)) {
11513
+ try {
11514
+ const frontmatter = parseFrontmatter((0, import_fs4.readFileSync)(skillPath, "utf8"));
11515
+ const name = frontmatter.name;
11516
+ const description = frontmatter.description;
11517
+ if (!name || !description || skills.has(name)) continue;
11518
+ skills.set(name, { name, description, path: skillPath });
11519
+ } catch {
11520
+ }
11521
+ }
11522
+ }
11523
+ return [...skills.values()].sort((a, b) => a.name.localeCompare(b.name));
11524
+ }
11525
+ function buildInstalledSkillsPrompt(roots = DEFAULT_SKILL_ROOTS) {
11526
+ const skills = loadInstalledSkills(roots);
11527
+ if (skills.length === 0) return void 0;
11528
+ return [
11529
+ "## Installed Skills",
11530
+ "",
11531
+ "Use an installed skill before improvising when the task matches a skill description.",
11532
+ "",
11533
+ "Protocol:",
11534
+ "1. Match the request against the skill names and descriptions below.",
11535
+ "2. Before acting on a matching skill, read its SKILL.md file completely.",
11536
+ "3. Resolve any referenced files such as references/, scripts/, or assets/ relative to the skill directory.",
11537
+ "4. Use the smallest set of matching skills for the current turn only.",
11538
+ "5. If a named skill is missing or unreadable, say so briefly and continue with the best fallback.",
11539
+ "",
11540
+ "Available skills:",
11541
+ ...skills.map((skill) => `- ${skill.name}: ${skill.description} (file: ${skill.path})`)
11542
+ ].join("\n");
11543
+ }
11371
11544
  var KIMI_RESUME_SESSION_RE = /To resume this session:\s*kimi(?:-cli)?\s+(?:-[rS]|--session|--resume)\s+([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
11372
11545
  function parseKimiStderrLine(line, state) {
11373
11546
  const match = line.match(KIMI_RESUME_SESSION_RE);
@@ -11470,7 +11643,13 @@ function createKimiCliBackend(command = "kimi-cli", defaultArgs = []) {
11470
11643
  if (model) args.push("--model", model);
11471
11644
  return [...args, ...defaultArgs, "--prompt", prompt];
11472
11645
  },
11473
- augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
11646
+ augmentPrompt: (ctx) => {
11647
+ const skillPrompt = buildInstalledSkillsPrompt();
11648
+ const promptText = [skillPrompt, ctx.promptText].filter(Boolean).join("\n\n");
11649
+ const basePrompt = buildPromptWithSystem(ctx.config, promptText);
11650
+ const modePrompt = ctx.config.mode === "plan" ? buildPlanModePrefix(basePrompt) : basePrompt;
11651
+ return appendImagePathReferences(modePrompt, imageFiles);
11652
+ },
11474
11653
  parseStructuredLine: parseKimiStructuredLine,
11475
11654
  parseStderrLine: (line, _ctx, state) => parseKimiStderrLine(line, state)
11476
11655
  }).run(context);
@@ -11813,40 +11992,40 @@ function parseSkillInvocation(text) {
11813
11992
  function candidateRoots(cwd) {
11814
11993
  const roots = [];
11815
11994
  if (cwd) {
11816
- roots.push((0, import_path4.join)(cwd, ".agents", "skills"));
11817
- roots.push((0, import_path4.join)(cwd, ".claude", "skills"));
11818
- roots.push((0, import_path4.join)(cwd, ".codex", "skills"));
11819
- if ((0, import_fs4.existsSync)(cwd)) {
11995
+ roots.push((0, import_path5.join)(cwd, ".agents", "skills"));
11996
+ roots.push((0, import_path5.join)(cwd, ".claude", "skills"));
11997
+ roots.push((0, import_path5.join)(cwd, ".codex", "skills"));
11998
+ if ((0, import_fs5.existsSync)(cwd)) {
11820
11999
  let entries = [];
11821
12000
  try {
11822
- entries = (0, import_fs4.readdirSync)(cwd, { withFileTypes: true });
12001
+ entries = (0, import_fs5.readdirSync)(cwd, { withFileTypes: true });
11823
12002
  } catch {
11824
12003
  }
11825
12004
  for (const entry of entries) {
11826
12005
  if (!entry.isDirectory()) continue;
11827
12006
  if (entry.name.startsWith(".")) continue;
11828
- const child = (0, import_path4.join)(cwd, entry.name);
11829
- roots.push((0, import_path4.join)(child, ".agents", "skills"));
11830
- roots.push((0, import_path4.join)(child, ".claude", "skills"));
11831
- roots.push((0, import_path4.join)(child, ".codex", "skills"));
12007
+ const child = (0, import_path5.join)(cwd, entry.name);
12008
+ roots.push((0, import_path5.join)(child, ".agents", "skills"));
12009
+ roots.push((0, import_path5.join)(child, ".claude", "skills"));
12010
+ roots.push((0, import_path5.join)(child, ".codex", "skills"));
11832
12011
  }
11833
12012
  }
11834
12013
  }
11835
- const home = (0, import_os4.homedir)();
11836
- roots.push((0, import_path4.join)(home, ".agents", "skills"));
11837
- roots.push((0, import_path4.join)(home, ".claude", "skills"));
11838
- roots.push((0, import_path4.join)(home, ".codex", "skills"));
12014
+ const home = (0, import_os5.homedir)();
12015
+ roots.push((0, import_path5.join)(home, ".agents", "skills"));
12016
+ roots.push((0, import_path5.join)(home, ".claude", "skills"));
12017
+ roots.push((0, import_path5.join)(home, ".codex", "skills"));
11839
12018
  return Array.from(new Set(roots));
11840
12019
  }
11841
12020
  function resolveSkillInvocation(text, cwd) {
11842
12021
  const parsed = parseSkillInvocation(text);
11843
12022
  if (!parsed) return null;
11844
12023
  for (const root of candidateRoots(cwd)) {
11845
- const skillPath = (0, import_path4.join)(root, parsed.command, "SKILL.md");
11846
- if (!(0, import_fs4.existsSync)(skillPath)) continue;
12024
+ const skillPath = (0, import_path5.join)(root, parsed.command, "SKILL.md");
12025
+ if (!(0, import_fs5.existsSync)(skillPath)) continue;
11847
12026
  let content;
11848
12027
  try {
11849
- content = (0, import_fs4.readFileSync)(skillPath, "utf-8");
12028
+ content = (0, import_fs5.readFileSync)(skillPath, "utf-8");
11850
12029
  } catch {
11851
12030
  continue;
11852
12031
  }
@@ -11895,6 +12074,8 @@ function defaultSelectedModelForBackend(backendKind) {
11895
12074
  return void 0;
11896
12075
  case "opencode_cli":
11897
12076
  return void 0;
12077
+ case "gemini_cli":
12078
+ return void 0;
11898
12079
  case "supatest_cli":
11899
12080
  return "medium";
11900
12081
  default:
@@ -12096,7 +12277,7 @@ Prefer relative paths and keep your work scoped to this project.`
12096
12277
  const promptText = this.buildPromptText(runtimeConfig);
12097
12278
  const runtimeCommand = runtimeConfig.runtimeCommand || this.runtime.runtimeCommand;
12098
12279
  const runtimeArgs = runtimeConfig.runtimeArgs || this.runtime.runtimeArgs || [];
12099
- const backend = backendKind === "claude_cli" ? createClaudeCliBackend(runtimeCommand || "claude", runtimeArgs) : backendKind === "codex_app_server" ? createCodexRuntimeBackend(runtimeCommand || "codex", runtimeArgs) : backendKind === "copilot_cli" ? createCopilotCliBackend(runtimeCommand || "copilot", runtimeArgs) : backendKind === "cursor_agent_cli" ? createCursorAgentCliBackend(runtimeCommand || "cursor-agent", runtimeArgs) : backendKind === "gemini_cli" ? createGeminiCliBackend(runtimeCommand || "gemini", runtimeArgs) : backendKind === "droid_cli" ? createDroidCliBackend(runtimeCommand || "droid", runtimeArgs) : backendKind === "opencode_cli" ? createOpencodeCliBackend(runtimeCommand || "opencode", runtimeArgs) : backendKind === "kimi_cli" ? createKimiCliBackend(runtimeCommand || "kimi-cli", runtimeArgs) : backendKind === "supatest_cli" ? createSupatestCliBackend(runtimeCommand || "supatest", runtimeArgs) : createGenericCliPassthroughBackend(
12280
+ const backend = backendKind === "claude_cli" ? createClaudeCliBackend(runtimeCommand || "claude", runtimeArgs) : backendKind === "codex_app_server" ? createCodexRuntimeBackend(runtimeCommand || "codex", runtimeArgs) : backendKind === "copilot_cli" ? createCopilotCliBackend(runtimeCommand || "copilot", runtimeArgs) : backendKind === "cursor_agent_cli" ? createCursorAgentCliBackend(runtimeCommand || "cursor-agent", runtimeArgs) : backendKind === "gemini_cli" ? createGeminiCliBackend(runtimeCommand || "agy", runtimeArgs) : backendKind === "droid_cli" ? createDroidCliBackend(runtimeCommand || "droid", runtimeArgs) : backendKind === "opencode_cli" ? createOpencodeCliBackend(runtimeCommand || "opencode", runtimeArgs) : backendKind === "kimi_cli" ? createKimiCliBackend(runtimeCommand || "kimi-cli", runtimeArgs) : backendKind === "supatest_cli" ? createSupatestCliBackend(runtimeCommand || "supatest", runtimeArgs) : createGenericCliPassthroughBackend(
12100
12281
  runtimeCommand || "generic-cli",
12101
12282
  runtimeArgs
12102
12283
  );
@@ -12162,18 +12343,18 @@ Prefer relative paths and keep your work scoped to this project.`
12162
12343
  `[base-machine-agent] Transient error (attempt ${attempt + 1}/${_BaseMachineAgent.MAX_RETRIES + 1}), retrying in ${delayMs}ms: ${lastResult.error}`
12163
12344
  );
12164
12345
  void this.presenter.onLog(`Transient error, retrying in ${Math.round(delayMs / 1e3)}s...`);
12165
- await new Promise((resolve2) => {
12346
+ await new Promise((resolve22) => {
12166
12347
  const signal = this.abortController?.signal;
12167
12348
  if (signal?.aborted) {
12168
- resolve2();
12349
+ resolve22();
12169
12350
  return;
12170
12351
  }
12171
- const timeoutId = setTimeout(resolve2, delayMs);
12352
+ const timeoutId = setTimeout(resolve22, delayMs);
12172
12353
  signal?.addEventListener(
12173
12354
  "abort",
12174
12355
  () => {
12175
12356
  clearTimeout(timeoutId);
12176
- resolve2();
12357
+ resolve22();
12177
12358
  },
12178
12359
  { once: true }
12179
12360
  );
@@ -12243,6 +12424,195 @@ Prefer relative paths and keep your work scoped to this project.`
12243
12424
  }
12244
12425
  }
12245
12426
  };
12427
+ var IMAGE_MIME_BY_EXT = {
12428
+ ".gif": "image/gif",
12429
+ ".jpeg": "image/jpeg",
12430
+ ".jpg": "image/jpeg",
12431
+ ".png": "image/png",
12432
+ ".webp": "image/webp"
12433
+ };
12434
+ var VIDEO_MIME_BY_EXT = {
12435
+ ".mov": "video/quicktime",
12436
+ ".mp4": "video/mp4",
12437
+ ".webm": "video/webm"
12438
+ };
12439
+ var MAX_GENERATED_IMAGE_BYTES = 20 * 1024 * 1024;
12440
+ var MAX_SESSION_VIDEO_BYTES = 100 * 1024 * 1024;
12441
+ function isRecord(value2) {
12442
+ return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
12443
+ }
12444
+ function shouldExtractGeneratedImagesFromToolResult(toolName, content, options = {}) {
12445
+ return isGeneratedImageToolName(toolName) || isBrowserScreenshotToolName(toolName) || isAgentBrowserMediaInvocation(toolName, content, options.toolInput, "image");
12446
+ }
12447
+ function collectStringValues(value2, out) {
12448
+ if (typeof value2 === "string") {
12449
+ out.push(value2);
12450
+ return;
12451
+ }
12452
+ if (Array.isArray(value2)) {
12453
+ for (const item of value2) collectStringValues(item, out);
12454
+ return;
12455
+ }
12456
+ if (!value2 || typeof value2 !== "object") return;
12457
+ for (const entry of Object.values(value2)) {
12458
+ collectStringValues(entry, out);
12459
+ }
12460
+ }
12461
+ function collectContentStrings(content, toolInput) {
12462
+ const values = [];
12463
+ if (content) {
12464
+ values.push(content);
12465
+ try {
12466
+ collectStringValues(JSON.parse(content), values);
12467
+ } catch {
12468
+ }
12469
+ }
12470
+ collectStringValues(toolInput, values);
12471
+ return values;
12472
+ }
12473
+ function isAgentBrowserMediaInvocation(toolName, content, toolInput, kind) {
12474
+ if (kind === "image" && isBrowserScreenshotToolName(toolName)) return true;
12475
+ if (kind === "video" && isBrowserRecordingToolName(toolName)) return true;
12476
+ const haystack = collectContentStrings(content, toolInput).join("\n").toLowerCase();
12477
+ if (!/(^|[^a-z0-9_-])agent[-_]browser([^a-z0-9_-]|$)/.test(haystack)) return false;
12478
+ return kind === "image" ? /\b(screenshot|capture)\b/.test(haystack) : /\b(record|recording|video)\b/.test(haystack);
12479
+ }
12480
+ function extractPathCandidatesFromValues(values, extensionsPattern = "png|jpe?g|gif|webp") {
12481
+ const paths = /* @__PURE__ */ new Set();
12482
+ const filePathPattern = new RegExp(
12483
+ `(?:file:\\/\\/)?(?:~|\\.{1,2}|\\/)?[^\\s"'\`)\\]}<>]+?\\.(?:${extensionsPattern})\\b`,
12484
+ "gi"
12485
+ );
12486
+ for (const value2 of values) {
12487
+ for (const match of value2.matchAll(filePathPattern)) {
12488
+ paths.add(match[0]);
12489
+ }
12490
+ }
12491
+ return [...paths];
12492
+ }
12493
+ function extractPathCandidates(content, options, extensionsPattern = "png|jpe?g|gif|webp") {
12494
+ return [
12495
+ ...extractPathCandidatesFromValues(
12496
+ collectContentStrings(content, options.toolInput),
12497
+ extensionsPattern
12498
+ ),
12499
+ ...options.pathHints ?? []
12500
+ ];
12501
+ }
12502
+ function normalizePath(candidate, cwd) {
12503
+ let path = candidate.trim().replace(/^file:\/\//, "");
12504
+ try {
12505
+ path = decodeURI(path);
12506
+ } catch {
12507
+ }
12508
+ if (path.startsWith("~/")) return null;
12509
+ if ((0, import_path6.isAbsolute)(path)) return path;
12510
+ return cwd ? (0, import_path6.join)(cwd, path) : null;
12511
+ }
12512
+ function resolveToolCwd(cwd, toolInput) {
12513
+ if (!isRecord(toolInput)) return cwd;
12514
+ const raw = typeof toolInput.cwd === "string" ? toolInput.cwd : typeof toolInput.workdir === "string" ? toolInput.workdir : typeof toolInput.workingDirectory === "string" ? toolInput.workingDirectory : null;
12515
+ if (!raw) return cwd;
12516
+ return (0, import_path6.isAbsolute)(raw) || !cwd ? raw : (0, import_path6.join)(cwd, raw);
12517
+ }
12518
+ function extractBrowserMediaPathHints(content, cwd, toolName, options = {}, kind = "video") {
12519
+ if (!isAgentBrowserMediaInvocation(toolName, content, options.toolInput, kind)) return [];
12520
+ const extensionsPattern = kind === "video" ? "mp4|webm|mov" : "png|jpe?g|gif|webp";
12521
+ const effectiveCwd = resolveToolCwd(cwd, options.toolInput);
12522
+ return extractPathCandidatesFromValues(
12523
+ collectContentStrings(content, options.toolInput),
12524
+ extensionsPattern
12525
+ ).map((candidate) => normalizePath(candidate, effectiveCwd)).filter((path) => Boolean(path));
12526
+ }
12527
+ function readImageDimensions2(buffer, mimeType) {
12528
+ if (mimeType === "image/png" && buffer.length >= 24 && buffer.toString("ascii", 1, 4) === "PNG") {
12529
+ return { width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) };
12530
+ }
12531
+ if (mimeType === "image/gif" && buffer.length >= 10 && buffer.toString("ascii", 0, 3) === "GIF") {
12532
+ return { width: buffer.readUInt16LE(6), height: buffer.readUInt16LE(8) };
12533
+ }
12534
+ if (mimeType === "image/jpeg" && buffer.length > 4) {
12535
+ let offset = 2;
12536
+ while (offset + 9 < buffer.length) {
12537
+ if (buffer[offset] !== 255) break;
12538
+ const marker = buffer[offset + 1];
12539
+ const length = buffer.readUInt16BE(offset + 2);
12540
+ if (length < 2) break;
12541
+ if (marker >= 192 && marker <= 207 && ![196, 200, 204].includes(marker)) {
12542
+ return {
12543
+ width: buffer.readUInt16BE(offset + 7),
12544
+ height: buffer.readUInt16BE(offset + 5)
12545
+ };
12546
+ }
12547
+ offset += 2 + length;
12548
+ }
12549
+ }
12550
+ return { width: null, height: null };
12551
+ }
12552
+ function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName, options = {}) {
12553
+ if (!shouldExtractGeneratedImagesFromToolResult(toolName, content, options)) return [];
12554
+ const images = [];
12555
+ const seen = /* @__PURE__ */ new Set();
12556
+ const effectiveCwd = resolveToolCwd(cwd, options.toolInput);
12557
+ for (const candidate of extractPathCandidates(content, options)) {
12558
+ const path = normalizePath(candidate, effectiveCwd);
12559
+ if (!path || seen.has(path) || !(0, import_fs6.existsSync)(path)) continue;
12560
+ seen.add(path);
12561
+ const ext = (0, import_path6.extname)(path).toLowerCase();
12562
+ const mimeType = IMAGE_MIME_BY_EXT[ext];
12563
+ if (!mimeType) continue;
12564
+ const stat = (0, import_fs6.statSync)(path);
12565
+ if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_GENERATED_IMAGE_BYTES) continue;
12566
+ const buffer = (0, import_fs6.readFileSync)(path);
12567
+ const { width, height } = readImageDimensions2(buffer, mimeType);
12568
+ images.push({
12569
+ type: "generated_image",
12570
+ id: (0, import_crypto2.randomUUID)(),
12571
+ filename: (0, import_path6.basename)(path),
12572
+ mimeType,
12573
+ size: buffer.length,
12574
+ width,
12575
+ height,
12576
+ data: buffer.toString("base64"),
12577
+ sourcePath: path,
12578
+ sourceHash: (0, import_crypto2.createHash)("sha256").update(buffer).digest("hex"),
12579
+ toolId,
12580
+ ts: Date.now()
12581
+ });
12582
+ }
12583
+ return images;
12584
+ }
12585
+ function extractSessionMediaFromToolResult(toolId, content, cwd, toolName, options = {}) {
12586
+ if (!isAgentBrowserMediaInvocation(toolName, content, options.toolInput, "video")) return [];
12587
+ const media = [];
12588
+ const seen = /* @__PURE__ */ new Set();
12589
+ const effectiveCwd = resolveToolCwd(cwd, options.toolInput);
12590
+ for (const candidate of extractPathCandidates(content, options, "mp4|webm|mov")) {
12591
+ const path = normalizePath(candidate, effectiveCwd);
12592
+ if (!path || seen.has(path) || !(0, import_fs6.existsSync)(path)) continue;
12593
+ seen.add(path);
12594
+ const ext = (0, import_path6.extname)(path).toLowerCase();
12595
+ const mimeType = VIDEO_MIME_BY_EXT[ext];
12596
+ if (!mimeType) continue;
12597
+ const stat = (0, import_fs6.statSync)(path);
12598
+ if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_SESSION_VIDEO_BYTES) continue;
12599
+ const buffer = (0, import_fs6.readFileSync)(path);
12600
+ media.push({
12601
+ type: "session_media",
12602
+ kind: "video",
12603
+ id: (0, import_crypto2.randomUUID)(),
12604
+ filename: (0, import_path6.basename)(path),
12605
+ mimeType,
12606
+ size: buffer.length,
12607
+ data: buffer.toString("base64"),
12608
+ sourcePath: path,
12609
+ sourceHash: (0, import_crypto2.createHash)("sha256").update(buffer).digest("hex"),
12610
+ toolId,
12611
+ ts: Date.now()
12612
+ });
12613
+ }
12614
+ return media;
12615
+ }
12246
12616
 
12247
12617
  // src/core-agent.ts
12248
12618
  var CoreAgent = class _CoreAgent extends BaseMachineAgent {
@@ -12541,149 +12911,162 @@ ${meta.description}`);
12541
12911
  }
12542
12912
  };
12543
12913
 
12544
- // src/generated-images.ts
12545
- var import_node_crypto = require("crypto");
12546
- var import_node_fs3 = require("fs");
12547
- var import_node_path3 = require("path");
12548
- var IMAGE_MIME_BY_EXT = {
12549
- ".gif": "image/gif",
12550
- ".jpeg": "image/jpeg",
12551
- ".jpg": "image/jpeg",
12552
- ".png": "image/png",
12553
- ".webp": "image/webp"
12554
- };
12555
- var MAX_GENERATED_IMAGE_BYTES = 20 * 1024 * 1024;
12556
- function isGeneratedImageToolName(toolName) {
12557
- if (!toolName) return false;
12558
- const normalized = toolName.toLowerCase().replace(/[^a-z0-9]+/g, "_");
12559
- return [
12560
- "image_generation",
12561
- "image_gen",
12562
- "imagegen",
12563
- "generate_image",
12564
- "edit_image",
12565
- "text_to_image",
12566
- "images_generate"
12567
- ].some((token) => normalized.includes(token));
12568
- }
12569
- function shouldExtractGeneratedImagesFromToolResult(toolName) {
12570
- return isGeneratedImageToolName(toolName);
12571
- }
12572
- function collectStringValues(value2, out) {
12573
- if (typeof value2 === "string") {
12574
- out.push(value2);
12575
- return;
12576
- }
12577
- if (Array.isArray(value2)) {
12578
- for (const item of value2) collectStringValues(item, out);
12579
- return;
12914
+ // src/version.ts
12915
+ var AGENT_VERSION = "0.1.27";
12916
+
12917
+ // src/daemon.ts
12918
+ var OPENCODE_FREE_MODEL_IDS = [
12919
+ "opencode/north-mini-code-free",
12920
+ "opencode/nemotron-3-ultra-free",
12921
+ "opencode/deepseek-v4-flash-free",
12922
+ "opencode/mimo-v2.5-free",
12923
+ "opencode/big-pickle"
12924
+ ];
12925
+ var MODEL_DISCOVERY_TIMEOUT_MS = 15e3;
12926
+ var PRODUCTION_API_URL = "https://api.aiden-platform.com";
12927
+ var PRODUCTION_WS_URL = "wss://ws.aiden-platform.com";
12928
+ var LOCAL_API_URL = "http://localhost:8400";
12929
+ var LOCAL_WS_URL = "ws://localhost:8401";
12930
+ function requiredString(params, key) {
12931
+ const value2 = params[key];
12932
+ if (typeof value2 !== "string" || value2.trim() === "") {
12933
+ throw new Error(`${key} is required`);
12580
12934
  }
12581
- if (!value2 || typeof value2 !== "object") return;
12582
- for (const entry of Object.values(value2)) {
12583
- collectStringValues(entry, out);
12935
+ return value2;
12936
+ }
12937
+ function assertValidGitRef(ref, label) {
12938
+ if (!ref || ref.startsWith("-") || ref.includes("\0")) {
12939
+ throw new Error(`${label} is not a safe git ref`);
12584
12940
  }
12941
+ const result = (0, import_node_child_process3.spawnSync)("git", ["check-ref-format", "--branch", ref], {
12942
+ env: getDaemonCliEnvironment(),
12943
+ encoding: "utf8"
12944
+ });
12945
+ if (result.status !== 0) throw new Error(`${label} is not a valid git ref`);
12585
12946
  }
12586
- function candidateStrings(content) {
12587
- const values = [content];
12588
- try {
12589
- collectStringValues(JSON.parse(content), values);
12590
- } catch {
12947
+ function isPathContained(childPath, parentPath) {
12948
+ const rel = (0, import_node_path3.relative)((0, import_node_path3.resolve)(parentPath), (0, import_node_path3.resolve)(childPath));
12949
+ return rel === "" || !(0, import_node_path3.isAbsolute)(rel) && rel !== ".." && !rel.startsWith(`..${import_node_path3.sep}`);
12950
+ }
12951
+ function assertPathContained(childPath, parentPath, label) {
12952
+ if (!isPathContained(childPath, parentPath)) {
12953
+ throw new Error(`${label} escapes allowed directory`);
12591
12954
  }
12592
- return values;
12593
12955
  }
12594
- function extractPathCandidates(content) {
12595
- const paths = /* @__PURE__ */ new Set();
12596
- const imagePathPattern = /(?:file:\/\/)?(?:~|\.{1,2}|\/)?[^\s"'`)\]}<>]+?\.(?:png|jpe?g|gif|webp)\b/gi;
12597
- for (const value2 of candidateStrings(content)) {
12598
- for (const match of value2.matchAll(imagePathPattern)) {
12599
- paths.add(match[0]);
12600
- }
12956
+ function git(cwd, args) {
12957
+ const result = (0, import_node_child_process3.spawnSync)("git", args, {
12958
+ cwd,
12959
+ env: getDaemonCliEnvironment(),
12960
+ encoding: "utf8",
12961
+ maxBuffer: 10 * 1024 * 1024
12962
+ });
12963
+ if (result.status !== 0) {
12964
+ throw new Error((result.stderr || result.stdout || "git command failed").trim());
12601
12965
  }
12602
- return [...paths];
12966
+ return result.stdout.trim();
12603
12967
  }
12604
- function normalizePath(candidate, cwd) {
12605
- let path = candidate.trim().replace(/^file:\/\//, "");
12968
+ function gitSafe(cwd, args) {
12606
12969
  try {
12607
- path = decodeURI(path);
12970
+ return git(cwd, args);
12608
12971
  } catch {
12972
+ return "";
12609
12973
  }
12610
- if (path.startsWith("~/")) return null;
12611
- if ((0, import_node_path3.isAbsolute)(path)) return path;
12612
- return cwd ? (0, import_node_path3.join)(cwd, path) : null;
12613
12974
  }
12614
- function readImageDimensions2(buffer, mimeType) {
12615
- if (mimeType === "image/png" && buffer.length >= 24 && buffer.toString("ascii", 1, 4) === "PNG") {
12616
- return { width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) };
12617
- }
12618
- if (mimeType === "image/gif" && buffer.length >= 10 && buffer.toString("ascii", 0, 3) === "GIF") {
12619
- return { width: buffer.readUInt16LE(6), height: buffer.readUInt16LE(8) };
12975
+ function worktreeDir(projectPath) {
12976
+ const resolved = (0, import_node_path3.resolve)(projectPath);
12977
+ return (0, import_node_path3.join)((0, import_node_path3.dirname)(resolved), `${(0, import_node_path3.basename)(resolved)}-aiden-worktrees`);
12978
+ }
12979
+ function pathSegment(value2) {
12980
+ return value2.toLowerCase().replace(/^aiden\//, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "task";
12981
+ }
12982
+ function repoName(repoUrl, localPath) {
12983
+ return repoUrl.replace(/\.git$/, "").split("/").pop() || (0, import_node_path3.basename)(localPath) || "repo";
12984
+ }
12985
+ function findWorktreeByBranch(repoPath, branchName) {
12986
+ const output = gitSafe(repoPath, ["worktree", "list", "--porcelain"]);
12987
+ const entries = output ? output.split("\n\n") : [];
12988
+ for (const entry of entries) {
12989
+ const lines = entry.split("\n");
12990
+ const path = lines.find((line) => line.startsWith("worktree "))?.slice("worktree ".length);
12991
+ const branch = lines.find((line) => line.startsWith("branch "))?.slice("branch ".length).replace("refs/heads/", "");
12992
+ if (path && branch === branchName) return path;
12620
12993
  }
12621
- if (mimeType === "image/jpeg" && buffer.length > 4) {
12622
- let offset = 2;
12623
- while (offset + 9 < buffer.length) {
12624
- if (buffer[offset] !== 255) break;
12625
- const marker = buffer[offset + 1];
12626
- const length = buffer.readUInt16BE(offset + 2);
12627
- if (length < 2) break;
12628
- if (marker >= 192 && marker <= 207 && ![196, 200, 204].includes(marker)) {
12994
+ return null;
12995
+ }
12996
+ function ensureGitWorktree(input) {
12997
+ assertValidGitRef(input.branchName, "branchName");
12998
+ assertValidGitRef(input.baseBranch, "baseBranch");
12999
+ const repoPath = (0, import_node_path3.resolve)(input.repoPath);
13000
+ const root = input.allowedRoot ?? worktreeDir(repoPath);
13001
+ const existingPath = findWorktreeByBranch(input.repoPath, input.branchName);
13002
+ if (existingPath) {
13003
+ if ((0, import_node_path3.resolve)(existingPath) === repoPath) {
13004
+ throw new Error(`${input.branchName} is checked out in the shared checkout`);
13005
+ }
13006
+ return existingPath;
13007
+ }
13008
+ assertPathContained(input.worktreePath, root, "worktreePath");
13009
+ (0, import_node_fs3.mkdirSync)((0, import_node_path3.dirname)(input.worktreePath), { recursive: true });
13010
+ if (!gitSafe(input.repoPath, ["branch", "--list", input.branchName])) {
13011
+ git(input.repoPath, ["branch", input.branchName, input.baseBranch]);
13012
+ }
13013
+ if ((0, import_node_fs3.existsSync)(input.worktreePath)) {
13014
+ if (gitSafe(input.worktreePath, ["branch", "--show-current"]) === input.branchName) {
13015
+ return input.worktreePath;
13016
+ }
13017
+ throw new Error("worktree path already exists for a different checkout");
13018
+ }
13019
+ git(input.repoPath, ["worktree", "add", input.worktreePath, input.branchName]);
13020
+ return input.worktreePath;
13021
+ }
13022
+ function ensureWorktree(params) {
13023
+ const input = {
13024
+ projectPath: requiredString(params, "projectPath"),
13025
+ taskId: requiredString(params, "taskId"),
13026
+ branchName: requiredString(params, "branchName"),
13027
+ baseBranch: typeof params.baseBranch === "string" ? params.baseBranch : "main",
13028
+ repoLocalPaths: Array.isArray(params.repoLocalPaths) ? params.repoLocalPaths : []
13029
+ };
13030
+ const taskRoot = (0, import_node_path3.join)(
13031
+ worktreeDir(input.projectPath),
13032
+ `${pathSegment(input.branchName)}-${input.taskId.slice(0, 8)}`
13033
+ );
13034
+ if (input.repoLocalPaths?.length) {
13035
+ return {
13036
+ worktrees: input.repoLocalPaths.map((repo) => {
13037
+ const baseBranch = repo.branch || input.baseBranch || "main";
13038
+ const worktreePath2 = ensureGitWorktree({
13039
+ repoPath: repo.localPath,
13040
+ worktreePath: (0, import_node_path3.join)(taskRoot, pathSegment(repoName(repo.repoUrl, repo.localPath))),
13041
+ branchName: input.branchName,
13042
+ baseBranch,
13043
+ allowedRoot: worktreeDir(input.projectPath)
13044
+ });
12629
13045
  return {
12630
- width: buffer.readUInt16BE(offset + 7),
12631
- height: buffer.readUInt16BE(offset + 5)
13046
+ repoId: repo.repoId ?? null,
13047
+ repoUrl: repo.repoUrl,
13048
+ worktreePath: worktreePath2,
13049
+ branchName: input.branchName,
13050
+ baseBranch
12632
13051
  };
12633
- }
12634
- offset += 2 + length;
12635
- }
13052
+ })
13053
+ };
12636
13054
  }
12637
- return { width: null, height: null };
13055
+ const worktreePath = ensureGitWorktree({
13056
+ repoPath: input.projectPath,
13057
+ worktreePath: taskRoot,
13058
+ branchName: input.branchName,
13059
+ baseBranch: input.baseBranch
13060
+ });
13061
+ return {
13062
+ worktrees: [{ worktreePath, branchName: input.branchName, baseBranch: input.baseBranch }]
13063
+ };
12638
13064
  }
12639
- function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName) {
12640
- if (!shouldExtractGeneratedImagesFromToolResult(toolName)) return [];
12641
- const images = [];
12642
- const seen = /* @__PURE__ */ new Set();
12643
- for (const candidate of extractPathCandidates(content)) {
12644
- const path = normalizePath(candidate, cwd);
12645
- if (!path || seen.has(path) || !(0, import_node_fs3.existsSync)(path)) continue;
12646
- seen.add(path);
12647
- const ext = (0, import_node_path3.extname)(path).toLowerCase();
12648
- const mimeType = IMAGE_MIME_BY_EXT[ext];
12649
- if (!mimeType) continue;
12650
- const stat = (0, import_node_fs3.statSync)(path);
12651
- if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_GENERATED_IMAGE_BYTES) continue;
12652
- const buffer = (0, import_node_fs3.readFileSync)(path);
12653
- const { width, height } = readImageDimensions2(buffer, mimeType);
12654
- images.push({
12655
- type: "generated_image",
12656
- id: (0, import_node_crypto.randomUUID)(),
12657
- filename: (0, import_node_path3.basename)(path),
12658
- mimeType,
12659
- size: buffer.length,
12660
- width,
12661
- height,
12662
- data: buffer.toString("base64"),
12663
- sourcePath: path,
12664
- sourceHash: (0, import_node_crypto.createHash)("sha256").update(buffer).digest("hex"),
12665
- toolId,
12666
- ts: Date.now()
12667
- });
12668
- }
12669
- return images;
13065
+ function resolveRuntimeCwd(payload) {
13066
+ const multiRepoWorktree = payload.worktreeInfo?.allWorktrees?.[0]?.worktreePath;
13067
+ if (multiRepoWorktree) return (0, import_node_path3.dirname)(multiRepoWorktree);
13068
+ return payload.worktreeInfo?.worktreePath ?? payload.projectPath ?? process.cwd();
12670
13069
  }
12671
-
12672
- // src/version.ts
12673
- var AGENT_VERSION = "0.1.26";
12674
-
12675
- // src/daemon.ts
12676
- var OPENCODE_FREE_MODEL_IDS = [
12677
- "opencode/north-mini-code-free",
12678
- "opencode/nemotron-3-ultra-free",
12679
- "opencode/deepseek-v4-flash-free",
12680
- "opencode/mimo-v2.5-free",
12681
- "opencode/big-pickle"
12682
- ];
12683
- var PRODUCTION_API_URL = "https://api.aiden-platform.com";
12684
- var PRODUCTION_WS_URL = "wss://ws.aiden-platform.com";
12685
- var LOCAL_API_URL = "http://localhost:8400";
12686
- var LOCAL_WS_URL = "ws://localhost:8401";
12687
13070
  function correlationLogFields(input) {
12688
13071
  return Object.fromEntries(
12689
13072
  Object.entries({
@@ -12722,33 +13105,33 @@ function describeOccupiedLocalDaemon(port) {
12722
13105
  return `local daemon API port ${port} is already in use. If Aiden desktop is open, it manages the daemon automatically. Quit Aiden desktop or stop the existing daemon before starting another.`;
12723
13106
  }
12724
13107
  function getConfigPath() {
12725
- return process.env.AIDEN_AGENT_CONFIG_PATH ?? (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".aiden", "agent", "config.json");
13108
+ return process.env.AIDEN_AGENT_CONFIG_PATH ?? (0, import_node_path3.join)((0, import_node_os4.homedir)(), ".aiden", "agent", "config.json");
12726
13109
  }
12727
13110
  function getEndpointDefaultsPath() {
12728
- return process.env.AIDEN_AGENT_ENDPOINTS_PATH ?? (0, import_node_path4.join)((0, import_node_path4.dirname)(getConfigPath()), "endpoints.json");
13111
+ return process.env.AIDEN_AGENT_ENDPOINTS_PATH ?? (0, import_node_path3.join)((0, import_node_path3.dirname)(getConfigPath()), "endpoints.json");
12729
13112
  }
12730
13113
  function readConfig() {
12731
13114
  const configPath = getConfigPath();
12732
- if (!(0, import_node_fs4.existsSync)(configPath)) return {};
12733
- return JSON.parse((0, import_node_fs4.readFileSync)(configPath, "utf8"));
13115
+ if (!(0, import_node_fs3.existsSync)(configPath)) return {};
13116
+ return JSON.parse((0, import_node_fs3.readFileSync)(configPath, "utf8"));
12734
13117
  }
12735
13118
  function readEndpointDefaults() {
12736
13119
  const endpointsPath = getEndpointDefaultsPath();
12737
- if (!(0, import_node_fs4.existsSync)(endpointsPath)) return {};
13120
+ if (!(0, import_node_fs3.existsSync)(endpointsPath)) return {};
12738
13121
  try {
12739
- return JSON.parse((0, import_node_fs4.readFileSync)(endpointsPath, "utf8"));
13122
+ return JSON.parse((0, import_node_fs3.readFileSync)(endpointsPath, "utf8"));
12740
13123
  } catch {
12741
13124
  return {};
12742
13125
  }
12743
13126
  }
12744
13127
  function writeConfig(config) {
12745
13128
  const configPath = getConfigPath();
12746
- (0, import_node_fs4.mkdirSync)((0, import_node_path4.dirname)(configPath), { recursive: true, mode: 448 });
12747
- (0, import_node_fs4.writeFileSync)(configPath, JSON.stringify(config, null, 2), { mode: 384 });
13129
+ (0, import_node_fs3.mkdirSync)((0, import_node_path3.dirname)(configPath), { recursive: true, mode: 448 });
13130
+ (0, import_node_fs3.writeFileSync)(configPath, JSON.stringify(config, null, 2), { mode: 384 });
12748
13131
  }
12749
13132
  function removeConfig() {
12750
13133
  const configPath = getConfigPath();
12751
- if ((0, import_node_fs4.existsSync)(configPath)) (0, import_node_fs4.rmSync)(configPath, { force: true });
13134
+ if ((0, import_node_fs3.existsSync)(configPath)) (0, import_node_fs3.rmSync)(configPath, { force: true });
12752
13135
  }
12753
13136
  function argValue(args, name) {
12754
13137
  const index = args.indexOf(name);
@@ -12784,7 +13167,7 @@ function getLocalApiPort(args, stored) {
12784
13167
  }
12785
13168
  function sleep(ms) {
12786
13169
  if (ms <= 0) return Promise.resolve();
12787
- return new Promise((resolve2) => setTimeout(resolve2, ms));
13170
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
12788
13171
  }
12789
13172
  function normalizeBackendKind(backendKind) {
12790
13173
  if (backendKind === "codex") return "codex_app_server";
@@ -12816,6 +13199,14 @@ function extractModelIdsFromJson(value2) {
12816
13199
  }
12817
13200
  return [];
12818
13201
  }
13202
+ function extractModelIdsFromText(value2) {
13203
+ const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
13204
+ const ignoredLinePattern = /^(available models|models|usage|error|flag provided|try |agy\b)/i;
13205
+ const ids = value2.split(/\r?\n/).map(
13206
+ (line) => line.replace(ansiPattern, "").replace(/^[-*•]\s*/, "").trim()
13207
+ ).filter((line) => line.length > 0 && !ignoredLinePattern.test(line));
13208
+ return [...new Set(ids)];
13209
+ }
12819
13210
  function discoverOpenCodeModels(executable, env) {
12820
13211
  for (const args of [
12821
13212
  ["models", "--json"],
@@ -12824,7 +13215,7 @@ function discoverOpenCodeModels(executable, env) {
12824
13215
  ]) {
12825
13216
  const result = (0, import_node_child_process3.spawnSync)(executable, args, {
12826
13217
  encoding: "utf8",
12827
- timeout: 5e3,
13218
+ timeout: MODEL_DISCOVERY_TIMEOUT_MS,
12828
13219
  env,
12829
13220
  windowsHide: true
12830
13221
  });
@@ -12837,7 +13228,27 @@ function discoverOpenCodeModels(executable, env) {
12837
13228
  }
12838
13229
  return OPENCODE_FREE_MODEL_IDS;
12839
13230
  }
13231
+ function discoverAntigravityModels(executable, env) {
13232
+ for (const args of [["models", "--json"], ["models"]]) {
13233
+ const result = (0, import_node_child_process3.spawnSync)(executable, args, {
13234
+ encoding: "utf8",
13235
+ timeout: MODEL_DISCOVERY_TIMEOUT_MS,
13236
+ env,
13237
+ windowsHide: true
13238
+ });
13239
+ if (!result || result.error || result.status !== 0 || !result.stdout.trim()) continue;
13240
+ try {
13241
+ const models = extractModelIdsFromJson(JSON.parse(result.stdout));
13242
+ if (models.length > 0) return models;
13243
+ } catch {
13244
+ const models = extractModelIdsFromText(result.stdout);
13245
+ if (models.length > 0) return models;
13246
+ }
13247
+ }
13248
+ return [];
13249
+ }
12840
13250
  function discoverProviderModels(provider, executable, env) {
13251
+ if (provider === "gemini_cli") return discoverAntigravityModels(executable, env);
12841
13252
  if (provider === "opencode_cli") return discoverOpenCodeModels(executable, env);
12842
13253
  return [];
12843
13254
  }
@@ -12918,7 +13329,7 @@ function collectRuntimeMetadata() {
12918
13329
  metadata.cpuCores = cpuList.length;
12919
13330
  }
12920
13331
  try {
12921
- const disk = (0, import_node_fs4.statfsSync)((0, import_node_os4.homedir)());
13332
+ const disk = (0, import_node_fs3.statfsSync)((0, import_node_os4.homedir)());
12922
13333
  metadata.diskFreeBytes = disk.bavail * disk.bsize;
12923
13334
  metadata.diskTotalBytes = disk.blocks * disk.bsize;
12924
13335
  } catch {
@@ -12946,7 +13357,10 @@ var RuntimePresenter = class {
12946
13357
  this.onRunComplete = onRunComplete;
12947
13358
  }
12948
13359
  emittedGeneratedImageKeys = /* @__PURE__ */ new Set();
13360
+ emittedSessionMediaKeys = /* @__PURE__ */ new Set();
12949
13361
  toolNamesById = /* @__PURE__ */ new Map();
13362
+ toolInputsById = /* @__PURE__ */ new Map();
13363
+ browserRecordingPathHints = /* @__PURE__ */ new Set();
12950
13364
  emitEvent(event) {
12951
13365
  this.touchRunnerActivity?.();
12952
13366
  this.socket.emit("agent.event", {
@@ -12969,6 +13383,12 @@ var RuntimePresenter = class {
12969
13383
  }
12970
13384
  onToolUse(tool, input, toolId) {
12971
13385
  this.toolNamesById.set(toolId, tool);
13386
+ this.toolInputsById.set(toolId, input);
13387
+ for (const path of extractBrowserMediaPathHints(void 0, this.cwd, tool, {
13388
+ toolInput: input
13389
+ })) {
13390
+ this.browserRecordingPathHints.add(path);
13391
+ }
12972
13392
  this.emitEvent({ type: "tool_use", id: toolId, name: tool, input, ts: Date.now() });
12973
13393
  }
12974
13394
  onToolResult(toolId, content, isError) {
@@ -12981,9 +13401,27 @@ var RuntimePresenter = class {
12981
13401
  });
12982
13402
  if (isError) return;
12983
13403
  const toolName = this.toolNamesById.get(toolId);
12984
- for (const image of extractGeneratedImagesFromToolResult(toolId, content, this.cwd, toolName)) {
13404
+ const toolInput = this.toolInputsById.get(toolId);
13405
+ const options = { toolInput, pathHints: [...this.browserRecordingPathHints] };
13406
+ for (const image of extractGeneratedImagesFromToolResult(
13407
+ toolId,
13408
+ content,
13409
+ this.cwd,
13410
+ toolName,
13411
+ options
13412
+ )) {
12985
13413
  this.emitGeneratedImage(image);
12986
13414
  }
13415
+ for (const media of extractSessionMediaFromToolResult(
13416
+ toolId,
13417
+ content,
13418
+ this.cwd,
13419
+ toolName,
13420
+ options
13421
+ )) {
13422
+ this.emitSessionMedia(media);
13423
+ if (media.sourcePath) this.browserRecordingPathHints.delete(media.sourcePath);
13424
+ }
12987
13425
  }
12988
13426
  onGeneratedImage(image) {
12989
13427
  this.emitGeneratedImage(image);
@@ -12994,6 +13432,15 @@ var RuntimePresenter = class {
12994
13432
  this.emittedGeneratedImageKeys.add(key);
12995
13433
  this.emitEvent(image);
12996
13434
  }
13435
+ onSessionMedia(media) {
13436
+ this.emitSessionMedia(media);
13437
+ }
13438
+ emitSessionMedia(media) {
13439
+ const key = media.sourceHash ?? `${media.sourcePath ?? media.id}:${media.size}`;
13440
+ if (this.emittedSessionMediaKeys.has(key)) return;
13441
+ this.emittedSessionMediaKeys.add(key);
13442
+ this.emitEvent(media);
13443
+ }
12997
13444
  onTurnComplete(content) {
12998
13445
  this.emitEvent({ type: "turn_complete", content, ts: Date.now() });
12999
13446
  }
@@ -13084,7 +13531,7 @@ async function setupDaemon(args) {
13084
13531
  runtimeId: body.runtime.id,
13085
13532
  runtimeToken: body.runtimeToken,
13086
13533
  localApiPort: getLocalApiPort(args, {}),
13087
- localApiToken: (0, import_node_crypto2.randomBytes)(24).toString("hex"),
13534
+ localApiToken: (0, import_node_crypto.randomBytes)(24).toString("hex"),
13088
13535
  displayName: body.runtime.displayName ?? displayName
13089
13536
  });
13090
13537
  console.info("[aiden-agent] Runtime registered", {
@@ -13135,7 +13582,7 @@ async function loginWithDeviceCode(args) {
13135
13582
  runtimeId: body.runtime.id,
13136
13583
  runtimeToken: body.runtimeToken,
13137
13584
  localApiPort: getLocalApiPort(args, {}),
13138
- localApiToken: (0, import_node_crypto2.randomBytes)(24).toString("hex"),
13585
+ localApiToken: (0, import_node_crypto.randomBytes)(24).toString("hex"),
13139
13586
  displayName: body.runtime.displayName ?? displayName
13140
13587
  });
13141
13588
  console.info("[aiden-agent] Runtime registered", {
@@ -13152,7 +13599,7 @@ async function startDaemon(args) {
13152
13599
  const runtimeToken = argValue(args, "--token") ?? process.env.AIDEN_RUNTIME_TOKEN ?? stored.runtimeToken;
13153
13600
  const wsUrl = argValue(args, "--ws-url") ?? process.env.AIDEN_WS_URL ?? stored.wsUrl;
13154
13601
  let localApiPort = getLocalApiPort(args, stored);
13155
- const localApiToken = stored.localApiToken ?? (0, import_node_crypto2.randomBytes)(24).toString("hex");
13602
+ const localApiToken = stored.localApiToken ?? (0, import_node_crypto.randomBytes)(24).toString("hex");
13156
13603
  if (!runtimeId || !runtimeToken || !wsUrl) {
13157
13604
  throw new Error(
13158
13605
  "daemon requires runtimeId, runtime token, and wsUrl. Run setup first or pass --runtime-id/--token/--ws-url."
@@ -13211,6 +13658,23 @@ async function startDaemon(args) {
13211
13658
  pushLog(`disconnected ${reason}`);
13212
13659
  console.info("[aiden-agent] Daemon disconnected:", reason);
13213
13660
  });
13661
+ socket.on("desktop:rpc", (message) => {
13662
+ if (!message?.id || !message.method) return;
13663
+ try {
13664
+ const params = message.params ?? {};
13665
+ if (message.method !== "worktree.ensure") {
13666
+ throw new Error(`Method not found: ${message.method}`);
13667
+ }
13668
+ const result = ensureWorktree(params);
13669
+ socket.emit("desktop:rpc", { jsonrpc: "2.0", id: message.id, result });
13670
+ } catch (error) {
13671
+ socket.emit("desktop:rpc", {
13672
+ jsonrpc: "2.0",
13673
+ id: message.id,
13674
+ error: { code: -32e3, message: error.message }
13675
+ });
13676
+ }
13677
+ });
13214
13678
  socket.on("agent.execute", (payload) => {
13215
13679
  if (!payload?.runId || !payload.conversationId || typeof payload.content !== "string") {
13216
13680
  return;
@@ -13240,7 +13704,7 @@ async function startDaemon(args) {
13240
13704
  });
13241
13705
  pushLog(`accepted run=${payload.runId}`);
13242
13706
  console.info("[aiden-agent] Agent run accepted", correlation);
13243
- const cwd = payload.projectPath ?? process.cwd();
13707
+ const cwd = resolveRuntimeCwd(payload);
13244
13708
  const nowMs = Date.now();
13245
13709
  const touchActivity = () => {
13246
13710
  const entry = activeAgents.get(payload.runId);
@@ -13401,7 +13865,7 @@ async function startDaemon(args) {
13401
13865
  `${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit Aiden desktop or run setup again.`
13402
13866
  );
13403
13867
  }
13404
- await new Promise((resolve2, reject) => {
13868
+ await new Promise((resolve3, reject) => {
13405
13869
  const onError = (error) => {
13406
13870
  clearInterval(heartbeat);
13407
13871
  socket.disconnect();
@@ -13428,7 +13892,7 @@ async function startDaemon(args) {
13428
13892
  if (address && typeof address === "object") localApiPort = address.port;
13429
13893
  pushLog(`local_api listening port=${localApiPort}`);
13430
13894
  console.info("[aiden-agent] Local daemon API listening", { port: localApiPort });
13431
- resolve2();
13895
+ resolve3();
13432
13896
  });
13433
13897
  });
13434
13898
  let stopped = false;
@@ -13441,8 +13905,8 @@ async function startDaemon(args) {
13441
13905
  }
13442
13906
  activeAgents.clear();
13443
13907
  socket.disconnect();
13444
- await new Promise((resolve2) => {
13445
- localServer.close(() => resolve2());
13908
+ await new Promise((resolve3) => {
13909
+ localServer.close(() => resolve3());
13446
13910
  });
13447
13911
  };
13448
13912
  const controller = {
@@ -13726,7 +14190,10 @@ var WebPresenter = class {
13726
14190
  }
13727
14191
  wsClient;
13728
14192
  emittedGeneratedImageKeys = /* @__PURE__ */ new Set();
14193
+ emittedSessionMediaKeys = /* @__PURE__ */ new Set();
13729
14194
  toolNamesById = /* @__PURE__ */ new Map();
14195
+ toolInputsById = /* @__PURE__ */ new Map();
14196
+ browserRecordingPathHints = /* @__PURE__ */ new Set();
13730
14197
  suppressSessionLifecycle = false;
13731
14198
  setSuppressSessionLifecycle(suppress) {
13732
14199
  this.suppressSessionLifecycle = suppress;
@@ -13745,6 +14212,12 @@ var WebPresenter = class {
13745
14212
  }
13746
14213
  onToolUse(tool, input, toolId) {
13747
14214
  this.toolNamesById.set(toolId, tool);
14215
+ this.toolInputsById.set(toolId, input);
14216
+ for (const path of extractBrowserMediaPathHints(void 0, this.cwd, tool, {
14217
+ toolInput: input
14218
+ })) {
14219
+ this.browserRecordingPathHints.add(path);
14220
+ }
13748
14221
  this.wsClient.emitEvent({ type: "tool_use", id: toolId, name: tool, input, ts: Date.now() });
13749
14222
  }
13750
14223
  onToolResult(toolId, content, isError) {
@@ -13757,9 +14230,27 @@ var WebPresenter = class {
13757
14230
  });
13758
14231
  if (isError) return;
13759
14232
  const toolName = this.toolNamesById.get(toolId);
13760
- for (const image of extractGeneratedImagesFromToolResult(toolId, content, this.cwd, toolName)) {
14233
+ const toolInput = this.toolInputsById.get(toolId);
14234
+ const options = { toolInput, pathHints: [...this.browserRecordingPathHints] };
14235
+ for (const image of extractGeneratedImagesFromToolResult(
14236
+ toolId,
14237
+ content,
14238
+ this.cwd,
14239
+ toolName,
14240
+ options
14241
+ )) {
13761
14242
  this.emitGeneratedImage(image);
13762
14243
  }
14244
+ for (const media of extractSessionMediaFromToolResult(
14245
+ toolId,
14246
+ content,
14247
+ this.cwd,
14248
+ toolName,
14249
+ options
14250
+ )) {
14251
+ this.emitSessionMedia(media);
14252
+ if (media.sourcePath) this.browserRecordingPathHints.delete(media.sourcePath);
14253
+ }
13763
14254
  }
13764
14255
  onGeneratedImage(image) {
13765
14256
  this.emitGeneratedImage(image);
@@ -13770,6 +14261,15 @@ var WebPresenter = class {
13770
14261
  this.emittedGeneratedImageKeys.add(key);
13771
14262
  this.wsClient.emitEvent(image);
13772
14263
  }
14264
+ onSessionMedia(media) {
14265
+ this.emitSessionMedia(media);
14266
+ }
14267
+ emitSessionMedia(media) {
14268
+ const key = media.sourceHash ?? `${media.sourcePath ?? media.id}:${media.size}`;
14269
+ if (this.emittedSessionMediaKeys.has(key)) return;
14270
+ this.emittedSessionMediaKeys.add(key);
14271
+ this.wsClient.emitEvent(media);
14272
+ }
13773
14273
  onTurnComplete(content) {
13774
14274
  this.wsClient.emitEvent({ type: "turn_complete", content, ts: Date.now() });
13775
14275
  }
@@ -13858,9 +14358,9 @@ var WSClient = class {
13858
14358
  this.socket.emit("agent_event", event);
13859
14359
  }
13860
14360
  waitForConnection(timeoutMs = 15e3) {
13861
- return new Promise((resolve2, reject) => {
14361
+ return new Promise((resolve3, reject) => {
13862
14362
  if (this.socket.connected) {
13863
- resolve2();
14363
+ resolve3();
13864
14364
  return;
13865
14365
  }
13866
14366
  const timeout = setTimeout(
@@ -13869,7 +14369,7 @@ var WSClient = class {
13869
14369
  );
13870
14370
  this.socket.once("connect", () => {
13871
14371
  clearTimeout(timeout);
13872
- resolve2();
14372
+ resolve3();
13873
14373
  });
13874
14374
  this.socket.once("connect_error", (err) => {
13875
14375
  clearTimeout(timeout);
@@ -14010,9 +14510,9 @@ async function runSandbox(config) {
14010
14510
  onUserMessage: (payload) => {
14011
14511
  if (payload === null) {
14012
14512
  if (pendingMessageResolve) {
14013
- const resolve2 = pendingMessageResolve;
14513
+ const resolve3 = pendingMessageResolve;
14014
14514
  pendingMessageResolve = null;
14015
- resolve2(null);
14515
+ resolve3(null);
14016
14516
  }
14017
14517
  return;
14018
14518
  }
@@ -14034,9 +14534,9 @@ async function runSandbox(config) {
14034
14534
  fileCount: payload.files?.length ?? 0
14035
14535
  });
14036
14536
  if (pendingMessageResolve) {
14037
- const resolve2 = pendingMessageResolve;
14537
+ const resolve3 = pendingMessageResolve;
14038
14538
  pendingMessageResolve = null;
14039
- resolve2(payload);
14539
+ resolve3(payload);
14040
14540
  } else {
14041
14541
  queuedMessages.push(payload);
14042
14542
  }
@@ -14190,8 +14690,8 @@ async function runSandbox(config) {
14190
14690
  });
14191
14691
  if (stopped) break;
14192
14692
  }
14193
- const nextPayload = queuedMessages.shift() ?? await new Promise((resolve2) => {
14194
- pendingMessageResolve = resolve2;
14693
+ const nextPayload = queuedMessages.shift() ?? await new Promise((resolve3) => {
14694
+ pendingMessageResolve = resolve3;
14195
14695
  });
14196
14696
  if (nextPayload === null || stopped) {
14197
14697
  break;