@aiden-ade/sandbox-agent 0.1.26 → 0.1.28

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 (2) hide show
  1. package/dist/index.cjs +915 -292
  2. package/package.json +1 -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"]
8701
8705
  },
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" }
8706
+ gemini_cli: {
8707
+ provider: "gemini_cli",
8708
+ command: "agy",
8709
+ envVars: ["AIDEN_ANTIGRAVITY_PATH", "AIDEN_GEMINI_PATH"]
8710
+ },
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
 
@@ -9403,6 +9475,123 @@ function getAidenAgentPrompt(agentId) {
9403
9475
  return getAgentSystemPrompt(agentId);
9404
9476
  }
9405
9477
 
9478
+ // ../shared/dist/types/frontend.js
9479
+ var CLAUDE_48_EFFORT_LEVELS = [
9480
+ "low",
9481
+ "medium",
9482
+ "high",
9483
+ "xhigh",
9484
+ "max"
9485
+ ];
9486
+ var CLAUDE_5_EFFORT_LEVELS = [
9487
+ "low",
9488
+ "medium",
9489
+ "high",
9490
+ "xhigh",
9491
+ "max"
9492
+ ];
9493
+ var CODEX_5X_EFFORT_LEVELS = [
9494
+ "low",
9495
+ "medium",
9496
+ "high",
9497
+ "xhigh"
9498
+ ];
9499
+ var AVAILABLE_MODELS = [
9500
+ {
9501
+ id: "",
9502
+ name: "CLI default",
9503
+ capabilities: { contextWindows: [], effortLevels: [] }
9504
+ },
9505
+ {
9506
+ id: "claude-sonnet-5",
9507
+ name: "Sonnet 5",
9508
+ capabilities: { contextWindows: ["200k", "1m"], effortLevels: CLAUDE_5_EFFORT_LEVELS }
9509
+ },
9510
+ {
9511
+ id: "claude-opus-4-8",
9512
+ name: "Opus 4.8",
9513
+ capabilities: { contextWindows: ["200k", "1m"], effortLevels: CLAUDE_48_EFFORT_LEVELS }
9514
+ },
9515
+ {
9516
+ id: "claude-haiku-4-5",
9517
+ name: "Haiku 4.5",
9518
+ capabilities: { contextWindows: ["200k"], effortLevels: [] }
9519
+ }
9520
+ ];
9521
+ var CURSOR_AGENT_MODELS = [
9522
+ { id: "auto", name: "Auto" },
9523
+ { id: "composer-2.5-fast", name: "Composer 2.5 Fast" },
9524
+ { id: "composer-2.5", name: "Composer 2.5" },
9525
+ { id: "composer-2-fast", name: "Composer 2 Fast" },
9526
+ { id: "composer-2", name: "Composer 2" },
9527
+ { id: "claude-4.6-sonnet-medium", name: "Sonnet 4.6 1M" },
9528
+ { id: "claude-4.6-sonnet-medium-thinking", name: "Sonnet 4.6 Thinking" },
9529
+ { id: "gpt-5.3-codex", name: "Codex 5.3" },
9530
+ { id: "gpt-5.4-medium", name: "GPT-5.4 1M" },
9531
+ { id: "gemini-3-flash", name: "Gemini 3 Flash" },
9532
+ { id: "gpt-5.5", name: "GPT-5.5" }
9533
+ ];
9534
+ var COPILOT_CLI_MODELS = [
9535
+ { id: "claude-sonnet-4.5", name: "Claude Sonnet 4.5" },
9536
+ { id: "claude-sonnet-4.6", name: "Claude Sonnet 4.6" },
9537
+ { id: "claude-haiku-4.5", name: "Claude Haiku 4.5" },
9538
+ { id: "gpt-5.3-codex", name: "GPT-5.3 Codex" },
9539
+ { id: "gpt-5.4", name: "GPT-5.4" },
9540
+ { id: "gpt-5.4-mini", name: "GPT-5.4 Mini" },
9541
+ { id: "auto", name: "Auto" }
9542
+ ];
9543
+ var PROVIDER_MODELS = {
9544
+ claude_cli: AVAILABLE_MODELS,
9545
+ codex_app_server: [
9546
+ {
9547
+ id: "gpt-5.5",
9548
+ name: "GPT-5.5",
9549
+ capabilities: { contextWindows: ["1m"], effortLevels: CODEX_5X_EFFORT_LEVELS }
9550
+ },
9551
+ {
9552
+ id: "gpt-5.4-mini",
9553
+ name: "GPT-5.4 Mini",
9554
+ capabilities: { contextWindows: ["1m"], effortLevels: CODEX_5X_EFFORT_LEVELS }
9555
+ }
9556
+ ],
9557
+ copilot_cli: COPILOT_CLI_MODELS,
9558
+ cursor_agent_cli: CURSOR_AGENT_MODELS,
9559
+ gemini_cli: [{ id: "", name: "CLI default" }],
9560
+ opencode_cli: [
9561
+ { id: "opencode/north-mini-code-free", name: "North Mini Code Free" },
9562
+ { id: "opencode/nemotron-3-ultra-free", name: "Nemotron 3 Ultra Free" },
9563
+ { id: "opencode/deepseek-v4-flash-free", name: "DeepSeek V4 Flash Free" },
9564
+ { id: "opencode/mimo-v2.5-free", name: "MiMo V2.5 Free" },
9565
+ { id: "opencode/big-pickle", name: "Big Pickle" }
9566
+ ],
9567
+ droid_cli: [
9568
+ { id: "claude-opus-4-7", name: "Claude Opus 4.7" },
9569
+ { id: "claude-opus-4-7-fast", name: "Claude Opus 4.7 Fast" },
9570
+ { id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5" },
9571
+ { id: "gpt-5.3-codex", name: "GPT-5.3 Codex" },
9572
+ { id: "gpt-5.2-codex", name: "GPT-5.2 Codex" },
9573
+ { id: "gemini-3-pro-preview", name: "Gemini 3 Pro" },
9574
+ { id: "gemini-3-flash-preview", name: "Gemini 3 Flash" },
9575
+ { id: "glm-4.7", name: "GLM 4.7" },
9576
+ { id: "kimi-k2.5", name: "Kimi k2.5" }
9577
+ ],
9578
+ kimi_cli: [
9579
+ { id: "kimi-latest", name: "Kimi Latest" },
9580
+ { id: "kimi-k2.6", name: "Kimi K2.6" },
9581
+ { id: "kimi-k2.6-thinking", name: "Kimi K2.6 Thinking" },
9582
+ { id: "kimi-code/kimi-for-coding", name: "Kimi Code" }
9583
+ ],
9584
+ supatest_cli: [
9585
+ { id: "small", name: "Small" },
9586
+ { id: "medium", name: "Medium" },
9587
+ { id: "premium", name: "Premium" },
9588
+ { id: "claude-sonnet-4-5", name: "Sonnet 4.5" },
9589
+ { id: "claude-opus-4-5", name: "Opus 4.5" },
9590
+ { id: "claude-haiku-4-5", name: "Haiku 4.5" }
9591
+ ],
9592
+ generic_cli: []
9593
+ };
9594
+
9406
9595
  // ../agent-core/dist/index.js
9407
9596
  var import_readline = require("readline");
9408
9597
  var import_child_process = require("child_process");
@@ -9621,6 +9810,12 @@ var import_path3 = require("path");
9621
9810
  var import_fs4 = require("fs");
9622
9811
  var import_os4 = require("os");
9623
9812
  var import_path4 = require("path");
9813
+ var import_fs5 = require("fs");
9814
+ var import_os5 = require("os");
9815
+ var import_path5 = require("path");
9816
+ var import_crypto2 = require("crypto");
9817
+ var import_fs6 = require("fs");
9818
+ var import_path6 = require("path");
9624
9819
  function shouldUseReadOnlyRuntimePermissions(config) {
9625
9820
  if (isReadOnlyAgent(config.agentId)) return true;
9626
9821
  switch (config.mode) {
@@ -9761,8 +9956,7 @@ function createGenericCliBackend(options) {
9761
9956
  const state = {
9762
9957
  process: null,
9763
9958
  iterations: 0,
9764
- summary: "",
9765
- allowContinuation: options.allowContinuation ?? false
9959
+ summary: ""
9766
9960
  };
9767
9961
  const prompt = options.augmentPrompt?.(context) ?? (context.config.mode === "plan" ? buildPlanModePrefix(buildPromptWithSystem(context.config, context.promptText)) : buildPromptWithSystem(context.config, context.promptText));
9768
9962
  const args = options.buildArgs?.(context, prompt) ?? options.args.map((arg) => arg === "{{prompt}}" ? prompt : arg);
@@ -9819,12 +10013,13 @@ function createGenericCliBackend(options) {
9819
10013
  }
9820
10014
  });
9821
10015
  let exitCode;
10016
+ let idleTimedOut = false;
9822
10017
  try {
9823
- const exitPromise = new Promise((resolve2, reject) => {
10018
+ const exitPromise = new Promise((resolve22, reject) => {
9824
10019
  child.on("error", (error) => {
9825
10020
  reject(formatCliSpawnError(options.command, error));
9826
10021
  });
9827
- child.on("exit", (code) => resolve2(code ?? 0));
10022
+ child.on("exit", (code) => resolve22(code ?? 0));
9828
10023
  const onAbort = () => {
9829
10024
  const pid = child.pid;
9830
10025
  if (!pid) return;
@@ -9840,53 +10035,67 @@ function createGenericCliBackend(options) {
9840
10035
  context.abortController.signal.addEventListener("abort", onAbort, { once: true });
9841
10036
  });
9842
10037
  if (options.keepStdinOpen) {
9843
- if (options.allowContinuation) {
9844
- const IDLE_MS = options.continuationIdleMs ?? 10 * 60 * 1e3;
9845
- let idleTimer = null;
9846
- const resetIdleTimer = () => {
10038
+ const resultPromise = new Promise((resolve22) => {
10039
+ state.onResultReceived = () => resolve22("result_received");
10040
+ if (state.resultReceived) resolve22("result_received");
10041
+ });
10042
+ const IDLE_MS = options.resultIdleTimeoutMs ?? 10 * 60 * 1e3;
10043
+ let idleTimer = null;
10044
+ const idlePromise = new Promise((resolve22) => {
10045
+ const armIdleTimer = () => {
9847
10046
  if (idleTimer) clearTimeout(idleTimer);
9848
10047
  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
- );
10048
+ if (presenter.pendingAskUserToolIds?.size) {
10049
+ resolve22("idle_timeout");
10050
+ } else {
10051
+ armIdleTimer();
9854
10052
  }
9855
10053
  }, IDLE_MS);
9856
10054
  };
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 {
10055
+ state.onNewOutput = armIdleTimer;
10056
+ armIdleTimer();
10057
+ });
10058
+ const raceResult = await Promise.race([exitPromise, resultPromise, idlePromise]);
10059
+ if (idleTimer) clearTimeout(idleTimer);
10060
+ if (raceResult === "idle_timeout") {
10061
+ idleTimedOut = true;
10062
+ console.warn(
10063
+ `[${options.kind}] Idle timeout (${IDLE_MS}ms) waiting for a pending user answer \u2014 force-killing orphaned process`
10064
+ );
10065
+ try {
10066
+ if (process.platform === "win32") {
10067
+ child.kill("SIGKILL");
10068
+ } else if (child.pid) {
10069
+ process.kill(-child.pid, "SIGKILL");
10070
+ }
10071
+ } catch {
10072
+ }
10073
+ exitCode = await Promise.race([
10074
+ exitPromise,
10075
+ new Promise((resolve22) => setTimeout(() => resolve22(1), 5e3))
10076
+ ]);
10077
+ } else if (raceResult === "result_received") {
10078
+ const GRACE_MS = 5e3;
10079
+ const gracedExit = await Promise.race([
10080
+ exitPromise,
10081
+ new Promise((resolve22) => setTimeout(() => resolve22(0), GRACE_MS))
10082
+ ]);
10083
+ exitCode = typeof gracedExit === "number" ? gracedExit : 0;
10084
+ if (child.exitCode === null && !child.killed) {
10085
+ console.warn(
10086
+ `[${options.kind}] Process still alive ${GRACE_MS}ms after result event, killing`
10087
+ );
10088
+ try {
10089
+ if (process.platform === "win32") {
10090
+ child.kill("SIGKILL");
10091
+ } else if (child.pid) {
10092
+ process.kill(-child.pid, "SIGKILL");
9885
10093
  }
10094
+ } catch {
9886
10095
  }
9887
- } else {
9888
- exitCode = raceResult;
9889
10096
  }
10097
+ } else {
10098
+ exitCode = raceResult;
9890
10099
  }
9891
10100
  } else {
9892
10101
  exitCode = await exitPromise;
@@ -9910,6 +10119,31 @@ function createGenericCliBackend(options) {
9910
10119
  usage: state.usage
9911
10120
  };
9912
10121
  }
10122
+ if (idleTimedOut) {
10123
+ console.error(`[${options.kind}] Process force-killed after idle timeout`, {
10124
+ stderrLines: stderrLines.slice(-10),
10125
+ summaryPreview: state.summary.slice(0, 200),
10126
+ runtimeSessionId: state.runtimeSessionId
10127
+ });
10128
+ await options.afterExit?.(context, state);
10129
+ const hasRenderableTurn2 = state.summary.trim().length > 0 || state.iterations > 0;
10130
+ if (hasRenderableTurn2) {
10131
+ await presenter.onTurnComplete([]);
10132
+ }
10133
+ return {
10134
+ success: false,
10135
+ summary: state.summary.trim() || "Agent execution timed out",
10136
+ filesModified: [],
10137
+ planFilesCreated: [],
10138
+ iterations: Math.max(state.iterations, 1),
10139
+ 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.",
10140
+ providerSessionId: state.runtimeSessionId,
10141
+ runtimeSessionId: state.runtimeSessionId,
10142
+ backendKind: options.kind,
10143
+ supportTier: options.supportTier,
10144
+ usage: state.usage
10145
+ };
10146
+ }
9913
10147
  if (exitCode !== 0) {
9914
10148
  console.error(`[${options.kind}] Process exited with code ${exitCode}`, {
9915
10149
  stderrLines: stderrLines.slice(-10),
@@ -9982,11 +10216,6 @@ function parseClaudeStructuredLine(line, context, state) {
9982
10216
  );
9983
10217
  break;
9984
10218
  }
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
10219
  state.onNewOutput?.();
9991
10220
  state.iterations += 1;
9992
10221
  const message = typeof parsed.message === "object" && parsed.message !== null ? parsed.message : null;
@@ -10115,31 +10344,10 @@ function parseClaudeStructuredLine(line, context, state) {
10115
10344
  break;
10116
10345
  }
10117
10346
  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
- }
10347
+ state.onResultReceived?.();
10348
+ if (state.stdin && !state.stdin.destroyed) {
10349
+ state.stdin.end();
10350
+ console.info("[claude_cli] Closed stdin after result event to signal CLI exit");
10143
10351
  }
10144
10352
  break;
10145
10353
  }
@@ -11333,10 +11541,36 @@ function parseGeminiStructuredLine(line, context, state) {
11333
11541
  break;
11334
11542
  }
11335
11543
  }
11336
- function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
11544
+ function parseAntigravityLine(line, context, state) {
11545
+ try {
11546
+ JSON.parse(line);
11547
+ parseGeminiStructuredLine(line, context, state);
11548
+ return;
11549
+ } catch {
11550
+ }
11551
+ if (!line.trim()) return;
11552
+ state.iterations = Math.max(state.iterations, 1);
11553
+ state.summary += `${line}
11554
+ `;
11555
+ void context.presenter.onAssistantText(`${line}
11556
+ `);
11557
+ }
11558
+ function buildAntigravityCliArgs(context, prompt, defaultArgs = []) {
11559
+ const args = [];
11560
+ const selectedModel = context.config.selectedModel?.trim();
11561
+ const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
11562
+ if (selectedModel) args.push("--model", selectedModel);
11563
+ if (resumeId) args.push("--conversation", resumeId);
11564
+ if (!shouldUseReadOnlyRuntimePermissions(context.config)) {
11565
+ args.push("--dangerously-skip-permissions");
11566
+ }
11567
+ args.push("--print-timeout", "10m");
11568
+ return [...args, ...defaultArgs, "-p", prompt];
11569
+ }
11570
+ function createAntigravityCliBackend(command = "agy", defaultArgs = []) {
11337
11571
  return {
11338
11572
  kind: "gemini_cli",
11339
- supportTier: "structured",
11573
+ supportTier: "text",
11340
11574
  async run(context) {
11341
11575
  const { files: imageFiles, cleanup } = writeImagesToTempFiles(
11342
11576
  context.config.images,
@@ -11345,22 +11579,12 @@ function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
11345
11579
  try {
11346
11580
  return await createGenericCliBackend({
11347
11581
  kind: "gemini_cli",
11348
- supportTier: "structured",
11582
+ supportTier: "text",
11349
11583
  command,
11350
11584
  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,
11585
+ buildArgs: (ctx, prompt) => buildAntigravityCliArgs(ctx, prompt, defaultArgs),
11362
11586
  augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
11363
- parseStructuredLine: parseGeminiStructuredLine
11587
+ parseStructuredLine: parseAntigravityLine
11364
11588
  }).run(context);
11365
11589
  } finally {
11366
11590
  cleanup();
@@ -11368,6 +11592,72 @@ function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
11368
11592
  }
11369
11593
  };
11370
11594
  }
11595
+ function createGeminiCliBackend(command = "agy", defaultArgs = []) {
11596
+ return createAntigravityCliBackend(command, defaultArgs);
11597
+ }
11598
+ var DEFAULT_SKILL_ROOTS = [
11599
+ (0, import_path4.join)((0, import_os4.homedir)(), ".agents", "skills"),
11600
+ (0, import_path4.join)((0, import_os4.homedir)(), ".claude", "skills"),
11601
+ (0, import_path4.join)((0, import_os4.homedir)(), ".config", "opencode", "skills")
11602
+ ];
11603
+ function parseFrontmatter(content) {
11604
+ if (!content.startsWith("---\n")) return {};
11605
+ const end = content.indexOf("\n---", 4);
11606
+ if (end === -1) return {};
11607
+ const result = {};
11608
+ for (const line of content.slice(4, end).split("\n")) {
11609
+ const match = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line.trim());
11610
+ if (!match) continue;
11611
+ const [, key, rawValue] = match;
11612
+ result[key] = rawValue.replace(/^["']|["']$/g, "").trim();
11613
+ }
11614
+ return result;
11615
+ }
11616
+ function listSkillFiles(root) {
11617
+ if (!(0, import_fs4.existsSync)(root)) return [];
11618
+ const files = [];
11619
+ for (const entry of (0, import_fs4.readdirSync)(root, { withFileTypes: true })) {
11620
+ if (!entry.isDirectory()) continue;
11621
+ const skillPath = (0, import_path4.join)(root, entry.name, "SKILL.md");
11622
+ if ((0, import_fs4.existsSync)(skillPath)) files.push(skillPath);
11623
+ }
11624
+ return files;
11625
+ }
11626
+ function loadInstalledSkills(roots = DEFAULT_SKILL_ROOTS) {
11627
+ const skills = /* @__PURE__ */ new Map();
11628
+ for (const root of roots) {
11629
+ for (const skillPath of listSkillFiles(root)) {
11630
+ try {
11631
+ const frontmatter = parseFrontmatter((0, import_fs4.readFileSync)(skillPath, "utf8"));
11632
+ const name = frontmatter.name;
11633
+ const description = frontmatter.description;
11634
+ if (!name || !description || skills.has(name)) continue;
11635
+ skills.set(name, { name, description, path: skillPath });
11636
+ } catch {
11637
+ }
11638
+ }
11639
+ }
11640
+ return [...skills.values()].sort((a, b) => a.name.localeCompare(b.name));
11641
+ }
11642
+ function buildInstalledSkillsPrompt(roots = DEFAULT_SKILL_ROOTS) {
11643
+ const skills = loadInstalledSkills(roots);
11644
+ if (skills.length === 0) return void 0;
11645
+ return [
11646
+ "## Installed Skills",
11647
+ "",
11648
+ "Use an installed skill before improvising when the task matches a skill description.",
11649
+ "",
11650
+ "Protocol:",
11651
+ "1. Match the request against the skill names and descriptions below.",
11652
+ "2. Before acting on a matching skill, read its SKILL.md file completely.",
11653
+ "3. Resolve any referenced files such as references/, scripts/, or assets/ relative to the skill directory.",
11654
+ "4. Use the smallest set of matching skills for the current turn only.",
11655
+ "5. If a named skill is missing or unreadable, say so briefly and continue with the best fallback.",
11656
+ "",
11657
+ "Available skills:",
11658
+ ...skills.map((skill) => `- ${skill.name}: ${skill.description} (file: ${skill.path})`)
11659
+ ].join("\n");
11660
+ }
11371
11661
  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
11662
  function parseKimiStderrLine(line, state) {
11373
11663
  const match = line.match(KIMI_RESUME_SESSION_RE);
@@ -11470,7 +11760,13 @@ function createKimiCliBackend(command = "kimi-cli", defaultArgs = []) {
11470
11760
  if (model) args.push("--model", model);
11471
11761
  return [...args, ...defaultArgs, "--prompt", prompt];
11472
11762
  },
11473
- augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
11763
+ augmentPrompt: (ctx) => {
11764
+ const skillPrompt = buildInstalledSkillsPrompt();
11765
+ const promptText = [skillPrompt, ctx.promptText].filter(Boolean).join("\n\n");
11766
+ const basePrompt = buildPromptWithSystem(ctx.config, promptText);
11767
+ const modePrompt = ctx.config.mode === "plan" ? buildPlanModePrefix(basePrompt) : basePrompt;
11768
+ return appendImagePathReferences(modePrompt, imageFiles);
11769
+ },
11474
11770
  parseStructuredLine: parseKimiStructuredLine,
11475
11771
  parseStderrLine: (line, _ctx, state) => parseKimiStderrLine(line, state)
11476
11772
  }).run(context);
@@ -11813,40 +12109,40 @@ function parseSkillInvocation(text) {
11813
12109
  function candidateRoots(cwd) {
11814
12110
  const roots = [];
11815
12111
  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)) {
12112
+ roots.push((0, import_path5.join)(cwd, ".agents", "skills"));
12113
+ roots.push((0, import_path5.join)(cwd, ".claude", "skills"));
12114
+ roots.push((0, import_path5.join)(cwd, ".codex", "skills"));
12115
+ if ((0, import_fs5.existsSync)(cwd)) {
11820
12116
  let entries = [];
11821
12117
  try {
11822
- entries = (0, import_fs4.readdirSync)(cwd, { withFileTypes: true });
12118
+ entries = (0, import_fs5.readdirSync)(cwd, { withFileTypes: true });
11823
12119
  } catch {
11824
12120
  }
11825
12121
  for (const entry of entries) {
11826
12122
  if (!entry.isDirectory()) continue;
11827
12123
  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"));
12124
+ const child = (0, import_path5.join)(cwd, entry.name);
12125
+ roots.push((0, import_path5.join)(child, ".agents", "skills"));
12126
+ roots.push((0, import_path5.join)(child, ".claude", "skills"));
12127
+ roots.push((0, import_path5.join)(child, ".codex", "skills"));
11832
12128
  }
11833
12129
  }
11834
12130
  }
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"));
12131
+ const home = (0, import_os5.homedir)();
12132
+ roots.push((0, import_path5.join)(home, ".agents", "skills"));
12133
+ roots.push((0, import_path5.join)(home, ".claude", "skills"));
12134
+ roots.push((0, import_path5.join)(home, ".codex", "skills"));
11839
12135
  return Array.from(new Set(roots));
11840
12136
  }
11841
12137
  function resolveSkillInvocation(text, cwd) {
11842
12138
  const parsed = parseSkillInvocation(text);
11843
12139
  if (!parsed) return null;
11844
12140
  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;
12141
+ const skillPath = (0, import_path5.join)(root, parsed.command, "SKILL.md");
12142
+ if (!(0, import_fs5.existsSync)(skillPath)) continue;
11847
12143
  let content;
11848
12144
  try {
11849
- content = (0, import_fs4.readFileSync)(skillPath, "utf-8");
12145
+ content = (0, import_fs5.readFileSync)(skillPath, "utf-8");
11850
12146
  } catch {
11851
12147
  continue;
11852
12148
  }
@@ -11880,7 +12176,6 @@ function expandSkillInvocationPrompt(text, cwd) {
11880
12176
  ].join("\n")
11881
12177
  };
11882
12178
  }
11883
- var DEFAULT_MODEL = "claude-sonnet-4-6";
11884
12179
  var DROID_DEFAULT_MODEL = "claude-opus-4-7";
11885
12180
  var AGENT_ACTIVITY_HEARTBEAT_MS = 1e4;
11886
12181
  function defaultSelectedModelForBackend(backendKind) {
@@ -11895,10 +12190,18 @@ function defaultSelectedModelForBackend(backendKind) {
11895
12190
  return void 0;
11896
12191
  case "opencode_cli":
11897
12192
  return void 0;
12193
+ case "gemini_cli":
12194
+ return void 0;
11898
12195
  case "supatest_cli":
11899
12196
  return "medium";
12197
+ case "codex_app_server":
12198
+ case "cursor_agent_cli":
12199
+ return PROVIDER_MODELS[backendKind]?.[0]?.id || void 0;
12200
+ case "generic_cli":
12201
+ case "simulation":
12202
+ return void 0;
11900
12203
  default:
11901
- return DEFAULT_MODEL;
12204
+ return void 0;
11902
12205
  }
11903
12206
  }
11904
12207
  function canonicalizePath(inputPath) {
@@ -12037,12 +12340,13 @@ var BaseMachineAgent = class _BaseMachineAgent {
12037
12340
  case "codex_app_server":
12038
12341
  case "cursor_agent_cli":
12039
12342
  case "droid_cli":
12040
- case "gemini_cli":
12041
12343
  case "opencode_cli":
12042
12344
  case "copilot_cli":
12043
12345
  case "supatest_cli":
12044
12346
  case "kimi_cli":
12045
12347
  return "structured";
12348
+ case "gemini_cli":
12349
+ return "text";
12046
12350
  default:
12047
12351
  return "text";
12048
12352
  }
@@ -12096,7 +12400,7 @@ Prefer relative paths and keep your work scoped to this project.`
12096
12400
  const promptText = this.buildPromptText(runtimeConfig);
12097
12401
  const runtimeCommand = runtimeConfig.runtimeCommand || this.runtime.runtimeCommand;
12098
12402
  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(
12403
+ 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
12404
  runtimeCommand || "generic-cli",
12101
12405
  runtimeArgs
12102
12406
  );
@@ -12162,18 +12466,18 @@ Prefer relative paths and keep your work scoped to this project.`
12162
12466
  `[base-machine-agent] Transient error (attempt ${attempt + 1}/${_BaseMachineAgent.MAX_RETRIES + 1}), retrying in ${delayMs}ms: ${lastResult.error}`
12163
12467
  );
12164
12468
  void this.presenter.onLog(`Transient error, retrying in ${Math.round(delayMs / 1e3)}s...`);
12165
- await new Promise((resolve2) => {
12469
+ await new Promise((resolve22) => {
12166
12470
  const signal = this.abortController?.signal;
12167
12471
  if (signal?.aborted) {
12168
- resolve2();
12472
+ resolve22();
12169
12473
  return;
12170
12474
  }
12171
- const timeoutId = setTimeout(resolve2, delayMs);
12475
+ const timeoutId = setTimeout(resolve22, delayMs);
12172
12476
  signal?.addEventListener(
12173
12477
  "abort",
12174
12478
  () => {
12175
12479
  clearTimeout(timeoutId);
12176
- resolve2();
12480
+ resolve22();
12177
12481
  },
12178
12482
  { once: true }
12179
12483
  );
@@ -12243,6 +12547,195 @@ Prefer relative paths and keep your work scoped to this project.`
12243
12547
  }
12244
12548
  }
12245
12549
  };
12550
+ var IMAGE_MIME_BY_EXT = {
12551
+ ".gif": "image/gif",
12552
+ ".jpeg": "image/jpeg",
12553
+ ".jpg": "image/jpeg",
12554
+ ".png": "image/png",
12555
+ ".webp": "image/webp"
12556
+ };
12557
+ var VIDEO_MIME_BY_EXT = {
12558
+ ".mov": "video/quicktime",
12559
+ ".mp4": "video/mp4",
12560
+ ".webm": "video/webm"
12561
+ };
12562
+ var MAX_GENERATED_IMAGE_BYTES = 20 * 1024 * 1024;
12563
+ var MAX_SESSION_VIDEO_BYTES = 100 * 1024 * 1024;
12564
+ function isRecord(value2) {
12565
+ return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
12566
+ }
12567
+ function shouldExtractGeneratedImagesFromToolResult(toolName, content, options = {}) {
12568
+ return isGeneratedImageToolName(toolName) || isBrowserScreenshotToolName(toolName) || isAgentBrowserMediaInvocation(toolName, content, options.toolInput, "image");
12569
+ }
12570
+ function collectStringValues(value2, out) {
12571
+ if (typeof value2 === "string") {
12572
+ out.push(value2);
12573
+ return;
12574
+ }
12575
+ if (Array.isArray(value2)) {
12576
+ for (const item of value2) collectStringValues(item, out);
12577
+ return;
12578
+ }
12579
+ if (!value2 || typeof value2 !== "object") return;
12580
+ for (const entry of Object.values(value2)) {
12581
+ collectStringValues(entry, out);
12582
+ }
12583
+ }
12584
+ function collectContentStrings(content, toolInput) {
12585
+ const values = [];
12586
+ if (content) {
12587
+ values.push(content);
12588
+ try {
12589
+ collectStringValues(JSON.parse(content), values);
12590
+ } catch {
12591
+ }
12592
+ }
12593
+ collectStringValues(toolInput, values);
12594
+ return values;
12595
+ }
12596
+ function isAgentBrowserMediaInvocation(toolName, content, toolInput, kind) {
12597
+ if (kind === "image" && isBrowserScreenshotToolName(toolName)) return true;
12598
+ if (kind === "video" && isBrowserRecordingToolName(toolName)) return true;
12599
+ const haystack = collectContentStrings(content, toolInput).join("\n").toLowerCase();
12600
+ if (!/(^|[^a-z0-9_-])agent[-_]browser([^a-z0-9_-]|$)/.test(haystack)) return false;
12601
+ return kind === "image" ? /\b(screenshot|capture)\b/.test(haystack) : /\b(record|recording|video)\b/.test(haystack);
12602
+ }
12603
+ function extractPathCandidatesFromValues(values, extensionsPattern = "png|jpe?g|gif|webp") {
12604
+ const paths = /* @__PURE__ */ new Set();
12605
+ const filePathPattern = new RegExp(
12606
+ `(?:file:\\/\\/)?(?:~|\\.{1,2}|\\/)?[^\\s"'\`)\\]}<>]+?\\.(?:${extensionsPattern})\\b`,
12607
+ "gi"
12608
+ );
12609
+ for (const value2 of values) {
12610
+ for (const match of value2.matchAll(filePathPattern)) {
12611
+ paths.add(match[0]);
12612
+ }
12613
+ }
12614
+ return [...paths];
12615
+ }
12616
+ function extractPathCandidates(content, options, extensionsPattern = "png|jpe?g|gif|webp") {
12617
+ return [
12618
+ ...extractPathCandidatesFromValues(
12619
+ collectContentStrings(content, options.toolInput),
12620
+ extensionsPattern
12621
+ ),
12622
+ ...options.pathHints ?? []
12623
+ ];
12624
+ }
12625
+ function normalizePath(candidate, cwd) {
12626
+ let path = candidate.trim().replace(/^file:\/\//, "");
12627
+ try {
12628
+ path = decodeURI(path);
12629
+ } catch {
12630
+ }
12631
+ if (path.startsWith("~/")) return null;
12632
+ if ((0, import_path6.isAbsolute)(path)) return path;
12633
+ return cwd ? (0, import_path6.join)(cwd, path) : null;
12634
+ }
12635
+ function resolveToolCwd(cwd, toolInput) {
12636
+ if (!isRecord(toolInput)) return cwd;
12637
+ const raw = typeof toolInput.cwd === "string" ? toolInput.cwd : typeof toolInput.workdir === "string" ? toolInput.workdir : typeof toolInput.workingDirectory === "string" ? toolInput.workingDirectory : null;
12638
+ if (!raw) return cwd;
12639
+ return (0, import_path6.isAbsolute)(raw) || !cwd ? raw : (0, import_path6.join)(cwd, raw);
12640
+ }
12641
+ function extractBrowserMediaPathHints(content, cwd, toolName, options = {}, kind = "video") {
12642
+ if (!isAgentBrowserMediaInvocation(toolName, content, options.toolInput, kind)) return [];
12643
+ const extensionsPattern = kind === "video" ? "mp4|webm|mov" : "png|jpe?g|gif|webp";
12644
+ const effectiveCwd = resolveToolCwd(cwd, options.toolInput);
12645
+ return extractPathCandidatesFromValues(
12646
+ collectContentStrings(content, options.toolInput),
12647
+ extensionsPattern
12648
+ ).map((candidate) => normalizePath(candidate, effectiveCwd)).filter((path) => Boolean(path));
12649
+ }
12650
+ function readImageDimensions2(buffer, mimeType) {
12651
+ if (mimeType === "image/png" && buffer.length >= 24 && buffer.toString("ascii", 1, 4) === "PNG") {
12652
+ return { width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) };
12653
+ }
12654
+ if (mimeType === "image/gif" && buffer.length >= 10 && buffer.toString("ascii", 0, 3) === "GIF") {
12655
+ return { width: buffer.readUInt16LE(6), height: buffer.readUInt16LE(8) };
12656
+ }
12657
+ if (mimeType === "image/jpeg" && buffer.length > 4) {
12658
+ let offset = 2;
12659
+ while (offset + 9 < buffer.length) {
12660
+ if (buffer[offset] !== 255) break;
12661
+ const marker = buffer[offset + 1];
12662
+ const length = buffer.readUInt16BE(offset + 2);
12663
+ if (length < 2) break;
12664
+ if (marker >= 192 && marker <= 207 && ![196, 200, 204].includes(marker)) {
12665
+ return {
12666
+ width: buffer.readUInt16BE(offset + 7),
12667
+ height: buffer.readUInt16BE(offset + 5)
12668
+ };
12669
+ }
12670
+ offset += 2 + length;
12671
+ }
12672
+ }
12673
+ return { width: null, height: null };
12674
+ }
12675
+ function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName, options = {}) {
12676
+ if (!shouldExtractGeneratedImagesFromToolResult(toolName, content, options)) return [];
12677
+ const images = [];
12678
+ const seen = /* @__PURE__ */ new Set();
12679
+ const effectiveCwd = resolveToolCwd(cwd, options.toolInput);
12680
+ for (const candidate of extractPathCandidates(content, options)) {
12681
+ const path = normalizePath(candidate, effectiveCwd);
12682
+ if (!path || seen.has(path) || !(0, import_fs6.existsSync)(path)) continue;
12683
+ seen.add(path);
12684
+ const ext = (0, import_path6.extname)(path).toLowerCase();
12685
+ const mimeType = IMAGE_MIME_BY_EXT[ext];
12686
+ if (!mimeType) continue;
12687
+ const stat = (0, import_fs6.statSync)(path);
12688
+ if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_GENERATED_IMAGE_BYTES) continue;
12689
+ const buffer = (0, import_fs6.readFileSync)(path);
12690
+ const { width, height } = readImageDimensions2(buffer, mimeType);
12691
+ images.push({
12692
+ type: "generated_image",
12693
+ id: (0, import_crypto2.randomUUID)(),
12694
+ filename: (0, import_path6.basename)(path),
12695
+ mimeType,
12696
+ size: buffer.length,
12697
+ width,
12698
+ height,
12699
+ data: buffer.toString("base64"),
12700
+ sourcePath: path,
12701
+ sourceHash: (0, import_crypto2.createHash)("sha256").update(buffer).digest("hex"),
12702
+ toolId,
12703
+ ts: Date.now()
12704
+ });
12705
+ }
12706
+ return images;
12707
+ }
12708
+ function extractSessionMediaFromToolResult(toolId, content, cwd, toolName, options = {}) {
12709
+ if (!isAgentBrowserMediaInvocation(toolName, content, options.toolInput, "video")) return [];
12710
+ const media = [];
12711
+ const seen = /* @__PURE__ */ new Set();
12712
+ const effectiveCwd = resolveToolCwd(cwd, options.toolInput);
12713
+ for (const candidate of extractPathCandidates(content, options, "mp4|webm|mov")) {
12714
+ const path = normalizePath(candidate, effectiveCwd);
12715
+ if (!path || seen.has(path) || !(0, import_fs6.existsSync)(path)) continue;
12716
+ seen.add(path);
12717
+ const ext = (0, import_path6.extname)(path).toLowerCase();
12718
+ const mimeType = VIDEO_MIME_BY_EXT[ext];
12719
+ if (!mimeType) continue;
12720
+ const stat = (0, import_fs6.statSync)(path);
12721
+ if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_SESSION_VIDEO_BYTES) continue;
12722
+ const buffer = (0, import_fs6.readFileSync)(path);
12723
+ media.push({
12724
+ type: "session_media",
12725
+ kind: "video",
12726
+ id: (0, import_crypto2.randomUUID)(),
12727
+ filename: (0, import_path6.basename)(path),
12728
+ mimeType,
12729
+ size: buffer.length,
12730
+ data: buffer.toString("base64"),
12731
+ sourcePath: path,
12732
+ sourceHash: (0, import_crypto2.createHash)("sha256").update(buffer).digest("hex"),
12733
+ toolId,
12734
+ ts: Date.now()
12735
+ });
12736
+ }
12737
+ return media;
12738
+ }
12246
12739
 
12247
12740
  // src/core-agent.ts
12248
12741
  var CoreAgent = class _CoreAgent extends BaseMachineAgent {
@@ -12541,149 +13034,162 @@ ${meta.description}`);
12541
13034
  }
12542
13035
  };
12543
13036
 
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;
13037
+ // src/version.ts
13038
+ var AGENT_VERSION = "0.1.28";
13039
+
13040
+ // src/daemon.ts
13041
+ var OPENCODE_FREE_MODEL_IDS = [
13042
+ "opencode/north-mini-code-free",
13043
+ "opencode/nemotron-3-ultra-free",
13044
+ "opencode/deepseek-v4-flash-free",
13045
+ "opencode/mimo-v2.5-free",
13046
+ "opencode/big-pickle"
13047
+ ];
13048
+ var MODEL_DISCOVERY_TIMEOUT_MS = 15e3;
13049
+ var PRODUCTION_API_URL = "https://api.aiden-platform.com";
13050
+ var PRODUCTION_WS_URL = "wss://ws.aiden-platform.com";
13051
+ var LOCAL_API_URL = "http://localhost:8400";
13052
+ var LOCAL_WS_URL = "ws://localhost:8401";
13053
+ function requiredString(params, key) {
13054
+ const value2 = params[key];
13055
+ if (typeof value2 !== "string" || value2.trim() === "") {
13056
+ throw new Error(`${key} is required`);
12580
13057
  }
12581
- if (!value2 || typeof value2 !== "object") return;
12582
- for (const entry of Object.values(value2)) {
12583
- collectStringValues(entry, out);
13058
+ return value2;
13059
+ }
13060
+ function assertValidGitRef(ref, label) {
13061
+ if (!ref || ref.startsWith("-") || ref.includes("\0")) {
13062
+ throw new Error(`${label} is not a safe git ref`);
12584
13063
  }
13064
+ const result = (0, import_node_child_process3.spawnSync)("git", ["check-ref-format", "--branch", ref], {
13065
+ env: getDaemonCliEnvironment(),
13066
+ encoding: "utf8"
13067
+ });
13068
+ if (result.status !== 0) throw new Error(`${label} is not a valid git ref`);
12585
13069
  }
12586
- function candidateStrings(content) {
12587
- const values = [content];
12588
- try {
12589
- collectStringValues(JSON.parse(content), values);
12590
- } catch {
13070
+ function isPathContained(childPath, parentPath) {
13071
+ const rel = (0, import_node_path3.relative)((0, import_node_path3.resolve)(parentPath), (0, import_node_path3.resolve)(childPath));
13072
+ return rel === "" || !(0, import_node_path3.isAbsolute)(rel) && rel !== ".." && !rel.startsWith(`..${import_node_path3.sep}`);
13073
+ }
13074
+ function assertPathContained(childPath, parentPath, label) {
13075
+ if (!isPathContained(childPath, parentPath)) {
13076
+ throw new Error(`${label} escapes allowed directory`);
12591
13077
  }
12592
- return values;
12593
13078
  }
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
- }
13079
+ function git(cwd, args) {
13080
+ const result = (0, import_node_child_process3.spawnSync)("git", args, {
13081
+ cwd,
13082
+ env: getDaemonCliEnvironment(),
13083
+ encoding: "utf8",
13084
+ maxBuffer: 10 * 1024 * 1024
13085
+ });
13086
+ if (result.status !== 0) {
13087
+ throw new Error((result.stderr || result.stdout || "git command failed").trim());
12601
13088
  }
12602
- return [...paths];
13089
+ return result.stdout.trim();
12603
13090
  }
12604
- function normalizePath(candidate, cwd) {
12605
- let path = candidate.trim().replace(/^file:\/\//, "");
13091
+ function gitSafe(cwd, args) {
12606
13092
  try {
12607
- path = decodeURI(path);
13093
+ return git(cwd, args);
12608
13094
  } catch {
13095
+ return "";
12609
13096
  }
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
13097
  }
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) };
13098
+ function worktreeDir(projectPath) {
13099
+ const resolved = (0, import_node_path3.resolve)(projectPath);
13100
+ return (0, import_node_path3.join)((0, import_node_path3.dirname)(resolved), `${(0, import_node_path3.basename)(resolved)}-aiden-worktrees`);
13101
+ }
13102
+ function pathSegment(value2) {
13103
+ return value2.toLowerCase().replace(/^aiden\//, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "task";
13104
+ }
13105
+ function repoName(repoUrl, localPath) {
13106
+ return repoUrl.replace(/\.git$/, "").split("/").pop() || (0, import_node_path3.basename)(localPath) || "repo";
13107
+ }
13108
+ function findWorktreeByBranch(repoPath, branchName) {
13109
+ const output = gitSafe(repoPath, ["worktree", "list", "--porcelain"]);
13110
+ const entries = output ? output.split("\n\n") : [];
13111
+ for (const entry of entries) {
13112
+ const lines = entry.split("\n");
13113
+ const path = lines.find((line) => line.startsWith("worktree "))?.slice("worktree ".length);
13114
+ const branch = lines.find((line) => line.startsWith("branch "))?.slice("branch ".length).replace("refs/heads/", "");
13115
+ if (path && branch === branchName) return path;
12620
13116
  }
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)) {
13117
+ return null;
13118
+ }
13119
+ function ensureGitWorktree(input) {
13120
+ assertValidGitRef(input.branchName, "branchName");
13121
+ assertValidGitRef(input.baseBranch, "baseBranch");
13122
+ const repoPath = (0, import_node_path3.resolve)(input.repoPath);
13123
+ const root = input.allowedRoot ?? worktreeDir(repoPath);
13124
+ const existingPath = findWorktreeByBranch(input.repoPath, input.branchName);
13125
+ if (existingPath) {
13126
+ if ((0, import_node_path3.resolve)(existingPath) === repoPath) {
13127
+ throw new Error(`${input.branchName} is checked out in the shared checkout`);
13128
+ }
13129
+ return existingPath;
13130
+ }
13131
+ assertPathContained(input.worktreePath, root, "worktreePath");
13132
+ (0, import_node_fs3.mkdirSync)((0, import_node_path3.dirname)(input.worktreePath), { recursive: true });
13133
+ if (!gitSafe(input.repoPath, ["branch", "--list", input.branchName])) {
13134
+ git(input.repoPath, ["branch", input.branchName, input.baseBranch]);
13135
+ }
13136
+ if ((0, import_node_fs3.existsSync)(input.worktreePath)) {
13137
+ if (gitSafe(input.worktreePath, ["branch", "--show-current"]) === input.branchName) {
13138
+ return input.worktreePath;
13139
+ }
13140
+ throw new Error("worktree path already exists for a different checkout");
13141
+ }
13142
+ git(input.repoPath, ["worktree", "add", input.worktreePath, input.branchName]);
13143
+ return input.worktreePath;
13144
+ }
13145
+ function ensureWorktree(params) {
13146
+ const input = {
13147
+ projectPath: requiredString(params, "projectPath"),
13148
+ taskId: requiredString(params, "taskId"),
13149
+ branchName: requiredString(params, "branchName"),
13150
+ baseBranch: typeof params.baseBranch === "string" ? params.baseBranch : "main",
13151
+ repoLocalPaths: Array.isArray(params.repoLocalPaths) ? params.repoLocalPaths : []
13152
+ };
13153
+ const taskRoot = (0, import_node_path3.join)(
13154
+ worktreeDir(input.projectPath),
13155
+ `${pathSegment(input.branchName)}-${input.taskId.slice(0, 8)}`
13156
+ );
13157
+ if (input.repoLocalPaths?.length) {
13158
+ return {
13159
+ worktrees: input.repoLocalPaths.map((repo) => {
13160
+ const baseBranch = repo.branch || input.baseBranch || "main";
13161
+ const worktreePath2 = ensureGitWorktree({
13162
+ repoPath: repo.localPath,
13163
+ worktreePath: (0, import_node_path3.join)(taskRoot, pathSegment(repoName(repo.repoUrl, repo.localPath))),
13164
+ branchName: input.branchName,
13165
+ baseBranch,
13166
+ allowedRoot: worktreeDir(input.projectPath)
13167
+ });
12629
13168
  return {
12630
- width: buffer.readUInt16BE(offset + 7),
12631
- height: buffer.readUInt16BE(offset + 5)
13169
+ repoId: repo.repoId ?? null,
13170
+ repoUrl: repo.repoUrl,
13171
+ worktreePath: worktreePath2,
13172
+ branchName: input.branchName,
13173
+ baseBranch
12632
13174
  };
12633
- }
12634
- offset += 2 + length;
12635
- }
13175
+ })
13176
+ };
12636
13177
  }
12637
- return { width: null, height: null };
13178
+ const worktreePath = ensureGitWorktree({
13179
+ repoPath: input.projectPath,
13180
+ worktreePath: taskRoot,
13181
+ branchName: input.branchName,
13182
+ baseBranch: input.baseBranch
13183
+ });
13184
+ return {
13185
+ worktrees: [{ worktreePath, branchName: input.branchName, baseBranch: input.baseBranch }]
13186
+ };
12638
13187
  }
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;
13188
+ function resolveRuntimeCwd(payload) {
13189
+ const multiRepoWorktree = payload.worktreeInfo?.allWorktrees?.[0]?.worktreePath;
13190
+ if (multiRepoWorktree) return (0, import_node_path3.dirname)(multiRepoWorktree);
13191
+ return payload.worktreeInfo?.worktreePath ?? payload.projectPath ?? process.cwd();
12670
13192
  }
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
13193
  function correlationLogFields(input) {
12688
13194
  return Object.fromEntries(
12689
13195
  Object.entries({
@@ -12722,33 +13228,33 @@ function describeOccupiedLocalDaemon(port) {
12722
13228
  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
13229
  }
12724
13230
  function getConfigPath() {
12725
- return process.env.AIDEN_AGENT_CONFIG_PATH ?? (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".aiden", "agent", "config.json");
13231
+ return process.env.AIDEN_AGENT_CONFIG_PATH ?? (0, import_node_path3.join)((0, import_node_os4.homedir)(), ".aiden", "agent", "config.json");
12726
13232
  }
12727
13233
  function getEndpointDefaultsPath() {
12728
- return process.env.AIDEN_AGENT_ENDPOINTS_PATH ?? (0, import_node_path4.join)((0, import_node_path4.dirname)(getConfigPath()), "endpoints.json");
13234
+ return process.env.AIDEN_AGENT_ENDPOINTS_PATH ?? (0, import_node_path3.join)((0, import_node_path3.dirname)(getConfigPath()), "endpoints.json");
12729
13235
  }
12730
13236
  function readConfig() {
12731
13237
  const configPath = getConfigPath();
12732
- if (!(0, import_node_fs4.existsSync)(configPath)) return {};
12733
- return JSON.parse((0, import_node_fs4.readFileSync)(configPath, "utf8"));
13238
+ if (!(0, import_node_fs3.existsSync)(configPath)) return {};
13239
+ return JSON.parse((0, import_node_fs3.readFileSync)(configPath, "utf8"));
12734
13240
  }
12735
13241
  function readEndpointDefaults() {
12736
13242
  const endpointsPath = getEndpointDefaultsPath();
12737
- if (!(0, import_node_fs4.existsSync)(endpointsPath)) return {};
13243
+ if (!(0, import_node_fs3.existsSync)(endpointsPath)) return {};
12738
13244
  try {
12739
- return JSON.parse((0, import_node_fs4.readFileSync)(endpointsPath, "utf8"));
13245
+ return JSON.parse((0, import_node_fs3.readFileSync)(endpointsPath, "utf8"));
12740
13246
  } catch {
12741
13247
  return {};
12742
13248
  }
12743
13249
  }
12744
13250
  function writeConfig(config) {
12745
13251
  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 });
13252
+ (0, import_node_fs3.mkdirSync)((0, import_node_path3.dirname)(configPath), { recursive: true, mode: 448 });
13253
+ (0, import_node_fs3.writeFileSync)(configPath, JSON.stringify(config, null, 2), { mode: 384 });
12748
13254
  }
12749
13255
  function removeConfig() {
12750
13256
  const configPath = getConfigPath();
12751
- if ((0, import_node_fs4.existsSync)(configPath)) (0, import_node_fs4.rmSync)(configPath, { force: true });
13257
+ if ((0, import_node_fs3.existsSync)(configPath)) (0, import_node_fs3.rmSync)(configPath, { force: true });
12752
13258
  }
12753
13259
  function argValue(args, name) {
12754
13260
  const index = args.indexOf(name);
@@ -12784,7 +13290,7 @@ function getLocalApiPort(args, stored) {
12784
13290
  }
12785
13291
  function sleep(ms) {
12786
13292
  if (ms <= 0) return Promise.resolve();
12787
- return new Promise((resolve2) => setTimeout(resolve2, ms));
13293
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
12788
13294
  }
12789
13295
  function normalizeBackendKind(backendKind) {
12790
13296
  if (backendKind === "codex") return "codex_app_server";
@@ -12816,6 +13322,14 @@ function extractModelIdsFromJson(value2) {
12816
13322
  }
12817
13323
  return [];
12818
13324
  }
13325
+ function extractModelIdsFromText(value2) {
13326
+ const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
13327
+ const ignoredLinePattern = /^(available models|models|usage|error|flag provided|try |agy\b)/i;
13328
+ const ids = value2.split(/\r?\n/).map(
13329
+ (line) => line.replace(ansiPattern, "").replace(/^[-*•]\s*/, "").trim()
13330
+ ).filter((line) => line.length > 0 && !ignoredLinePattern.test(line));
13331
+ return [...new Set(ids)];
13332
+ }
12819
13333
  function discoverOpenCodeModels(executable, env) {
12820
13334
  for (const args of [
12821
13335
  ["models", "--json"],
@@ -12824,7 +13338,7 @@ function discoverOpenCodeModels(executable, env) {
12824
13338
  ]) {
12825
13339
  const result = (0, import_node_child_process3.spawnSync)(executable, args, {
12826
13340
  encoding: "utf8",
12827
- timeout: 5e3,
13341
+ timeout: MODEL_DISCOVERY_TIMEOUT_MS,
12828
13342
  env,
12829
13343
  windowsHide: true
12830
13344
  });
@@ -12837,7 +13351,27 @@ function discoverOpenCodeModels(executable, env) {
12837
13351
  }
12838
13352
  return OPENCODE_FREE_MODEL_IDS;
12839
13353
  }
13354
+ function discoverAntigravityModels(executable, env) {
13355
+ for (const args of [["models", "--json"], ["models"]]) {
13356
+ const result = (0, import_node_child_process3.spawnSync)(executable, args, {
13357
+ encoding: "utf8",
13358
+ timeout: MODEL_DISCOVERY_TIMEOUT_MS,
13359
+ env,
13360
+ windowsHide: true
13361
+ });
13362
+ if (!result || result.error || result.status !== 0 || !result.stdout.trim()) continue;
13363
+ try {
13364
+ const models = extractModelIdsFromJson(JSON.parse(result.stdout));
13365
+ if (models.length > 0) return models;
13366
+ } catch {
13367
+ const models = extractModelIdsFromText(result.stdout);
13368
+ if (models.length > 0) return models;
13369
+ }
13370
+ }
13371
+ return [];
13372
+ }
12840
13373
  function discoverProviderModels(provider, executable, env) {
13374
+ if (provider === "gemini_cli") return discoverAntigravityModels(executable, env);
12841
13375
  if (provider === "opencode_cli") return discoverOpenCodeModels(executable, env);
12842
13376
  return [];
12843
13377
  }
@@ -12918,7 +13452,7 @@ function collectRuntimeMetadata() {
12918
13452
  metadata.cpuCores = cpuList.length;
12919
13453
  }
12920
13454
  try {
12921
- const disk = (0, import_node_fs4.statfsSync)((0, import_node_os4.homedir)());
13455
+ const disk = (0, import_node_fs3.statfsSync)((0, import_node_os4.homedir)());
12922
13456
  metadata.diskFreeBytes = disk.bavail * disk.bsize;
12923
13457
  metadata.diskTotalBytes = disk.blocks * disk.bsize;
12924
13458
  } catch {
@@ -12946,7 +13480,10 @@ var RuntimePresenter = class {
12946
13480
  this.onRunComplete = onRunComplete;
12947
13481
  }
12948
13482
  emittedGeneratedImageKeys = /* @__PURE__ */ new Set();
13483
+ emittedSessionMediaKeys = /* @__PURE__ */ new Set();
12949
13484
  toolNamesById = /* @__PURE__ */ new Map();
13485
+ toolInputsById = /* @__PURE__ */ new Map();
13486
+ browserRecordingPathHints = /* @__PURE__ */ new Set();
12950
13487
  emitEvent(event) {
12951
13488
  this.touchRunnerActivity?.();
12952
13489
  this.socket.emit("agent.event", {
@@ -12969,6 +13506,12 @@ var RuntimePresenter = class {
12969
13506
  }
12970
13507
  onToolUse(tool, input, toolId) {
12971
13508
  this.toolNamesById.set(toolId, tool);
13509
+ this.toolInputsById.set(toolId, input);
13510
+ for (const path of extractBrowserMediaPathHints(void 0, this.cwd, tool, {
13511
+ toolInput: input
13512
+ })) {
13513
+ this.browserRecordingPathHints.add(path);
13514
+ }
12972
13515
  this.emitEvent({ type: "tool_use", id: toolId, name: tool, input, ts: Date.now() });
12973
13516
  }
12974
13517
  onToolResult(toolId, content, isError) {
@@ -12981,9 +13524,27 @@ var RuntimePresenter = class {
12981
13524
  });
12982
13525
  if (isError) return;
12983
13526
  const toolName = this.toolNamesById.get(toolId);
12984
- for (const image of extractGeneratedImagesFromToolResult(toolId, content, this.cwd, toolName)) {
13527
+ const toolInput = this.toolInputsById.get(toolId);
13528
+ const options = { toolInput, pathHints: [...this.browserRecordingPathHints] };
13529
+ for (const image of extractGeneratedImagesFromToolResult(
13530
+ toolId,
13531
+ content,
13532
+ this.cwd,
13533
+ toolName,
13534
+ options
13535
+ )) {
12985
13536
  this.emitGeneratedImage(image);
12986
13537
  }
13538
+ for (const media of extractSessionMediaFromToolResult(
13539
+ toolId,
13540
+ content,
13541
+ this.cwd,
13542
+ toolName,
13543
+ options
13544
+ )) {
13545
+ this.emitSessionMedia(media);
13546
+ if (media.sourcePath) this.browserRecordingPathHints.delete(media.sourcePath);
13547
+ }
12987
13548
  }
12988
13549
  onGeneratedImage(image) {
12989
13550
  this.emitGeneratedImage(image);
@@ -12994,6 +13555,15 @@ var RuntimePresenter = class {
12994
13555
  this.emittedGeneratedImageKeys.add(key);
12995
13556
  this.emitEvent(image);
12996
13557
  }
13558
+ onSessionMedia(media) {
13559
+ this.emitSessionMedia(media);
13560
+ }
13561
+ emitSessionMedia(media) {
13562
+ const key = media.sourceHash ?? `${media.sourcePath ?? media.id}:${media.size}`;
13563
+ if (this.emittedSessionMediaKeys.has(key)) return;
13564
+ this.emittedSessionMediaKeys.add(key);
13565
+ this.emitEvent(media);
13566
+ }
12997
13567
  onTurnComplete(content) {
12998
13568
  this.emitEvent({ type: "turn_complete", content, ts: Date.now() });
12999
13569
  }
@@ -13084,7 +13654,7 @@ async function setupDaemon(args) {
13084
13654
  runtimeId: body.runtime.id,
13085
13655
  runtimeToken: body.runtimeToken,
13086
13656
  localApiPort: getLocalApiPort(args, {}),
13087
- localApiToken: (0, import_node_crypto2.randomBytes)(24).toString("hex"),
13657
+ localApiToken: (0, import_node_crypto.randomBytes)(24).toString("hex"),
13088
13658
  displayName: body.runtime.displayName ?? displayName
13089
13659
  });
13090
13660
  console.info("[aiden-agent] Runtime registered", {
@@ -13135,7 +13705,7 @@ async function loginWithDeviceCode(args) {
13135
13705
  runtimeId: body.runtime.id,
13136
13706
  runtimeToken: body.runtimeToken,
13137
13707
  localApiPort: getLocalApiPort(args, {}),
13138
- localApiToken: (0, import_node_crypto2.randomBytes)(24).toString("hex"),
13708
+ localApiToken: (0, import_node_crypto.randomBytes)(24).toString("hex"),
13139
13709
  displayName: body.runtime.displayName ?? displayName
13140
13710
  });
13141
13711
  console.info("[aiden-agent] Runtime registered", {
@@ -13152,7 +13722,7 @@ async function startDaemon(args) {
13152
13722
  const runtimeToken = argValue(args, "--token") ?? process.env.AIDEN_RUNTIME_TOKEN ?? stored.runtimeToken;
13153
13723
  const wsUrl = argValue(args, "--ws-url") ?? process.env.AIDEN_WS_URL ?? stored.wsUrl;
13154
13724
  let localApiPort = getLocalApiPort(args, stored);
13155
- const localApiToken = stored.localApiToken ?? (0, import_node_crypto2.randomBytes)(24).toString("hex");
13725
+ const localApiToken = stored.localApiToken ?? (0, import_node_crypto.randomBytes)(24).toString("hex");
13156
13726
  if (!runtimeId || !runtimeToken || !wsUrl) {
13157
13727
  throw new Error(
13158
13728
  "daemon requires runtimeId, runtime token, and wsUrl. Run setup first or pass --runtime-id/--token/--ws-url."
@@ -13211,6 +13781,23 @@ async function startDaemon(args) {
13211
13781
  pushLog(`disconnected ${reason}`);
13212
13782
  console.info("[aiden-agent] Daemon disconnected:", reason);
13213
13783
  });
13784
+ socket.on("desktop:rpc", (message) => {
13785
+ if (!message?.id || !message.method) return;
13786
+ try {
13787
+ const params = message.params ?? {};
13788
+ if (message.method !== "worktree.ensure") {
13789
+ throw new Error(`Method not found: ${message.method}`);
13790
+ }
13791
+ const result = ensureWorktree(params);
13792
+ socket.emit("desktop:rpc", { jsonrpc: "2.0", id: message.id, result });
13793
+ } catch (error) {
13794
+ socket.emit("desktop:rpc", {
13795
+ jsonrpc: "2.0",
13796
+ id: message.id,
13797
+ error: { code: -32e3, message: error.message }
13798
+ });
13799
+ }
13800
+ });
13214
13801
  socket.on("agent.execute", (payload) => {
13215
13802
  if (!payload?.runId || !payload.conversationId || typeof payload.content !== "string") {
13216
13803
  return;
@@ -13240,7 +13827,7 @@ async function startDaemon(args) {
13240
13827
  });
13241
13828
  pushLog(`accepted run=${payload.runId}`);
13242
13829
  console.info("[aiden-agent] Agent run accepted", correlation);
13243
- const cwd = payload.projectPath ?? process.cwd();
13830
+ const cwd = resolveRuntimeCwd(payload);
13244
13831
  const nowMs = Date.now();
13245
13832
  const touchActivity = () => {
13246
13833
  const entry = activeAgents.get(payload.runId);
@@ -13401,7 +13988,7 @@ async function startDaemon(args) {
13401
13988
  `${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit Aiden desktop or run setup again.`
13402
13989
  );
13403
13990
  }
13404
- await new Promise((resolve2, reject) => {
13991
+ await new Promise((resolve3, reject) => {
13405
13992
  const onError = (error) => {
13406
13993
  clearInterval(heartbeat);
13407
13994
  socket.disconnect();
@@ -13428,7 +14015,7 @@ async function startDaemon(args) {
13428
14015
  if (address && typeof address === "object") localApiPort = address.port;
13429
14016
  pushLog(`local_api listening port=${localApiPort}`);
13430
14017
  console.info("[aiden-agent] Local daemon API listening", { port: localApiPort });
13431
- resolve2();
14018
+ resolve3();
13432
14019
  });
13433
14020
  });
13434
14021
  let stopped = false;
@@ -13441,8 +14028,8 @@ async function startDaemon(args) {
13441
14028
  }
13442
14029
  activeAgents.clear();
13443
14030
  socket.disconnect();
13444
- await new Promise((resolve2) => {
13445
- localServer.close(() => resolve2());
14031
+ await new Promise((resolve3) => {
14032
+ localServer.close(() => resolve3());
13446
14033
  });
13447
14034
  };
13448
14035
  const controller = {
@@ -13726,7 +14313,10 @@ var WebPresenter = class {
13726
14313
  }
13727
14314
  wsClient;
13728
14315
  emittedGeneratedImageKeys = /* @__PURE__ */ new Set();
14316
+ emittedSessionMediaKeys = /* @__PURE__ */ new Set();
13729
14317
  toolNamesById = /* @__PURE__ */ new Map();
14318
+ toolInputsById = /* @__PURE__ */ new Map();
14319
+ browserRecordingPathHints = /* @__PURE__ */ new Set();
13730
14320
  suppressSessionLifecycle = false;
13731
14321
  setSuppressSessionLifecycle(suppress) {
13732
14322
  this.suppressSessionLifecycle = suppress;
@@ -13745,6 +14335,12 @@ var WebPresenter = class {
13745
14335
  }
13746
14336
  onToolUse(tool, input, toolId) {
13747
14337
  this.toolNamesById.set(toolId, tool);
14338
+ this.toolInputsById.set(toolId, input);
14339
+ for (const path of extractBrowserMediaPathHints(void 0, this.cwd, tool, {
14340
+ toolInput: input
14341
+ })) {
14342
+ this.browserRecordingPathHints.add(path);
14343
+ }
13748
14344
  this.wsClient.emitEvent({ type: "tool_use", id: toolId, name: tool, input, ts: Date.now() });
13749
14345
  }
13750
14346
  onToolResult(toolId, content, isError) {
@@ -13757,9 +14353,27 @@ var WebPresenter = class {
13757
14353
  });
13758
14354
  if (isError) return;
13759
14355
  const toolName = this.toolNamesById.get(toolId);
13760
- for (const image of extractGeneratedImagesFromToolResult(toolId, content, this.cwd, toolName)) {
14356
+ const toolInput = this.toolInputsById.get(toolId);
14357
+ const options = { toolInput, pathHints: [...this.browserRecordingPathHints] };
14358
+ for (const image of extractGeneratedImagesFromToolResult(
14359
+ toolId,
14360
+ content,
14361
+ this.cwd,
14362
+ toolName,
14363
+ options
14364
+ )) {
13761
14365
  this.emitGeneratedImage(image);
13762
14366
  }
14367
+ for (const media of extractSessionMediaFromToolResult(
14368
+ toolId,
14369
+ content,
14370
+ this.cwd,
14371
+ toolName,
14372
+ options
14373
+ )) {
14374
+ this.emitSessionMedia(media);
14375
+ if (media.sourcePath) this.browserRecordingPathHints.delete(media.sourcePath);
14376
+ }
13763
14377
  }
13764
14378
  onGeneratedImage(image) {
13765
14379
  this.emitGeneratedImage(image);
@@ -13770,6 +14384,15 @@ var WebPresenter = class {
13770
14384
  this.emittedGeneratedImageKeys.add(key);
13771
14385
  this.wsClient.emitEvent(image);
13772
14386
  }
14387
+ onSessionMedia(media) {
14388
+ this.emitSessionMedia(media);
14389
+ }
14390
+ emitSessionMedia(media) {
14391
+ const key = media.sourceHash ?? `${media.sourcePath ?? media.id}:${media.size}`;
14392
+ if (this.emittedSessionMediaKeys.has(key)) return;
14393
+ this.emittedSessionMediaKeys.add(key);
14394
+ this.wsClient.emitEvent(media);
14395
+ }
13773
14396
  onTurnComplete(content) {
13774
14397
  this.wsClient.emitEvent({ type: "turn_complete", content, ts: Date.now() });
13775
14398
  }
@@ -13858,9 +14481,9 @@ var WSClient = class {
13858
14481
  this.socket.emit("agent_event", event);
13859
14482
  }
13860
14483
  waitForConnection(timeoutMs = 15e3) {
13861
- return new Promise((resolve2, reject) => {
14484
+ return new Promise((resolve3, reject) => {
13862
14485
  if (this.socket.connected) {
13863
- resolve2();
14486
+ resolve3();
13864
14487
  return;
13865
14488
  }
13866
14489
  const timeout = setTimeout(
@@ -13869,7 +14492,7 @@ var WSClient = class {
13869
14492
  );
13870
14493
  this.socket.once("connect", () => {
13871
14494
  clearTimeout(timeout);
13872
- resolve2();
14495
+ resolve3();
13873
14496
  });
13874
14497
  this.socket.once("connect_error", (err) => {
13875
14498
  clearTimeout(timeout);
@@ -14010,9 +14633,9 @@ async function runSandbox(config) {
14010
14633
  onUserMessage: (payload) => {
14011
14634
  if (payload === null) {
14012
14635
  if (pendingMessageResolve) {
14013
- const resolve2 = pendingMessageResolve;
14636
+ const resolve3 = pendingMessageResolve;
14014
14637
  pendingMessageResolve = null;
14015
- resolve2(null);
14638
+ resolve3(null);
14016
14639
  }
14017
14640
  return;
14018
14641
  }
@@ -14034,9 +14657,9 @@ async function runSandbox(config) {
14034
14657
  fileCount: payload.files?.length ?? 0
14035
14658
  });
14036
14659
  if (pendingMessageResolve) {
14037
- const resolve2 = pendingMessageResolve;
14660
+ const resolve3 = pendingMessageResolve;
14038
14661
  pendingMessageResolve = null;
14039
- resolve2(payload);
14662
+ resolve3(payload);
14040
14663
  } else {
14041
14664
  queuedMessages.push(payload);
14042
14665
  }
@@ -14190,8 +14813,8 @@ async function runSandbox(config) {
14190
14813
  });
14191
14814
  if (stopped) break;
14192
14815
  }
14193
- const nextPayload = queuedMessages.shift() ?? await new Promise((resolve2) => {
14194
- pendingMessageResolve = resolve2;
14816
+ const nextPayload = queuedMessages.shift() ?? await new Promise((resolve3) => {
14817
+ pendingMessageResolve = resolve3;
14195
14818
  });
14196
14819
  if (nextPayload === null || stopped) {
14197
14820
  break;