@aiden-ade/sandbox-agent 0.1.25 → 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 (2) hide show
  1. package/dist/index.cjs +1060 -316
  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
 
@@ -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
  }
@@ -10225,10 +10316,7 @@ function createClaudeCliBackend(command = "claude", defaultArgs = []) {
10225
10316
  stdin.write(`${message}
10226
10317
  `);
10227
10318
  },
10228
- keepStdinOpen: true,
10229
- allowContinuation: true,
10230
- continuationIdleMs: 10 * 60 * 1e3
10231
- // 10 minutes
10319
+ keepStdinOpen: true
10232
10320
  }).run(context);
10233
10321
  }
10234
10322
  };
@@ -11336,7 +11424,33 @@ function parseGeminiStructuredLine(line, context, state) {
11336
11424
  break;
11337
11425
  }
11338
11426
  }
11339
- 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 = []) {
11340
11454
  return {
11341
11455
  kind: "gemini_cli",
11342
11456
  supportTier: "structured",
@@ -11351,19 +11465,9 @@ function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
11351
11465
  supportTier: "structured",
11352
11466
  command,
11353
11467
  args: [],
11354
- buildArgs: (ctx) => {
11355
- const args = ["-p", "", "--output-format", "stream-json"];
11356
- const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
11357
- const approvalMode = shouldUseReadOnlyRuntimePermissions(ctx.config) ? "plan" : "yolo";
11358
- args.push("--approval-mode", approvalMode);
11359
- if (resumeId) args.push("--resume", resumeId);
11360
- if (ctx.config.selectedModel?.trim())
11361
- args.push("--model", ctx.config.selectedModel.trim());
11362
- return [...args, ...defaultArgs];
11363
- },
11364
- promptViaStdin: true,
11468
+ buildArgs: (ctx, prompt) => buildAntigravityCliArgs(ctx, prompt, defaultArgs),
11365
11469
  augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
11366
- parseStructuredLine: parseGeminiStructuredLine
11470
+ parseStructuredLine: parseAntigravityLine
11367
11471
  }).run(context);
11368
11472
  } finally {
11369
11473
  cleanup();
@@ -11371,6 +11475,72 @@ function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
11371
11475
  }
11372
11476
  };
11373
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
+ }
11374
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;
11375
11545
  function parseKimiStderrLine(line, state) {
11376
11546
  const match = line.match(KIMI_RESUME_SESSION_RE);
@@ -11473,7 +11643,13 @@ function createKimiCliBackend(command = "kimi-cli", defaultArgs = []) {
11473
11643
  if (model) args.push("--model", model);
11474
11644
  return [...args, ...defaultArgs, "--prompt", prompt];
11475
11645
  },
11476
- 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
+ },
11477
11653
  parseStructuredLine: parseKimiStructuredLine,
11478
11654
  parseStderrLine: (line, _ctx, state) => parseKimiStderrLine(line, state)
11479
11655
  }).run(context);
@@ -11816,40 +11992,40 @@ function parseSkillInvocation(text) {
11816
11992
  function candidateRoots(cwd) {
11817
11993
  const roots = [];
11818
11994
  if (cwd) {
11819
- roots.push((0, import_path4.join)(cwd, ".agents", "skills"));
11820
- roots.push((0, import_path4.join)(cwd, ".claude", "skills"));
11821
- roots.push((0, import_path4.join)(cwd, ".codex", "skills"));
11822
- 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)) {
11823
11999
  let entries = [];
11824
12000
  try {
11825
- entries = (0, import_fs4.readdirSync)(cwd, { withFileTypes: true });
12001
+ entries = (0, import_fs5.readdirSync)(cwd, { withFileTypes: true });
11826
12002
  } catch {
11827
12003
  }
11828
12004
  for (const entry of entries) {
11829
12005
  if (!entry.isDirectory()) continue;
11830
12006
  if (entry.name.startsWith(".")) continue;
11831
- const child = (0, import_path4.join)(cwd, entry.name);
11832
- roots.push((0, import_path4.join)(child, ".agents", "skills"));
11833
- roots.push((0, import_path4.join)(child, ".claude", "skills"));
11834
- 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"));
11835
12011
  }
11836
12012
  }
11837
12013
  }
11838
- const home = (0, import_os4.homedir)();
11839
- roots.push((0, import_path4.join)(home, ".agents", "skills"));
11840
- roots.push((0, import_path4.join)(home, ".claude", "skills"));
11841
- 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"));
11842
12018
  return Array.from(new Set(roots));
11843
12019
  }
11844
12020
  function resolveSkillInvocation(text, cwd) {
11845
12021
  const parsed = parseSkillInvocation(text);
11846
12022
  if (!parsed) return null;
11847
12023
  for (const root of candidateRoots(cwd)) {
11848
- const skillPath = (0, import_path4.join)(root, parsed.command, "SKILL.md");
11849
- 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;
11850
12026
  let content;
11851
12027
  try {
11852
- content = (0, import_fs4.readFileSync)(skillPath, "utf-8");
12028
+ content = (0, import_fs5.readFileSync)(skillPath, "utf-8");
11853
12029
  } catch {
11854
12030
  continue;
11855
12031
  }
@@ -11898,6 +12074,8 @@ function defaultSelectedModelForBackend(backendKind) {
11898
12074
  return void 0;
11899
12075
  case "opencode_cli":
11900
12076
  return void 0;
12077
+ case "gemini_cli":
12078
+ return void 0;
11901
12079
  case "supatest_cli":
11902
12080
  return "medium";
11903
12081
  default:
@@ -12099,7 +12277,7 @@ Prefer relative paths and keep your work scoped to this project.`
12099
12277
  const promptText = this.buildPromptText(runtimeConfig);
12100
12278
  const runtimeCommand = runtimeConfig.runtimeCommand || this.runtime.runtimeCommand;
12101
12279
  const runtimeArgs = runtimeConfig.runtimeArgs || this.runtime.runtimeArgs || [];
12102
- 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(
12103
12281
  runtimeCommand || "generic-cli",
12104
12282
  runtimeArgs
12105
12283
  );
@@ -12165,18 +12343,18 @@ Prefer relative paths and keep your work scoped to this project.`
12165
12343
  `[base-machine-agent] Transient error (attempt ${attempt + 1}/${_BaseMachineAgent.MAX_RETRIES + 1}), retrying in ${delayMs}ms: ${lastResult.error}`
12166
12344
  );
12167
12345
  void this.presenter.onLog(`Transient error, retrying in ${Math.round(delayMs / 1e3)}s...`);
12168
- await new Promise((resolve2) => {
12346
+ await new Promise((resolve22) => {
12169
12347
  const signal = this.abortController?.signal;
12170
12348
  if (signal?.aborted) {
12171
- resolve2();
12349
+ resolve22();
12172
12350
  return;
12173
12351
  }
12174
- const timeoutId = setTimeout(resolve2, delayMs);
12352
+ const timeoutId = setTimeout(resolve22, delayMs);
12175
12353
  signal?.addEventListener(
12176
12354
  "abort",
12177
12355
  () => {
12178
12356
  clearTimeout(timeoutId);
12179
- resolve2();
12357
+ resolve22();
12180
12358
  },
12181
12359
  { once: true }
12182
12360
  );
@@ -12246,6 +12424,195 @@ Prefer relative paths and keep your work scoped to this project.`
12246
12424
  }
12247
12425
  }
12248
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
+ }
12249
12616
 
12250
12617
  // src/core-agent.ts
12251
12618
  var CoreAgent = class _CoreAgent extends BaseMachineAgent {
@@ -12544,149 +12911,179 @@ ${meta.description}`);
12544
12911
  }
12545
12912
  };
12546
12913
 
12547
- // src/generated-images.ts
12548
- var import_node_crypto = require("crypto");
12549
- var import_node_fs3 = require("fs");
12550
- var import_node_path3 = require("path");
12551
- var IMAGE_MIME_BY_EXT = {
12552
- ".gif": "image/gif",
12553
- ".jpeg": "image/jpeg",
12554
- ".jpg": "image/jpeg",
12555
- ".png": "image/png",
12556
- ".webp": "image/webp"
12557
- };
12558
- var MAX_GENERATED_IMAGE_BYTES = 20 * 1024 * 1024;
12559
- function isGeneratedImageToolName(toolName) {
12560
- if (!toolName) return false;
12561
- const normalized = toolName.toLowerCase().replace(/[^a-z0-9]+/g, "_");
12562
- return [
12563
- "image_generation",
12564
- "image_gen",
12565
- "imagegen",
12566
- "generate_image",
12567
- "edit_image",
12568
- "text_to_image",
12569
- "images_generate"
12570
- ].some((token) => normalized.includes(token));
12571
- }
12572
- function shouldExtractGeneratedImagesFromToolResult(toolName) {
12573
- return isGeneratedImageToolName(toolName);
12574
- }
12575
- function collectStringValues(value2, out) {
12576
- if (typeof value2 === "string") {
12577
- out.push(value2);
12578
- return;
12579
- }
12580
- if (Array.isArray(value2)) {
12581
- for (const item of value2) collectStringValues(item, out);
12582
- 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`);
12583
12934
  }
12584
- if (!value2 || typeof value2 !== "object") return;
12585
- for (const entry of Object.values(value2)) {
12586
- 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`);
12587
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`);
12588
12946
  }
12589
- function candidateStrings(content) {
12590
- const values = [content];
12591
- try {
12592
- collectStringValues(JSON.parse(content), values);
12593
- } 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`);
12594
12954
  }
12595
- return values;
12596
12955
  }
12597
- function extractPathCandidates(content) {
12598
- const paths = /* @__PURE__ */ new Set();
12599
- const imagePathPattern = /(?:file:\/\/)?(?:~|\.{1,2}|\/)?[^\s"'`)\]}<>]+?\.(?:png|jpe?g|gif|webp)\b/gi;
12600
- for (const value2 of candidateStrings(content)) {
12601
- for (const match of value2.matchAll(imagePathPattern)) {
12602
- paths.add(match[0]);
12603
- }
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());
12604
12965
  }
12605
- return [...paths];
12966
+ return result.stdout.trim();
12606
12967
  }
12607
- function normalizePath(candidate, cwd) {
12608
- let path = candidate.trim().replace(/^file:\/\//, "");
12968
+ function gitSafe(cwd, args) {
12609
12969
  try {
12610
- path = decodeURI(path);
12970
+ return git(cwd, args);
12611
12971
  } catch {
12972
+ return "";
12612
12973
  }
12613
- if (path.startsWith("~/")) return null;
12614
- if ((0, import_node_path3.isAbsolute)(path)) return path;
12615
- return cwd ? (0, import_node_path3.join)(cwd, path) : null;
12616
12974
  }
12617
- function readImageDimensions2(buffer, mimeType) {
12618
- if (mimeType === "image/png" && buffer.length >= 24 && buffer.toString("ascii", 1, 4) === "PNG") {
12619
- return { width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) };
12620
- }
12621
- if (mimeType === "image/gif" && buffer.length >= 10 && buffer.toString("ascii", 0, 3) === "GIF") {
12622
- 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;
12623
12993
  }
12624
- if (mimeType === "image/jpeg" && buffer.length > 4) {
12625
- let offset = 2;
12626
- while (offset + 9 < buffer.length) {
12627
- if (buffer[offset] !== 255) break;
12628
- const marker = buffer[offset + 1];
12629
- const length = buffer.readUInt16BE(offset + 2);
12630
- if (length < 2) break;
12631
- 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
+ });
12632
13045
  return {
12633
- width: buffer.readUInt16BE(offset + 7),
12634
- height: buffer.readUInt16BE(offset + 5)
13046
+ repoId: repo.repoId ?? null,
13047
+ repoUrl: repo.repoUrl,
13048
+ worktreePath: worktreePath2,
13049
+ branchName: input.branchName,
13050
+ baseBranch
12635
13051
  };
12636
- }
12637
- offset += 2 + length;
12638
- }
13052
+ })
13053
+ };
12639
13054
  }
12640
- 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
+ };
12641
13064
  }
12642
- function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName) {
12643
- if (!shouldExtractGeneratedImagesFromToolResult(toolName)) return [];
12644
- const images = [];
12645
- const seen = /* @__PURE__ */ new Set();
12646
- for (const candidate of extractPathCandidates(content)) {
12647
- const path = normalizePath(candidate, cwd);
12648
- if (!path || seen.has(path) || !(0, import_node_fs3.existsSync)(path)) continue;
12649
- seen.add(path);
12650
- const ext = (0, import_node_path3.extname)(path).toLowerCase();
12651
- const mimeType = IMAGE_MIME_BY_EXT[ext];
12652
- if (!mimeType) continue;
12653
- const stat = (0, import_node_fs3.statSync)(path);
12654
- if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_GENERATED_IMAGE_BYTES) continue;
12655
- const buffer = (0, import_node_fs3.readFileSync)(path);
12656
- const { width, height } = readImageDimensions2(buffer, mimeType);
12657
- images.push({
12658
- type: "generated_image",
12659
- id: (0, import_node_crypto.randomUUID)(),
12660
- filename: (0, import_node_path3.basename)(path),
12661
- mimeType,
12662
- size: buffer.length,
12663
- width,
12664
- height,
12665
- data: buffer.toString("base64"),
12666
- sourcePath: path,
12667
- sourceHash: (0, import_node_crypto.createHash)("sha256").update(buffer).digest("hex"),
12668
- toolId,
12669
- ts: Date.now()
12670
- });
12671
- }
12672
- 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();
13069
+ }
13070
+ function correlationLogFields(input) {
13071
+ return Object.fromEntries(
13072
+ Object.entries({
13073
+ taskId: input.taskId,
13074
+ conversationId: input.conversationId,
13075
+ runId: input.runId,
13076
+ messageId: input.messageId,
13077
+ cloudEnvironmentId: input.cloudEnvironmentId,
13078
+ provider: input.backendKind,
13079
+ backendKind: input.backendKind,
13080
+ agentVersion: input.agentVersion
13081
+ }).filter((entry) => {
13082
+ const value2 = entry[1];
13083
+ return typeof value2 === "string" && value2.trim().length > 0;
13084
+ }).map(([key, value2]) => [key, value2.trim()])
13085
+ );
12673
13086
  }
12674
-
12675
- // src/version.ts
12676
- var AGENT_VERSION = "0.1.25";
12677
-
12678
- // src/daemon.ts
12679
- var OPENCODE_FREE_MODEL_IDS = [
12680
- "opencode/north-mini-code-free",
12681
- "opencode/nemotron-3-ultra-free",
12682
- "opencode/deepseek-v4-flash-free",
12683
- "opencode/mimo-v2.5-free",
12684
- "opencode/big-pickle"
12685
- ];
12686
- var PRODUCTION_API_URL = "https://api.aiden-platform.com";
12687
- var PRODUCTION_WS_URL = "wss://ws.aiden-platform.com";
12688
- var LOCAL_API_URL = "http://localhost:8400";
12689
- var LOCAL_WS_URL = "ws://localhost:8401";
12690
13087
  async function probeLocalDaemonEndpoint(port, token, timeoutMs = 750) {
12691
13088
  try {
12692
13089
  const headers = {};
@@ -12708,33 +13105,33 @@ function describeOccupiedLocalDaemon(port) {
12708
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.`;
12709
13106
  }
12710
13107
  function getConfigPath() {
12711
- 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");
12712
13109
  }
12713
13110
  function getEndpointDefaultsPath() {
12714
- 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");
12715
13112
  }
12716
13113
  function readConfig() {
12717
13114
  const configPath = getConfigPath();
12718
- if (!(0, import_node_fs4.existsSync)(configPath)) return {};
12719
- 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"));
12720
13117
  }
12721
13118
  function readEndpointDefaults() {
12722
13119
  const endpointsPath = getEndpointDefaultsPath();
12723
- if (!(0, import_node_fs4.existsSync)(endpointsPath)) return {};
13120
+ if (!(0, import_node_fs3.existsSync)(endpointsPath)) return {};
12724
13121
  try {
12725
- return JSON.parse((0, import_node_fs4.readFileSync)(endpointsPath, "utf8"));
13122
+ return JSON.parse((0, import_node_fs3.readFileSync)(endpointsPath, "utf8"));
12726
13123
  } catch {
12727
13124
  return {};
12728
13125
  }
12729
13126
  }
12730
13127
  function writeConfig(config) {
12731
13128
  const configPath = getConfigPath();
12732
- (0, import_node_fs4.mkdirSync)((0, import_node_path4.dirname)(configPath), { recursive: true, mode: 448 });
12733
- (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 });
12734
13131
  }
12735
13132
  function removeConfig() {
12736
13133
  const configPath = getConfigPath();
12737
- 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 });
12738
13135
  }
12739
13136
  function argValue(args, name) {
12740
13137
  const index = args.indexOf(name);
@@ -12770,7 +13167,7 @@ function getLocalApiPort(args, stored) {
12770
13167
  }
12771
13168
  function sleep(ms) {
12772
13169
  if (ms <= 0) return Promise.resolve();
12773
- return new Promise((resolve2) => setTimeout(resolve2, ms));
13170
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
12774
13171
  }
12775
13172
  function normalizeBackendKind(backendKind) {
12776
13173
  if (backendKind === "codex") return "codex_app_server";
@@ -12802,6 +13199,14 @@ function extractModelIdsFromJson(value2) {
12802
13199
  }
12803
13200
  return [];
12804
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
+ }
12805
13210
  function discoverOpenCodeModels(executable, env) {
12806
13211
  for (const args of [
12807
13212
  ["models", "--json"],
@@ -12810,7 +13215,7 @@ function discoverOpenCodeModels(executable, env) {
12810
13215
  ]) {
12811
13216
  const result = (0, import_node_child_process3.spawnSync)(executable, args, {
12812
13217
  encoding: "utf8",
12813
- timeout: 5e3,
13218
+ timeout: MODEL_DISCOVERY_TIMEOUT_MS,
12814
13219
  env,
12815
13220
  windowsHide: true
12816
13221
  });
@@ -12823,7 +13228,27 @@ function discoverOpenCodeModels(executable, env) {
12823
13228
  }
12824
13229
  return OPENCODE_FREE_MODEL_IDS;
12825
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
+ }
12826
13250
  function discoverProviderModels(provider, executable, env) {
13251
+ if (provider === "gemini_cli") return discoverAntigravityModels(executable, env);
12827
13252
  if (provider === "opencode_cli") return discoverOpenCodeModels(executable, env);
12828
13253
  return [];
12829
13254
  }
@@ -12904,7 +13329,7 @@ function collectRuntimeMetadata() {
12904
13329
  metadata.cpuCores = cpuList.length;
12905
13330
  }
12906
13331
  try {
12907
- 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)());
12908
13333
  metadata.diskFreeBytes = disk.bavail * disk.bsize;
12909
13334
  metadata.diskTotalBytes = disk.blocks * disk.bsize;
12910
13335
  } catch {
@@ -12923,15 +13348,19 @@ async function collectRuntimeMetadataWithProviderLimits() {
12923
13348
  return metadata;
12924
13349
  }
12925
13350
  var RuntimePresenter = class {
12926
- constructor(socket, conversationId, runId, cwd, touchRunnerActivity) {
13351
+ constructor(socket, conversationId, runId, cwd, touchRunnerActivity, onRunComplete) {
12927
13352
  this.socket = socket;
12928
13353
  this.conversationId = conversationId;
12929
13354
  this.runId = runId;
12930
13355
  this.cwd = cwd;
12931
13356
  this.touchRunnerActivity = touchRunnerActivity;
13357
+ this.onRunComplete = onRunComplete;
12932
13358
  }
12933
13359
  emittedGeneratedImageKeys = /* @__PURE__ */ new Set();
13360
+ emittedSessionMediaKeys = /* @__PURE__ */ new Set();
12934
13361
  toolNamesById = /* @__PURE__ */ new Map();
13362
+ toolInputsById = /* @__PURE__ */ new Map();
13363
+ browserRecordingPathHints = /* @__PURE__ */ new Set();
12935
13364
  emitEvent(event) {
12936
13365
  this.touchRunnerActivity?.();
12937
13366
  this.socket.emit("agent.event", {
@@ -12954,6 +13383,12 @@ var RuntimePresenter = class {
12954
13383
  }
12955
13384
  onToolUse(tool, input, toolId) {
12956
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
+ }
12957
13392
  this.emitEvent({ type: "tool_use", id: toolId, name: tool, input, ts: Date.now() });
12958
13393
  }
12959
13394
  onToolResult(toolId, content, isError) {
@@ -12966,9 +13401,27 @@ var RuntimePresenter = class {
12966
13401
  });
12967
13402
  if (isError) return;
12968
13403
  const toolName = this.toolNamesById.get(toolId);
12969
- 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
+ )) {
12970
13413
  this.emitGeneratedImage(image);
12971
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
+ }
12972
13425
  }
12973
13426
  onGeneratedImage(image) {
12974
13427
  this.emitGeneratedImage(image);
@@ -12979,6 +13432,15 @@ var RuntimePresenter = class {
12979
13432
  this.emittedGeneratedImageKeys.add(key);
12980
13433
  this.emitEvent(image);
12981
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
+ }
12982
13444
  onTurnComplete(content) {
12983
13445
  this.emitEvent({ type: "turn_complete", content, ts: Date.now() });
12984
13446
  }
@@ -13015,6 +13477,7 @@ var RuntimePresenter = class {
13015
13477
  }
13016
13478
  onComplete(result) {
13017
13479
  this.emitEvent({ type: "query_complete", result, ts: Date.now() });
13480
+ this.onRunComplete?.();
13018
13481
  }
13019
13482
  };
13020
13483
  async function setupDaemon(args) {
@@ -13068,7 +13531,7 @@ async function setupDaemon(args) {
13068
13531
  runtimeId: body.runtime.id,
13069
13532
  runtimeToken: body.runtimeToken,
13070
13533
  localApiPort: getLocalApiPort(args, {}),
13071
- localApiToken: (0, import_node_crypto2.randomBytes)(24).toString("hex"),
13534
+ localApiToken: (0, import_node_crypto.randomBytes)(24).toString("hex"),
13072
13535
  displayName: body.runtime.displayName ?? displayName
13073
13536
  });
13074
13537
  console.info("[aiden-agent] Runtime registered", {
@@ -13119,7 +13582,7 @@ async function loginWithDeviceCode(args) {
13119
13582
  runtimeId: body.runtime.id,
13120
13583
  runtimeToken: body.runtimeToken,
13121
13584
  localApiPort: getLocalApiPort(args, {}),
13122
- localApiToken: (0, import_node_crypto2.randomBytes)(24).toString("hex"),
13585
+ localApiToken: (0, import_node_crypto.randomBytes)(24).toString("hex"),
13123
13586
  displayName: body.runtime.displayName ?? displayName
13124
13587
  });
13125
13588
  console.info("[aiden-agent] Runtime registered", {
@@ -13136,7 +13599,7 @@ async function startDaemon(args) {
13136
13599
  const runtimeToken = argValue(args, "--token") ?? process.env.AIDEN_RUNTIME_TOKEN ?? stored.runtimeToken;
13137
13600
  const wsUrl = argValue(args, "--ws-url") ?? process.env.AIDEN_WS_URL ?? stored.wsUrl;
13138
13601
  let localApiPort = getLocalApiPort(args, stored);
13139
- 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");
13140
13603
  if (!runtimeId || !runtimeToken || !wsUrl) {
13141
13604
  throw new Error(
13142
13605
  "daemon requires runtimeId, runtime token, and wsUrl. Run setup first or pass --runtime-id/--token/--ws-url."
@@ -13195,6 +13658,23 @@ async function startDaemon(args) {
13195
13658
  pushLog(`disconnected ${reason}`);
13196
13659
  console.info("[aiden-agent] Daemon disconnected:", reason);
13197
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
+ });
13198
13678
  socket.on("agent.execute", (payload) => {
13199
13679
  if (!payload?.runId || !payload.conversationId || typeof payload.content !== "string") {
13200
13680
  return;
@@ -13213,19 +13693,34 @@ async function startDaemon(args) {
13213
13693
  return;
13214
13694
  }
13215
13695
  socket.emit("agent.accepted", { runId: payload.runId });
13696
+ const correlation = correlationLogFields({
13697
+ taskId: payload.taskId ?? payload.taskMeta?.id,
13698
+ conversationId: payload.conversationId,
13699
+ runId: payload.runId,
13700
+ messageId: payload.messageId,
13701
+ cloudEnvironmentId: payload.cloudEnvironmentId,
13702
+ backendKind,
13703
+ agentVersion: AGENT_VERSION
13704
+ });
13216
13705
  pushLog(`accepted run=${payload.runId}`);
13217
- const cwd = payload.projectPath ?? process.cwd();
13706
+ console.info("[aiden-agent] Agent run accepted", correlation);
13707
+ const cwd = resolveRuntimeCwd(payload);
13218
13708
  const nowMs = Date.now();
13219
13709
  const touchActivity = () => {
13220
13710
  const entry = activeAgents.get(payload.runId);
13221
13711
  if (entry) entry.lastActivityAtMs = Date.now();
13222
13712
  };
13713
+ const releaseActiveRun = () => {
13714
+ if (!activeAgents.delete(payload.runId)) return;
13715
+ console.info("[aiden-agent] Agent run finished", correlation);
13716
+ };
13223
13717
  const presenter = new RuntimePresenter(
13224
13718
  socket,
13225
13719
  payload.conversationId,
13226
13720
  payload.runId,
13227
13721
  cwd,
13228
- touchActivity
13722
+ touchActivity,
13723
+ releaseActiveRun
13229
13724
  );
13230
13725
  const agent = new CoreAgent(presenter, {
13231
13726
  backendKind,
@@ -13254,7 +13749,7 @@ async function startDaemon(args) {
13254
13749
  taskMeta: payload.taskMeta,
13255
13750
  prMeta: payload.prMeta,
13256
13751
  conversationId: payload.conversationId,
13257
- taskId: payload.taskMeta?.id,
13752
+ taskId: payload.taskId ?? payload.taskMeta?.id,
13258
13753
  teamId: payload.teamId,
13259
13754
  currentUser: payload.currentUser,
13260
13755
  workflowTools: payload.workflowTools,
@@ -13263,13 +13758,17 @@ async function startDaemon(args) {
13263
13758
  }).catch((error) => {
13264
13759
  const message = error instanceof Error ? error.message : String(error);
13265
13760
  pushLog(`failed run=${payload.runId} ${message}`);
13761
+ console.error("[aiden-agent] Agent run failed", {
13762
+ ...correlation,
13763
+ error: message
13764
+ });
13266
13765
  socket.emit("agent.event", {
13267
13766
  conversationId: payload.conversationId,
13268
13767
  runId: payload.runId,
13269
13768
  event: { type: "session_error", error: message }
13270
13769
  });
13271
13770
  }).finally(() => {
13272
- activeAgents.delete(payload.runId);
13771
+ releaseActiveRun();
13273
13772
  });
13274
13773
  });
13275
13774
  socket.on("agent.abort", (payload) => {
@@ -13279,6 +13778,10 @@ async function startDaemon(args) {
13279
13778
  abortActiveAgent(entry);
13280
13779
  activeAgents.delete(payload.runId);
13281
13780
  pushLog(`aborted run=${payload.runId}`);
13781
+ console.info("[aiden-agent] Agent run aborted", {
13782
+ runId: payload.runId,
13783
+ agentVersion: AGENT_VERSION
13784
+ });
13282
13785
  });
13283
13786
  socket.on(
13284
13787
  "agent.tool_response",
@@ -13362,7 +13865,7 @@ async function startDaemon(args) {
13362
13865
  `${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit Aiden desktop or run setup again.`
13363
13866
  );
13364
13867
  }
13365
- await new Promise((resolve2, reject) => {
13868
+ await new Promise((resolve3, reject) => {
13366
13869
  const onError = (error) => {
13367
13870
  clearInterval(heartbeat);
13368
13871
  socket.disconnect();
@@ -13389,7 +13892,7 @@ async function startDaemon(args) {
13389
13892
  if (address && typeof address === "object") localApiPort = address.port;
13390
13893
  pushLog(`local_api listening port=${localApiPort}`);
13391
13894
  console.info("[aiden-agent] Local daemon API listening", { port: localApiPort });
13392
- resolve2();
13895
+ resolve3();
13393
13896
  });
13394
13897
  });
13395
13898
  let stopped = false;
@@ -13402,8 +13905,8 @@ async function startDaemon(args) {
13402
13905
  }
13403
13906
  activeAgents.clear();
13404
13907
  socket.disconnect();
13405
- await new Promise((resolve2) => {
13406
- localServer.close(() => resolve2());
13908
+ await new Promise((resolve3) => {
13909
+ localServer.close(() => resolve3());
13407
13910
  });
13408
13911
  };
13409
13912
  const controller = {
@@ -13607,6 +14110,78 @@ async function printDoctor(args = []) {
13607
14110
  );
13608
14111
  }
13609
14112
 
14113
+ // src/lifecycle-logger.ts
14114
+ var AgentLifecycleLogger = class _AgentLifecycleLogger {
14115
+ context;
14116
+ constructor(context = {}) {
14117
+ this.context = normalizeContext(context);
14118
+ }
14119
+ setContext(context) {
14120
+ this.context = normalizeContext({ ...this.context, ...context });
14121
+ }
14122
+ child(context) {
14123
+ return new _AgentLifecycleLogger({ ...this.context, ...context });
14124
+ }
14125
+ debug(event, fields = {}) {
14126
+ this.log("debug", event, fields);
14127
+ }
14128
+ info(event, fields = {}) {
14129
+ this.log("info", event, fields);
14130
+ }
14131
+ warn(event, fields = {}) {
14132
+ this.log("warn", event, fields);
14133
+ }
14134
+ error(event, fields = {}) {
14135
+ this.log("error", event, fields);
14136
+ }
14137
+ log(level, event, fields) {
14138
+ const record = omitUndefined({
14139
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
14140
+ level,
14141
+ component: "sandbox-agent",
14142
+ event,
14143
+ ...this.context,
14144
+ ...normalizeFields(fields)
14145
+ });
14146
+ console[level](JSON.stringify(record));
14147
+ }
14148
+ };
14149
+ function lifecycleContextFromEnv(env) {
14150
+ return normalizeContext({
14151
+ taskId: env.AIDEN_TASK_ID,
14152
+ conversationId: env.AIDEN_SESSION_ID,
14153
+ runId: env.AIDEN_RUN_ID,
14154
+ sandboxId: env.AIDEN_SANDBOX_ID,
14155
+ provider: env.AIDEN_SANDBOX_PROVIDER,
14156
+ backendKind: env.AIDEN_BACKEND_KIND,
14157
+ agentVersion: AGENT_VERSION
14158
+ });
14159
+ }
14160
+ function normalizeContext(context) {
14161
+ return omitUndefined({
14162
+ ...context,
14163
+ agentVersion: context.agentVersion ?? AGENT_VERSION
14164
+ });
14165
+ }
14166
+ function normalizeFields(fields) {
14167
+ const normalized = {};
14168
+ for (const [key, value2] of Object.entries(fields)) {
14169
+ if (value2 instanceof Error) {
14170
+ normalized[key] = {
14171
+ name: value2.name,
14172
+ message: value2.message,
14173
+ stack: value2.stack
14174
+ };
14175
+ } else {
14176
+ normalized[key] = value2;
14177
+ }
14178
+ }
14179
+ return normalized;
14180
+ }
14181
+ function omitUndefined(record) {
14182
+ return Object.fromEntries(Object.entries(record).filter(([, value2]) => value2 !== void 0));
14183
+ }
14184
+
13610
14185
  // src/web-presenter.ts
13611
14186
  var WebPresenter = class {
13612
14187
  constructor(wsClient, _conversationId, cwd) {
@@ -13615,7 +14190,10 @@ var WebPresenter = class {
13615
14190
  }
13616
14191
  wsClient;
13617
14192
  emittedGeneratedImageKeys = /* @__PURE__ */ new Set();
14193
+ emittedSessionMediaKeys = /* @__PURE__ */ new Set();
13618
14194
  toolNamesById = /* @__PURE__ */ new Map();
14195
+ toolInputsById = /* @__PURE__ */ new Map();
14196
+ browserRecordingPathHints = /* @__PURE__ */ new Set();
13619
14197
  suppressSessionLifecycle = false;
13620
14198
  setSuppressSessionLifecycle(suppress) {
13621
14199
  this.suppressSessionLifecycle = suppress;
@@ -13634,6 +14212,12 @@ var WebPresenter = class {
13634
14212
  }
13635
14213
  onToolUse(tool, input, toolId) {
13636
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
+ }
13637
14221
  this.wsClient.emitEvent({ type: "tool_use", id: toolId, name: tool, input, ts: Date.now() });
13638
14222
  }
13639
14223
  onToolResult(toolId, content, isError) {
@@ -13646,9 +14230,27 @@ var WebPresenter = class {
13646
14230
  });
13647
14231
  if (isError) return;
13648
14232
  const toolName = this.toolNamesById.get(toolId);
13649
- 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
+ )) {
13650
14242
  this.emitGeneratedImage(image);
13651
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
+ }
13652
14254
  }
13653
14255
  onGeneratedImage(image) {
13654
14256
  this.emitGeneratedImage(image);
@@ -13659,6 +14261,15 @@ var WebPresenter = class {
13659
14261
  this.emittedGeneratedImageKeys.add(key);
13660
14262
  this.wsClient.emitEvent(image);
13661
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
+ }
13662
14273
  onTurnComplete(content) {
13663
14274
  this.wsClient.emitEvent({ type: "turn_complete", content, ts: Date.now() });
13664
14275
  }
@@ -13725,8 +14336,9 @@ var WebPresenter = class {
13725
14336
 
13726
14337
  // src/ws-client.ts
13727
14338
  var WSClient = class {
13728
- socket;
13729
- constructor(wsUrl, sessionId, taskId, token, callbacks, agentVersion) {
14339
+ constructor(wsUrl, sessionId, taskId, token, callbacks, agentVersion, lifecycle) {
14340
+ this.lifecycle = lifecycle;
14341
+ this.lifecycle?.info("sandbox_agent_ws_socket_create", { wsUrl });
13730
14342
  this.socket = lookup(wsUrl, {
13731
14343
  query: { type: "agent", sessionId, taskId, agentVersion: agentVersion ?? "" },
13732
14344
  auth: { token },
@@ -13741,13 +14353,14 @@ var WSClient = class {
13741
14353
  });
13742
14354
  this.setupListeners(callbacks);
13743
14355
  }
14356
+ socket;
13744
14357
  emitEvent(event) {
13745
14358
  this.socket.emit("agent_event", event);
13746
14359
  }
13747
14360
  waitForConnection(timeoutMs = 15e3) {
13748
- return new Promise((resolve2, reject) => {
14361
+ return new Promise((resolve3, reject) => {
13749
14362
  if (this.socket.connected) {
13750
- resolve2();
14363
+ resolve3();
13751
14364
  return;
13752
14365
  }
13753
14366
  const timeout = setTimeout(
@@ -13756,7 +14369,7 @@ var WSClient = class {
13756
14369
  );
13757
14370
  this.socket.once("connect", () => {
13758
14371
  clearTimeout(timeout);
13759
- resolve2();
14372
+ resolve3();
13760
14373
  });
13761
14374
  this.socket.once("connect_error", (err) => {
13762
14375
  clearTimeout(timeout);
@@ -13767,20 +14380,26 @@ var WSClient = class {
13767
14380
  get connected() {
13768
14381
  return this.socket.connected;
13769
14382
  }
14383
+ setLifecycleContext(context) {
14384
+ this.lifecycle?.setContext(context);
14385
+ }
13770
14386
  close() {
14387
+ this.lifecycle?.info("sandbox_agent_ws_close_requested");
13771
14388
  this.socket.disconnect();
13772
14389
  }
13773
14390
  setupListeners(callbacks) {
13774
14391
  this.socket.on("connect", () => {
13775
- console.debug("[WSClient] Connected to WS server");
14392
+ this.lifecycle?.info("sandbox_agent_ws_socket_connected", { socketId: this.socket.id });
13776
14393
  callbacks.onConnect?.();
13777
14394
  });
13778
14395
  this.socket.on("disconnect", (reason) => {
13779
- console.debug(`[WSClient] Disconnected: ${reason}`);
14396
+ this.lifecycle?.info("sandbox_agent_ws_socket_disconnected", { reason });
13780
14397
  callbacks.onDisconnect?.(reason);
13781
14398
  });
13782
14399
  this.socket.on("connect_error", (error) => {
13783
- console.warn(`[WSClient] Connection error: ${error.message}`);
14400
+ this.lifecycle?.warn("sandbox_agent_ws_socket_connect_error", {
14401
+ error: error instanceof Error ? error : new Error(String(error))
14402
+ });
13784
14403
  });
13785
14404
  this.socket.on(
13786
14405
  "user_message",
@@ -13805,6 +14424,7 @@ var WSClient = class {
13805
14424
  messageId: data.messageId,
13806
14425
  maxIterations: data.maxIterations,
13807
14426
  taskId: data.taskId,
14427
+ cloudEnvironmentId: data.cloudEnvironmentId,
13808
14428
  conversationId: data.conversationId,
13809
14429
  teamId: data.teamId,
13810
14430
  currentUser: data.currentUser
@@ -13820,12 +14440,32 @@ var WSClient = class {
13820
14440
  }
13821
14441
  });
13822
14442
  this.socket.on("error", (data) => {
13823
- console.error(`[WSClient] Server error: ${data.message} (${data.code})`);
14443
+ this.lifecycle?.error("sandbox_agent_ws_server_error", {
14444
+ message: data.message,
14445
+ code: data.code
14446
+ });
13824
14447
  });
13825
14448
  }
13826
14449
  };
13827
14450
 
13828
14451
  // src/sandbox.ts
14452
+ function correlationLogFields2(input) {
14453
+ return Object.fromEntries(
14454
+ Object.entries({
14455
+ taskId: input.taskId,
14456
+ conversationId: input.conversationId,
14457
+ runId: input.runId,
14458
+ messageId: input.messageId,
14459
+ cloudEnvironmentId: input.cloudEnvironmentId,
14460
+ provider: input.backendKind,
14461
+ backendKind: input.backendKind,
14462
+ agentVersion: input.agentVersion
14463
+ }).filter((entry) => {
14464
+ const value2 = entry[1];
14465
+ return typeof value2 === "string" && value2.trim().length > 0;
14466
+ }).map(([key, value2]) => [key, value2.trim()])
14467
+ );
14468
+ }
13829
14469
  async function runSandbox(config) {
13830
14470
  let currentTask = config.task;
13831
14471
  let currentImages = [];
@@ -13840,15 +14480,26 @@ async function runSandbox(config) {
13840
14480
  let currentMaxIterations = config.maxIterations ?? 50;
13841
14481
  let currentTaskId = config.taskId;
13842
14482
  let currentConversationId = config.sessionId;
14483
+ let currentRunId;
14484
+ let currentMessageId;
14485
+ let currentCloudEnvironmentId;
13843
14486
  let currentTeamId = config.teamId;
13844
14487
  const workflowId = config.workflowId;
13845
14488
  const workflowExecutionId = config.workflowExecutionId;
13846
14489
  let currentUser = config.currentUser;
13847
14490
  let currentTerminalContextRefs;
13848
14491
  let currentTerminalSnapshots;
13849
- console.info("[sandbox] Connecting to WS server", {
13850
- wsUrl: config.wsUrl,
13851
- sessionId: config.sessionId
14492
+ const lifecycle = config.lifecycle ?? new AgentLifecycleLogger({
14493
+ taskId: config.taskId,
14494
+ conversationId: config.sessionId,
14495
+ runId: config.runId,
14496
+ sandboxId: config.sandboxId,
14497
+ provider: config.provider,
14498
+ backendKind: config.backendKind,
14499
+ agentVersion: AGENT_VERSION
14500
+ });
14501
+ lifecycle.info("sandbox_agent_ws_connect_attempt", {
14502
+ wsUrl: config.wsUrl
13852
14503
  });
13853
14504
  const wsClient = new WSClient(
13854
14505
  config.wsUrl,
@@ -13857,16 +14508,41 @@ async function runSandbox(config) {
13857
14508
  config.sessionToken,
13858
14509
  {
13859
14510
  onUserMessage: (payload) => {
14511
+ if (payload === null) {
14512
+ if (pendingMessageResolve) {
14513
+ const resolve3 = pendingMessageResolve;
14514
+ pendingMessageResolve = null;
14515
+ resolve3(null);
14516
+ }
14517
+ return;
14518
+ }
14519
+ lifecycle.setContext({
14520
+ runId: payload.runId,
14521
+ taskId: payload.taskId,
14522
+ conversationId: payload.conversationId,
14523
+ backendKind: payload.backendKind
14524
+ });
14525
+ wsClient.setLifecycleContext({
14526
+ runId: payload.runId,
14527
+ taskId: payload.taskId,
14528
+ conversationId: payload.conversationId,
14529
+ backendKind: payload.backendKind
14530
+ });
14531
+ lifecycle.info("sandbox_agent_user_message_received", {
14532
+ messageId: payload.messageId,
14533
+ hasImages: Boolean(payload.images?.length),
14534
+ fileCount: payload.files?.length ?? 0
14535
+ });
13860
14536
  if (pendingMessageResolve) {
13861
- const resolve2 = pendingMessageResolve;
14537
+ const resolve3 = pendingMessageResolve;
13862
14538
  pendingMessageResolve = null;
13863
- resolve2(payload);
14539
+ resolve3(payload);
13864
14540
  } else {
13865
14541
  queuedMessages.push(payload);
13866
14542
  }
13867
14543
  },
13868
14544
  onStop: () => {
13869
- console.info("[sandbox] Stop signal received");
14545
+ lifecycle.info("sandbox_agent_stop_received");
13870
14546
  if (currentAgent) {
13871
14547
  const killed = currentAgent.kill();
13872
14548
  if (!killed) currentAgent.abort();
@@ -13876,21 +14552,24 @@ async function runSandbox(config) {
13876
14552
  if (currentAgent) {
13877
14553
  const sent = currentAgent.sendToolResponse(toolId, response);
13878
14554
  if (!sent) {
13879
- console.warn("[sandbox] Failed to deliver tool_response to agent stdin", { toolId });
14555
+ lifecycle.warn("sandbox_agent_tool_response_delivery_failed", { toolId });
13880
14556
  }
13881
14557
  } else {
13882
- console.warn("[sandbox] No active agent for tool_response", { toolId });
14558
+ lifecycle.warn("sandbox_agent_tool_response_without_active_agent", { toolId });
13883
14559
  }
13884
14560
  },
13885
- onConnect: () => console.info("[sandbox] WS connected"),
13886
- onDisconnect: (reason) => console.info(`[sandbox] WS disconnected: ${reason}`)
14561
+ onConnect: () => lifecycle.info("sandbox_agent_ws_connected"),
14562
+ onDisconnect: (reason) => lifecycle.info("sandbox_agent_ws_disconnected", { reason })
13887
14563
  },
13888
- AGENT_VERSION
14564
+ AGENT_VERSION,
14565
+ lifecycle
13889
14566
  );
13890
14567
  try {
13891
14568
  await wsClient.waitForConnection();
13892
14569
  } catch (error) {
13893
- console.error("[sandbox] Failed to connect to WS server:", error.message);
14570
+ lifecycle.error("sandbox_agent_ws_connect_failed", {
14571
+ error: error instanceof Error ? error : new Error(String(error))
14572
+ });
13894
14573
  process.exit(1);
13895
14574
  }
13896
14575
  const presenter = new WebPresenter(wsClient, config.sessionId, config.projectPath);
@@ -13909,8 +14588,33 @@ async function runSandbox(config) {
13909
14588
  backendKind: activeBackendKind
13910
14589
  });
13911
14590
  currentAgent = agent;
14591
+ const correlation = correlationLogFields2({
14592
+ taskId: currentTaskId,
14593
+ conversationId: currentConversationId,
14594
+ runId: currentRunId,
14595
+ messageId: currentMessageId,
14596
+ cloudEnvironmentId: currentCloudEnvironmentId,
14597
+ backendKind: activeBackendKind,
14598
+ agentVersion: AGENT_VERSION
14599
+ });
14600
+ console.info("[sandbox] Agent run started", correlation);
13912
14601
  let result;
13913
14602
  try {
14603
+ lifecycle.setContext({
14604
+ taskId: currentTaskId,
14605
+ conversationId: currentConversationId,
14606
+ backendKind: activeBackendKind
14607
+ });
14608
+ wsClient.setLifecycleContext({
14609
+ taskId: currentTaskId,
14610
+ conversationId: currentConversationId,
14611
+ backendKind: activeBackendKind
14612
+ });
14613
+ lifecycle.info("sandbox_agent_run_start", {
14614
+ mode: activeMode,
14615
+ model: activeModel,
14616
+ maxIterations: currentMaxIterations
14617
+ });
13914
14618
  result = await agent.run({
13915
14619
  task: currentTask,
13916
14620
  maxIterations: currentMaxIterations,
@@ -13954,7 +14658,13 @@ async function runSandbox(config) {
13954
14658
  });
13955
14659
  } catch (error) {
13956
14660
  const errorMessage = error instanceof Error ? error.message : String(error);
13957
- console.error("[sandbox] Agent run failed:", errorMessage);
14661
+ console.error("[sandbox] Agent run failed", {
14662
+ ...correlation,
14663
+ error: errorMessage
14664
+ });
14665
+ lifecycle.error("sandbox_agent_run_failed", {
14666
+ error: error instanceof Error ? error : new Error(errorMessage)
14667
+ });
13958
14668
  result = {
13959
14669
  success: false,
13960
14670
  summary: "Agent run failed",
@@ -13965,13 +14675,23 @@ async function runSandbox(config) {
13965
14675
  };
13966
14676
  }
13967
14677
  currentAgent = null;
14678
+ lifecycle.info("sandbox_agent_run_complete", {
14679
+ success: result.success,
14680
+ iterations: result.iterations,
14681
+ error: result.error
14682
+ });
13968
14683
  if (result?.providerSessionId) {
13969
14684
  lastProviderSessionId = result.providerSessionId;
13970
14685
  }
14686
+ console.info("[sandbox] Agent run finished", {
14687
+ ...correlation,
14688
+ success: String(result.success),
14689
+ iterations: String(result.iterations)
14690
+ });
13971
14691
  if (stopped) break;
13972
14692
  }
13973
- const nextPayload = queuedMessages.shift() ?? await new Promise((resolve2) => {
13974
- pendingMessageResolve = resolve2;
14693
+ const nextPayload = queuedMessages.shift() ?? await new Promise((resolve3) => {
14694
+ pendingMessageResolve = resolve3;
13975
14695
  });
13976
14696
  if (nextPayload === null || stopped) {
13977
14697
  break;
@@ -14018,6 +14738,15 @@ async function runSandbox(config) {
14018
14738
  if (nextPayload.taskId !== void 0) {
14019
14739
  currentTaskId = nextPayload.taskId;
14020
14740
  }
14741
+ if (nextPayload.runId !== void 0) {
14742
+ currentRunId = nextPayload.runId;
14743
+ }
14744
+ if (nextPayload.messageId !== void 0) {
14745
+ currentMessageId = nextPayload.messageId;
14746
+ }
14747
+ if (nextPayload.cloudEnvironmentId !== void 0) {
14748
+ currentCloudEnvironmentId = nextPayload.cloudEnvironmentId;
14749
+ }
14021
14750
  if (nextPayload.conversationId !== void 0) {
14022
14751
  currentConversationId = nextPayload.conversationId;
14023
14752
  }
@@ -14035,6 +14764,7 @@ async function runSandbox(config) {
14035
14764
  }
14036
14765
  }
14037
14766
  presenter.setSuppressSessionLifecycle(false);
14767
+ lifecycle.info("sandbox_agent_process_exit");
14038
14768
  wsClient.close();
14039
14769
  }
14040
14770
 
@@ -14057,20 +14787,28 @@ async function runSessionFromEnv() {
14057
14787
  const providerSessionId = process.env.AIDEN_PROVIDER_SESSION_ID || void 0;
14058
14788
  const workflowId = process.env.AIDEN_WORKFLOW_ID || void 0;
14059
14789
  const workflowExecutionId = process.env.AIDEN_WORKFLOW_EXECUTION_ID || void 0;
14790
+ const sandboxId = process.env.AIDEN_SANDBOX_ID || void 0;
14791
+ const provider = process.env.AIDEN_SANDBOX_PROVIDER || void 0;
14792
+ const runId = process.env.AIDEN_RUN_ID || void 0;
14793
+ const lifecycle = new AgentLifecycleLogger(lifecycleContextFromEnv(process.env));
14060
14794
  if (!wsUrl || !sessionId || !taskId || !sessionToken || task == null) {
14795
+ lifecycle.error("sandbox_agent_bootstrap_missing_env", {
14796
+ hasWsUrl: Boolean(wsUrl),
14797
+ hasConversationId: Boolean(sessionId),
14798
+ hasTaskId: Boolean(taskId),
14799
+ hasSessionToken: Boolean(sessionToken),
14800
+ hasTask: task != null
14801
+ });
14061
14802
  throw new Error(
14062
14803
  "run-session is an internal Aiden sandbox/session command and must be launched by Aiden with session env vars. Missing required env vars: AIDEN_WS_URL, AIDEN_SESSION_ID, AIDEN_TASK_ID, AIDEN_SESSION_TOKEN, AIDEN_TASK"
14063
14804
  );
14064
14805
  }
14065
- console.info("[aiden-agent] Starting", {
14806
+ lifecycle.info("sandbox_agent_process_start", {
14066
14807
  sessionId,
14067
- taskId,
14068
14808
  projectPath,
14069
- backendKind,
14070
14809
  agentId,
14071
14810
  mode,
14072
- model,
14073
- version: AGENT_VERSION
14811
+ model
14074
14812
  });
14075
14813
  await runSandbox({
14076
14814
  wsUrl,
@@ -14090,6 +14828,10 @@ async function runSessionFromEnv() {
14090
14828
  providerSessionId,
14091
14829
  workflowId,
14092
14830
  workflowExecutionId,
14831
+ sandboxId,
14832
+ provider,
14833
+ runId,
14834
+ lifecycle,
14093
14835
  maxIterations: 50
14094
14836
  });
14095
14837
  }
@@ -14149,7 +14891,9 @@ async function main() {
14149
14891
  ${HELP_TEXT}`);
14150
14892
  }
14151
14893
  main().catch((error) => {
14152
- console.error("[aiden-agent] Fatal error:", error instanceof Error ? error.message : error);
14894
+ new AgentLifecycleLogger(lifecycleContextFromEnv(process.env)).error("sandbox_agent_fatal", {
14895
+ error: error instanceof Error ? error : new Error(String(error))
14896
+ });
14153
14897
  process.exit(1);
14154
14898
  });
14155
14899
  /*! Bundled license information: