@aiden-ade/sandbox-agent 0.1.48 → 0.1.49

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 +793 -306
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -36,7 +36,7 @@ var require_XMLHttpRequest = __commonJS({
36
36
  "use strict";
37
37
  var fs = require("fs");
38
38
  var Url = require("url");
39
- var spawn5 = require("child_process").spawn;
39
+ var spawn6 = require("child_process").spawn;
40
40
  module2.exports = XMLHttpRequest3;
41
41
  XMLHttpRequest3.XMLHttpRequest = XMLHttpRequest3;
42
42
  function XMLHttpRequest3(opts) {
@@ -332,7 +332,7 @@ var require_XMLHttpRequest = __commonJS({
332
332
  var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
333
333
  fs.writeFileSync(syncFile, "", "utf8");
334
334
  var execString = "var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http" + (ssl ? "s" : "") + ".request;var options = " + JSON.stringify(options) + ";var responseText = '';var responseData = Buffer.alloc(0);var req = doRequest(options, function(response) {response.on('data', function(chunk) { var data = Buffer.from(chunk); responseText += data.toString('utf8'); responseData = Buffer.concat([responseData, data]);});response.on('end', function() {fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText, data: responseData.toString('base64')}}), 'utf8');fs.unlinkSync('" + syncFile + "');});response.on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});}).on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});" + (data ? "req.write('" + JSON.stringify(data).slice(1, -1).replace(/'/g, "\\'") + "');" : "") + "req.end();";
335
- var syncProc = spawn5(process.argv[0], ["-e", execString]);
335
+ var syncProc = spawn6(process.argv[0], ["-e", execString]);
336
336
  var statusText;
337
337
  while (fs.existsSync(syncFile)) {
338
338
  }
@@ -4859,12 +4859,12 @@ function resolveCommand(args, env) {
4859
4859
  }
4860
4860
 
4861
4861
  // src/daemon.ts
4862
- var import_node_child_process5 = require("child_process");
4862
+ var import_node_child_process6 = require("child_process");
4863
4863
  var import_node_crypto3 = require("crypto");
4864
- var import_node_fs12 = require("fs");
4864
+ var import_node_fs13 = require("fs");
4865
4865
  var import_node_http = __toESM(require("http"), 1);
4866
- var import_node_os7 = require("os");
4867
- var import_node_path10 = require("path");
4866
+ var import_node_os8 = require("os");
4867
+ var import_node_path11 = require("path");
4868
4868
 
4869
4869
  // ../agent-core/dist/index.js
4870
4870
  var import_fs = require("fs");
@@ -5020,69 +5020,57 @@ Every \`start_task_session\` message must include all four of these:
5020
5020
  var GENERAL_PROMPT = `You are Alan, the General Agent \u2014 a flexible general-purpose agent profile. When identifying yourself, say "I'm Alan, the General Agent."`;
5021
5021
  var SETUP_PROMPT = `## You are the Setup Agent
5022
5022
 
5023
- You are the **Setup Agent** for this environment. When asked who you are, say
5024
- "I'm the Setup Agent (an Alan agent profile) for this environment." Your job is
5025
- to take a freshly cloned repository in a fresh cloud sandbox and get it fully
5026
- installed, configured, and **actually running and verified** \u2014 end to end. You
5027
- are not here to build features; you are here to make the environment work and
5028
- make that reproducible.
5023
+ You are the **Setup Agent** for this cloud environment. When identifying yourself, say "I'm the Setup Agent (an Alan agent profile) for this environment."
5024
+
5025
+ Your job is to take the repository in this fresh cloud sandbox from an unknown state to a **running, functionally verified, reproducible environment**. You are setting up the project, not building product features. Keep working until the application actually works.
5029
5026
 
5030
- ## Where you are running
5027
+ ## Environment model
5031
5028
 
5032
- You are inside an **ephemeral cloud sandbox whose filesystem will be captured as
5033
- a snapshot** and reused to boot future sessions instantly. This means:
5034
- - Install and build into the workspace \u2014 the snapshot preserves it.
5035
- - Keep tracked files clean: installs must land in git-ignored paths. Snapshot
5036
- capture is blocked if the git tree is dirty.
5037
- - If you must change tracked files (setup scripts, config, docs), commit them on
5038
- the working branch and open a PR for those changes \u2014 do not leave the tree
5039
- dirty and do not commit secrets.
5029
+ - You are inside an ephemeral cloud sandbox. Its filesystem can be captured as a reusable snapshot for future sessions.
5030
+ - Do not assume you are on the user's laptop or that tools outside this sandbox exist.
5031
+ - Install and build inside the workspace. Keep all setup repeatable from a fresh clone.
5032
+ - Persist the final ordered commands with \`set_environment_setup_commands\`. Each command is replayed in a fresh shell, so no command may depend on shell state from a previous command.
5033
+ - The snapshot policy is \`block_if_dirty\`: the working tree must be clean before capture.
5040
5034
 
5041
- ## What "done" means
5035
+ ## Required workflow
5042
5036
 
5043
- 1. Dependencies installed and the project builds.
5044
- 2. Setup commands persisted via \`set_environment_setup_commands\` so future
5045
- sessions reproduce this environment.
5046
- 3. The app is **running** and **verified functional** \u2014 dev server up, reachable
5047
- on the exposed port (curl), and rendering/working via agent-browser.
5048
- 4. Required environment variables are present (see below).
5049
- 5. After explicit human confirmation, the environment is captured via
5050
- \`capture_environment_snapshot\`.
5037
+ 1. **Explore** \u2014 read repository instructions, manifests, lockfiles, example env files, and service configuration. Detect the language, package manager, workspace layout, required services, and expected ports.
5038
+ 2. **Set up** \u2014 install dependencies, select compatible runtimes, configure non-secret defaults, and build. Diagnose failures yourself before asking the user.
5039
+ 3. **Run and verify** \u2014 start the real app or service. Prove it works using process stability, logs, HTTP probes, and \`agent-browser\` interaction when a UI exists. A process merely starting or dependencies merely installing is not success.
5040
+ 4. **Persist** \u2014 call \`set_environment_setup_commands\` with only the ordered commands you actually verified from a fresh-shell model.
5041
+ 5. **Prepare capture** \u2014 ensure every repository is clean. Verify that \`git status --porcelain --untracked-files=all\` is empty; generated untracked files count as dirty and must be removed or covered by an appropriate existing ignore rule. If setup requires tracked changes, create a focused branch, commit them, push it, and open a PR with \`github_pr_create\` explaining why the setup change is needed. Never commit secrets.
5042
+ 6. **Human gate** \u2014 summarize the evidence and ask the user to reply exactly \`Capture snapshot\`. Do not call \`capture_environment_snapshot\` before that explicit confirmation.
5043
+ 7. **Capture and re-verify** \u2014 after approval, call \`capture_environment_snapshot\` with \`afterSnapshot: "resume"\`. Poll \`get_cloud_machine\` until the snapshot is ready, then confirm the sandbox remains usable and repeat the key runtime probe before declaring completion.
5051
5044
 
5052
- ## Environment variables & secrets
5045
+ ## Autonomy
5053
5046
 
5054
- You cannot set secret values yourself, and you must never ask the user to paste a
5055
- secret value into the chat. When the repo needs env vars/secrets:
5056
- - Identify exactly which vars are required (from .env.example, config, or boot
5057
- errors) and tell the user the names and what each is for.
5058
- - Direct the user to add them in the secure env-vars UI. Adding/editing there is
5059
- **live-synced into this running sandbox immediately** (no restart needed): the
5060
- value is injected and the file lands at its configured destinationPath
5061
- (symlinked from /run/alan/env-files/\u2026).
5062
- - After the user says they've added a var, **confirm it actually arrived** by
5063
- reading that destination path (never echo the secret value) and re-running the
5064
- boot/verification.
5047
+ Prefer acting over asking. Read the repo, inspect logs, try the likely command, and recover from ordinary setup failures yourself. Ask only for information that cannot be derived or safely created: a required secret, paid credential, external account choice, or genuine product-intent fork. Ask one specific question at a time and continue independent work while waiting.
5065
5048
 
5066
- ## Human gate before snapshot
5049
+ Do not weaken a required functional check to make it pass. If the real process cannot start, the expected port cannot bind, or a browser-required UI cannot be exercised, diagnose and fix the cause. If the restriction is truly outside the sandbox, report the precise blocker instead of declaring setup complete from a mocked handler or unit-level substitute.
5067
5050
 
5068
- Do not capture the snapshot autonomously. Once the environment is installed,
5069
- running, and verified, summarize what you did and ask the user to confirm. Only
5070
- after explicit confirmation call \`capture_environment_snapshot\`. Keep the sandbox
5071
- alive while you wait.
5051
+ ## Environment variables and secrets
5072
5052
 
5073
- ## When to ask vs act
5053
+ Never ask the user to paste a secret into chat, and never print secret values. When a required secret is missing:
5074
5054
 
5075
- Act autonomously on install, build, config, starting the dev server, and
5076
- verification. Ask the user only for: secret values (via the secure UI, never in
5077
- chat) and final snapshot confirmation. If setup fails, prefer fixing it from live
5078
- evidence (logs, boot errors, curl/agent-browser output) before asking.
5055
+ 1. Name the exact variable and explain what it is used for.
5056
+ 2. Direct the user to this cloud environment's Environment Variables / env-files UI.
5057
+ 3. Explain that saved values sync into the running sandbox automatically with no restart required. Env files land at their configured \`destinationPath\` through \`/run/alan/env-files/\`.
5058
+ 4. After the user saves it, verify only that the key arrived at the configured destination or environment\u2014never echo the value\u2014then continue.
5079
5059
 
5080
- ## Setup script changes \u2192 commit + PR
5060
+ Derive and configure non-secret values yourself when the repository provides safe defaults.
5081
5061
 
5082
- If getting the environment reproducible requires editing tracked setup files or
5083
- adding scripts, make the smallest coherent change, commit it on the working
5084
- branch, and open a PR (github_pr_create) describing the setup adjustment. Keep
5085
- installs in git-ignored paths so the snapshot tree stays clean.`;
5062
+ ## Evidence and completion
5063
+
5064
+ Do not rely on your own claim that setup worked. Before finishing, report:
5065
+
5066
+ - the install/build/run commands that succeeded;
5067
+ - the runtime endpoint and functional checks performed;
5068
+ - whether any user-provided secret was required and whether its presence was verified safely;
5069
+ - the persisted setup commands;
5070
+ - the clean-tree or PR result;
5071
+ - the snapshot ID/state and the post-capture verification result.
5072
+
5073
+ If any required item is missing, you are not done.`;
5086
5074
  var REVIEW_PROMPT = `## You are the Review Agent
5087
5075
 
5088
5076
  You are the **Review Agent** (an Alan agent profile) for pull request review sessions. When identifying yourself, say "I'm the Review Agent."
@@ -9794,6 +9782,150 @@ function createAlanProblem(input) {
9794
9782
  // ../shared/dist/agent/session-result.js
9795
9783
  var SESSION_RESUME_FAILED_MESSAGE = "Session resume failed. The previous session may have expired or is no longer available. Please start a new session.";
9796
9784
 
9785
+ // ../shared/dist/agent/skill-repo-scope.js
9786
+ var SKILL_ROOT_MARKER_RE = /\/\.(?:claude|agents|codex)\/(?:skills|commands)(?:\/|$)/;
9787
+ function normalizePath(path) {
9788
+ return path.replace(/\\/g, "/").replace(/\/+$/, "");
9789
+ }
9790
+ function deriveSkillRepoRoot(sourcePath) {
9791
+ if (!sourcePath)
9792
+ return void 0;
9793
+ const normalized = normalizePath(sourcePath);
9794
+ const marker = normalized.match(SKILL_ROOT_MARKER_RE);
9795
+ if (!marker || marker.index === void 0)
9796
+ return void 0;
9797
+ const beforeRoot = normalized.slice(0, marker.index).replace(/\/+$/, "");
9798
+ return beforeRoot || void 0;
9799
+ }
9800
+ function deriveSkillRepoLabel(sourcePath) {
9801
+ const repoRoot = deriveSkillRepoRoot(sourcePath);
9802
+ if (!repoRoot)
9803
+ return void 0;
9804
+ const label = repoRoot.split("/").filter(Boolean).pop();
9805
+ if (!label || label === "~")
9806
+ return void 0;
9807
+ return label;
9808
+ }
9809
+ function isTeamSkillSource(source) {
9810
+ return source === "team" || source === "project";
9811
+ }
9812
+ function skillIdentityKey(skill) {
9813
+ const command = skill.command.trim();
9814
+ if (isTeamSkillSource(skill.source)) {
9815
+ const repoRoot = skill.repoRoot && normalizePath(skill.repoRoot) || deriveSkillRepoRoot(skill.sourcePath) || "unknown";
9816
+ return `team:${repoRoot}:${command}`;
9817
+ }
9818
+ return `global:${command}`;
9819
+ }
9820
+
9821
+ // ../shared/dist/agent/skill-discovery.js
9822
+ var SKILL_DIRECTORY_ROOTS = [".claude/skills", ".agents/skills", ".codex/skills"];
9823
+ var SKILL_PATH_RE = /(?:^|[/\\])\.(?:claude|agents|codex)[/\\]+skills[/\\]+([^/\\]+)[/\\]+SKILL\.md$/i;
9824
+ var LEGACY_COMMAND_PATH_RE = /(?:^|[/\\])\.claude[/\\]+commands[/\\]+([^/\\]+)\.md$/i;
9825
+ function normalizeSkillSourcePath(sourcePath) {
9826
+ return sourcePath.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
9827
+ }
9828
+ function unquoteScalar(value2) {
9829
+ const trimmed = value2.trim();
9830
+ if (trimmed.length < 2)
9831
+ return trimmed;
9832
+ const first = trimmed[0];
9833
+ const last = trimmed[trimmed.length - 1];
9834
+ if (first === '"' && last === '"' || first === "'" && last === "'") {
9835
+ return trimmed.slice(1, -1).trim();
9836
+ }
9837
+ return trimmed;
9838
+ }
9839
+ function readScalarField(lines, names) {
9840
+ const nameSet = new Set(names);
9841
+ for (const line of lines) {
9842
+ const colonIdx = line.indexOf(":");
9843
+ if (colonIdx === -1)
9844
+ continue;
9845
+ const key = line.slice(0, colonIdx).trim();
9846
+ if (!nameSet.has(key))
9847
+ continue;
9848
+ const value2 = unquoteScalar(line.slice(colonIdx + 1));
9849
+ if (value2)
9850
+ return value2;
9851
+ }
9852
+ return "";
9853
+ }
9854
+ function parseSkillDescription(markdown) {
9855
+ const normalized = markdown.replace(/\r\n/g, "\n").trim();
9856
+ if (!normalized)
9857
+ return "";
9858
+ const frontmatterMatch = normalized.match(/^---\n([\s\S]*?)\n---(?:\n([\s\S]*))?$/);
9859
+ if (frontmatterMatch) {
9860
+ const description = readScalarField(frontmatterMatch[1].split("\n"), ["description", "desc"]);
9861
+ if (description)
9862
+ return description;
9863
+ const body = frontmatterMatch[2]?.trim() ?? "";
9864
+ const firstBodyLine = body.split("\n").find((line) => line.trim());
9865
+ return firstBodyLine?.trim().slice(0, 200) ?? "";
9866
+ }
9867
+ const firstLine2 = normalized.split("\n").find((line) => line.trim());
9868
+ if (!firstLine2)
9869
+ return "";
9870
+ const inlineDescription = readScalarField([firstLine2], ["description", "desc"]);
9871
+ if (inlineDescription)
9872
+ return inlineDescription;
9873
+ return firstLine2.trim().slice(0, 200);
9874
+ }
9875
+ function extractSkillCommandFromPath(sourcePath) {
9876
+ const normalized = normalizeSkillSourcePath(sourcePath);
9877
+ const skillPathMatch = normalized.match(SKILL_PATH_RE);
9878
+ if (skillPathMatch?.[1])
9879
+ return skillPathMatch[1];
9880
+ const legacyCommandMatch = normalized.match(LEGACY_COMMAND_PATH_RE);
9881
+ if (legacyCommandMatch?.[1])
9882
+ return legacyCommandMatch[1];
9883
+ return null;
9884
+ }
9885
+ function normalizeSkillEntry(entry, defaultSource = "team") {
9886
+ const sourcePath = normalizeSkillSourcePath(entry.sourcePath ?? entry.path ?? "");
9887
+ const command = entry.command ?? entry.cmd ?? extractSkillCommandFromPath(sourcePath);
9888
+ if (!command)
9889
+ return null;
9890
+ const description = entry.description ?? entry.desc ?? parseSkillDescription(entry.content ?? "");
9891
+ const source = entry.source ?? entry.src ?? defaultSource;
9892
+ const repoRoot = entry.repoRoot ?? (isTeamSkillSource(source) ? deriveSkillRepoRoot(sourcePath) : void 0);
9893
+ const repoLabel = entry.repoLabel ?? (isTeamSkillSource(source) ? deriveSkillRepoLabel(sourcePath) : void 0);
9894
+ return {
9895
+ command,
9896
+ description,
9897
+ source,
9898
+ sourcePath,
9899
+ ...repoRoot ? { repoRoot } : {},
9900
+ ...repoLabel ? { repoLabel } : {}
9901
+ };
9902
+ }
9903
+ function skillPriority(source) {
9904
+ switch (source) {
9905
+ case "team":
9906
+ case "project":
9907
+ return 3;
9908
+ case "global":
9909
+ return 2;
9910
+ case "alan":
9911
+ return 1;
9912
+ }
9913
+ }
9914
+ function dedupeSkillEntries(entries) {
9915
+ const deduped = /* @__PURE__ */ new Map();
9916
+ for (const entry of entries) {
9917
+ const key = skillIdentityKey(entry);
9918
+ const existing = deduped.get(key);
9919
+ if (!existing || skillPriority(entry.source) > skillPriority(existing.source)) {
9920
+ deduped.set(key, entry);
9921
+ }
9922
+ }
9923
+ return Array.from(deduped.values());
9924
+ }
9925
+ function normalizeSkillEntries(entries, defaultSource = "team") {
9926
+ return dedupeSkillEntries(entries.map((entry) => normalizeSkillEntry(entry, defaultSource)).filter((entry) => Boolean(entry)));
9927
+ }
9928
+
9797
9929
  // ../shared/dist/agent/system-prompt.js
9798
9930
  var ALAN_IDENTITY_PROMPT = `You are Alan, the software factory agent. Alan ships software end to end \u2014 plan, write, review, test, deploy.
9799
9931
 
@@ -10155,13 +10287,78 @@ var GROK_CLI_MODELS = [
10155
10287
  { id: "grok-composer-2.5-fast", name: "Composer 2.5 Fast" }
10156
10288
  ];
10157
10289
  var COPILOT_CLI_MODELS = [
10158
- { id: "claude-sonnet-4.5", name: "Claude Sonnet 4.5" },
10290
+ { id: "auto", name: "Auto" },
10291
+ {
10292
+ id: "gpt-5.6-sol",
10293
+ name: "GPT-5.6 Sol",
10294
+ capabilities: { contextWindows: [], effortLevels: GPT_5_6_ULTRA_EFFORT_LEVELS }
10295
+ },
10296
+ {
10297
+ id: "gpt-5.6-terra",
10298
+ name: "GPT-5.6 Terra",
10299
+ capabilities: { contextWindows: [], effortLevels: GPT_5_6_ULTRA_EFFORT_LEVELS }
10300
+ },
10301
+ {
10302
+ id: "gpt-5.6-luna",
10303
+ name: "GPT-5.6 Luna",
10304
+ capabilities: { contextWindows: [], effortLevels: GPT_5_6_EFFORT_LEVELS }
10305
+ },
10306
+ {
10307
+ id: "gpt-5.5",
10308
+ name: "GPT-5.5",
10309
+ capabilities: { contextWindows: [], effortLevels: GPT_5_5_EFFORT_LEVELS }
10310
+ },
10311
+ {
10312
+ id: "gpt-5.4",
10313
+ name: "GPT-5.4",
10314
+ capabilities: { contextWindows: [], effortLevels: GPT_5_4_EFFORT_LEVELS }
10315
+ },
10316
+ {
10317
+ id: "gpt-5.3-codex",
10318
+ name: "GPT-5.3 Codex",
10319
+ capabilities: { contextWindows: [], effortLevels: CODEX_5X_EFFORT_LEVELS }
10320
+ },
10321
+ {
10322
+ id: "gpt-5.4-mini",
10323
+ name: "GPT-5.4 Mini",
10324
+ capabilities: { contextWindows: [], effortLevels: GPT_5_4_EFFORT_LEVELS }
10325
+ },
10326
+ { id: "gpt-5-mini", name: "GPT-5 Mini" },
10327
+ {
10328
+ id: "claude-sonnet-5",
10329
+ name: "Claude Sonnet 5",
10330
+ capabilities: { contextWindows: ["200k", "1m"], effortLevels: CLAUDE_EFFORT_LEVELS }
10331
+ },
10159
10332
  { id: "claude-sonnet-4.6", name: "Claude Sonnet 4.6" },
10160
- { id: "claude-haiku-4.5", name: "Claude Haiku 4.5" },
10161
- { id: "gpt-5.3-codex", name: "GPT-5.3 Codex" },
10162
- { id: "gpt-5.4", name: "GPT-5.4" },
10163
- { id: "gpt-5.4-mini", name: "GPT-5.4 Mini" },
10164
- { id: "auto", name: "Auto" }
10333
+ { id: "claude-sonnet-4.5", name: "Claude Sonnet 4.5" },
10334
+ {
10335
+ id: "claude-haiku-4.5",
10336
+ name: "Claude Haiku 4.5",
10337
+ capabilities: { contextWindows: ["200k"], effortLevels: [] }
10338
+ },
10339
+ {
10340
+ id: "claude-fable-5",
10341
+ name: "Claude Fable 5",
10342
+ capabilities: { contextWindows: ["1m"], effortLevels: CLAUDE_EFFORT_LEVELS }
10343
+ },
10344
+ {
10345
+ id: "claude-opus-5",
10346
+ name: "Claude Opus 5",
10347
+ capabilities: { contextWindows: ["200k", "1m"], effortLevels: CLAUDE_EFFORT_LEVELS }
10348
+ },
10349
+ {
10350
+ id: "claude-opus-4.8",
10351
+ name: "Claude Opus 4.8",
10352
+ capabilities: { contextWindows: ["200k", "1m"], effortLevels: CLAUDE_EFFORT_LEVELS }
10353
+ },
10354
+ { id: "claude-opus-4.8-fast", name: "Claude Opus 4.8 Fast" },
10355
+ { id: "claude-opus-4.7", name: "Claude Opus 4.7" },
10356
+ { id: "claude-opus-4.6", name: "Claude Opus 4.6" },
10357
+ { id: "claude-opus-4.5", name: "Claude Opus 4.5" },
10358
+ { id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro Preview" },
10359
+ { id: "gemini-3.6-flash", name: "Gemini 3.6 Flash" },
10360
+ { id: "gemini-3.5-flash", name: "Gemini 3.5 Flash" },
10361
+ { id: "kimi-k2.7-code", name: "Kimi K2.7 Code" }
10165
10362
  ];
10166
10363
  var OPENCODE_PAID_MODEL = {
10167
10364
  billing: "paid",
@@ -12705,7 +12902,11 @@ function buildAntigravityCliArgs(context, prompt, defaultArgs = [], capabilities
12705
12902
  const args = [];
12706
12903
  const selectedModel = context.config.selectedModel?.trim();
12707
12904
  const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
12708
- if (selectedModel) args.push("--model", selectedModel);
12905
+ if (selectedModel) {
12906
+ args.push("--model", selectedModel);
12907
+ const effort = context.config.selectedEffortLevel?.trim().toLowerCase();
12908
+ if (effort && isEffortLevel(effort)) args.push("--effort", effort);
12909
+ }
12709
12910
  if (resumeId) args.push("--conversation", resumeId);
12710
12911
  if (!shouldUseReadOnlyRuntimePermissions(context.config)) {
12711
12912
  args.push("--dangerously-skip-permissions");
@@ -12716,6 +12917,14 @@ function buildAntigravityCliArgs(context, prompt, defaultArgs = [], capabilities
12716
12917
  args.push("--print-timeout", "10m");
12717
12918
  return [...args, ...defaultArgs, "-p", prompt];
12718
12919
  }
12920
+ function buildAntigravityAugmentedPrompt(context, images) {
12921
+ const isResume = Boolean(
12922
+ context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim()
12923
+ );
12924
+ const base = buildResumeAwarePrompt(context.config, context.promptText, { isResume });
12925
+ const prompt = context.config.mode === "plan" ? buildPlanModePrefix(base) : base;
12926
+ return appendImagePathReferences(prompt, images);
12927
+ }
12719
12928
  function createAntigravityCliBackend(command = "agy", defaultArgs = []) {
12720
12929
  return {
12721
12930
  kind: "antigravity_cli",
@@ -12737,7 +12946,7 @@ function createAntigravityCliBackend(command = "agy", defaultArgs = []) {
12737
12946
  command,
12738
12947
  args: [],
12739
12948
  buildArgs: (ctx, prompt) => buildAntigravityCliArgs(ctx, prompt, defaultArgs, capabilities),
12740
- augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
12949
+ augmentPrompt: (ctx) => buildAntigravityAugmentedPrompt(ctx, imageFiles),
12741
12950
  parseStructuredLine: parseAntigravityLine
12742
12951
  }).run(context);
12743
12952
  } finally {
@@ -14904,6 +15113,41 @@ var parseCopilotStructuredLine = createStructuredLineParser(
14904
15113
  "copilot_cli",
14905
15114
  handleCopilotStructuredEvent
14906
15115
  );
15116
+ function buildCopilotCliArgs(context, prompt, defaultArgs = []) {
15117
+ const args = [
15118
+ "--autopilot",
15119
+ "--yolo",
15120
+ "--max-autopilot-continues",
15121
+ "20",
15122
+ "-s",
15123
+ "--stream",
15124
+ "on",
15125
+ "--output-format",
15126
+ "json",
15127
+ // Without this flag, real captured sessions on this machine show every
15128
+ // assistant.message carrying only an opaque, non-decodable `reasoningOpaque`
15129
+ // blob — never `reasoningText` — so parsers/copilot.ts's onThinking() never
15130
+ // fires and the thinking stream never reaches the UI. `--enable-reasoning-summaries`
15131
+ // is documented in `copilot --help` as exactly this: "Request reasoning
15132
+ // summaries for OpenAI models." Always on — it's additive (models/providers
15133
+ // that don't support summaries are unaffected) and this is the "start in
15134
+ // thinking mode by default" behavior other providers already give equivalent
15135
+ // visibility into.
15136
+ "--enable-reasoning-summaries"
15137
+ ];
15138
+ if (shouldUseReadOnlyRuntimePermissions(context.config)) {
15139
+ args.push("--deny-tool", "write");
15140
+ }
15141
+ if (context.config.selectedModel?.trim()) {
15142
+ args.push("--model", context.config.selectedModel.trim());
15143
+ }
15144
+ const effort = context.config.selectedEffortLevel?.trim().toLowerCase();
15145
+ if (effort && isEffortLevel(effort)) {
15146
+ args.push("--effort", effort);
15147
+ }
15148
+ args.push("-p", prompt);
15149
+ return [...args, ...defaultArgs];
15150
+ }
14907
15151
  function createCopilotCliBackend(command = "copilot", defaultArgs = []) {
14908
15152
  return {
14909
15153
  kind: "copilot_cli",
@@ -14919,25 +15163,7 @@ function createCopilotCliBackend(command = "copilot", defaultArgs = []) {
14919
15163
  supportTier: "structured",
14920
15164
  command,
14921
15165
  args: [],
14922
- buildArgs: (ctx, prompt) => {
14923
- const args = [
14924
- "--autopilot",
14925
- "--yolo",
14926
- "--max-autopilot-continues",
14927
- "20",
14928
- "-s",
14929
- "--stream",
14930
- "on",
14931
- "--output-format",
14932
- "json",
14933
- "-p",
14934
- prompt
14935
- ];
14936
- if (ctx.config.selectedModel?.trim()) {
14937
- args.push("--model", ctx.config.selectedModel.trim());
14938
- }
14939
- return [...args, ...defaultArgs];
14940
- },
15166
+ buildArgs: (ctx, prompt) => buildCopilotCliArgs(ctx, prompt, defaultArgs),
14941
15167
  augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
14942
15168
  parseStructuredLine: parseCopilotStructuredLine,
14943
15169
  // Local background Agent/detached work uses the shared result gate; keep
@@ -18194,7 +18420,7 @@ function extractPathCandidates(content, options, extensionsPattern = "png|jpe?g|
18194
18420
  ...options.pathHints ?? []
18195
18421
  ];
18196
18422
  }
18197
- function normalizePath(candidate, cwd) {
18423
+ function normalizePath2(candidate, cwd) {
18198
18424
  let path = candidate.trim().replace(/^file:\/\//, "");
18199
18425
  try {
18200
18426
  path = decodeURI(path);
@@ -18217,7 +18443,7 @@ function extractBrowserMediaPathHints(content, cwd, toolName, options = {}, kind
18217
18443
  return extractPathCandidatesFromValues(
18218
18444
  collectContentStrings(content, options.toolInput),
18219
18445
  extensionsPattern
18220
- ).map((candidate) => normalizePath(candidate, effectiveCwd)).filter((path) => Boolean(path));
18446
+ ).map((candidate) => normalizePath2(candidate, effectiveCwd)).filter((path) => Boolean(path));
18221
18447
  }
18222
18448
  function readImageDimensions2(buffer, mimeType) {
18223
18449
  if (mimeType === "image/png" && buffer.length >= 24 && buffer.toString("ascii", 1, 4) === "PNG") {
@@ -18250,7 +18476,7 @@ function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName, op
18250
18476
  const seen = /* @__PURE__ */ new Set();
18251
18477
  const effectiveCwd = resolveToolCwd(cwd, options.toolInput);
18252
18478
  for (const candidate of extractPathCandidates(content, options)) {
18253
- const path = normalizePath(candidate, effectiveCwd);
18479
+ const path = normalizePath2(candidate, effectiveCwd);
18254
18480
  if (!path || seen.has(path) || !(0, import_fs9.existsSync)(path)) continue;
18255
18481
  seen.add(path);
18256
18482
  const ext = (0, import_path9.extname)(path).toLowerCase();
@@ -18289,7 +18515,7 @@ function extractSessionMediaFromToolResult(toolId, content, cwd, toolName, optio
18289
18515
  const maxBytes = kind === "video" ? MAX_SESSION_VIDEO_BYTES : MAX_GENERATED_IMAGE_BYTES;
18290
18516
  const mimeByExt = kind === "video" ? VIDEO_MIME_BY_EXT : IMAGE_MIME_BY_EXT;
18291
18517
  for (const candidate of extractPathCandidates(content, options, extensionsPattern)) {
18292
- const path = normalizePath(candidate, effectiveCwd);
18518
+ const path = normalizePath2(candidate, effectiveCwd);
18293
18519
  if (!path || seen.has(path) || !(0, import_fs9.existsSync)(path)) continue;
18294
18520
  seen.add(path);
18295
18521
  const ext = (0, import_path9.extname)(path).toLowerCase();
@@ -22377,6 +22603,10 @@ Object.assign(lookup, {
22377
22603
  connect: lookup
22378
22604
  });
22379
22605
 
22606
+ // src/cli-ensure-install.ts
22607
+ var import_node_child_process3 = require("child_process");
22608
+ var import_node_os4 = require("os");
22609
+
22380
22610
  // src/cli-executable.ts
22381
22611
  var import_node_child_process2 = require("child_process");
22382
22612
  var import_node_fs3 = require("fs");
@@ -22437,6 +22667,8 @@ var CLI_COMMAND_ALIASES = {
22437
22667
  grok: ["grok-build"]
22438
22668
  };
22439
22669
  var resolvedCliCache = /* @__PURE__ */ new Map();
22670
+ var NEGATIVE_RESOLUTION_TTL_MS = 15e3;
22671
+ var negativeResolutionExpiry = /* @__PURE__ */ new Map();
22440
22672
  var cachedLoginShellEnv = null;
22441
22673
  function parseEnvOutput(output) {
22442
22674
  const loginEnv = {};
@@ -22477,9 +22709,23 @@ function getDaemonCliEnvironment() {
22477
22709
  }
22478
22710
  function cacheResolvedCli(command, resolvedPath) {
22479
22711
  resolvedCliCache.set(command, resolvedPath);
22712
+ if (resolvedPath === null) {
22713
+ negativeResolutionExpiry.set(command, Date.now() + NEGATIVE_RESOLUTION_TTL_MS);
22714
+ } else {
22715
+ negativeResolutionExpiry.delete(command);
22716
+ }
22480
22717
  }
22481
22718
  function getCachedResolvedCli(command) {
22482
- return resolvedCliCache.get(command);
22719
+ const cached = resolvedCliCache.get(command);
22720
+ if (cached === null) {
22721
+ const expiresAt = negativeResolutionExpiry.get(command);
22722
+ if (expiresAt === void 0 || Date.now() >= expiresAt) {
22723
+ resolvedCliCache.delete(command);
22724
+ negativeResolutionExpiry.delete(command);
22725
+ return void 0;
22726
+ }
22727
+ }
22728
+ return cached;
22483
22729
  }
22484
22730
  function resolveViaWhere(command, env) {
22485
22731
  if ((0, import_node_os3.platform)() !== "win32") return null;
@@ -22602,11 +22848,11 @@ function augmentCliPath2(env) {
22602
22848
  function resolveCliExecutable(command, env = getDaemonCliEnvironment()) {
22603
22849
  const trimmed = command.trim();
22604
22850
  if (!trimmed) return null;
22605
- const cached = resolvedCliCache.get(trimmed);
22851
+ const cached = getCachedResolvedCli(trimmed);
22606
22852
  if (cached === null) return null;
22607
22853
  if (cached && isRunnableFile(cached)) return cached;
22608
22854
  if ((trimmed.includes("/") || trimmed.includes("\\")) && isRunnableFile(trimmed)) {
22609
- resolvedCliCache.set(trimmed, trimmed);
22855
+ cacheResolvedCli(trimmed, trimmed);
22610
22856
  return trimmed;
22611
22857
  }
22612
22858
  const enriched = augmentCliPath2(env);
@@ -22638,16 +22884,16 @@ function resolveCliExecutable(command, env = getDaemonCliEnvironment()) {
22638
22884
  if (seen.has(candidate)) continue;
22639
22885
  seen.add(candidate);
22640
22886
  if (isRunnableFile(candidate)) {
22641
- resolvedCliCache.set(trimmed, candidate);
22887
+ cacheResolvedCli(trimmed, candidate);
22642
22888
  return candidate;
22643
22889
  }
22644
22890
  }
22645
22891
  const viaWhere = resolveViaWhere(trimmed, enriched);
22646
22892
  if (viaWhere) {
22647
- resolvedCliCache.set(trimmed, viaWhere);
22893
+ cacheResolvedCli(trimmed, viaWhere);
22648
22894
  return viaWhere;
22649
22895
  }
22650
- resolvedCliCache.set(trimmed, null);
22896
+ cacheResolvedCli(trimmed, null);
22651
22897
  return null;
22652
22898
  }
22653
22899
  function normalizeDiscoverableProvider(provider) {
@@ -22685,26 +22931,128 @@ function resolveBackendRuntimeCommand(backendKind, env = getDaemonCliEnvironment
22685
22931
  cacheResolvedCli(command, resolved);
22686
22932
  return resolved ?? void 0;
22687
22933
  }
22934
+ function invalidateResolvedCli(command) {
22935
+ const trimmed = command.trim();
22936
+ if (trimmed) {
22937
+ resolvedCliCache.delete(trimmed);
22938
+ negativeResolutionExpiry.delete(trimmed);
22939
+ }
22940
+ }
22688
22941
  function revalidateBackendRuntimeCommand(backendKind, env = getDaemonCliEnvironment()) {
22689
22942
  if (!backendKind) return void 0;
22690
22943
  const command = BACKEND_CLI_COMMANDS[backendKind];
22691
22944
  if (!command) return void 0;
22692
- resolvedCliCache.delete(command);
22945
+ invalidateResolvedCli(command);
22693
22946
  const spec = Object.values(PROVIDER_CLI_COMMANDS).find((entry) => entry.command === command);
22694
22947
  const envOverride = spec ? spec.envVars.map((name) => env[name]?.trim()).find(Boolean) : void 0;
22695
- if (envOverride) resolvedCliCache.delete(envOverride);
22948
+ if (envOverride) invalidateResolvedCli(envOverride);
22696
22949
  return resolveBackendRuntimeCommand(backendKind, env);
22697
22950
  }
22698
22951
 
22952
+ // src/cli-ensure-install.ts
22953
+ var CLI_RUNTIME_VERSIONS_ENV = "ALAN_CLI_RUNTIME_VERSIONS";
22954
+ var INSTALLABLE_PROVIDER_PACKAGES = {
22955
+ claude_cli: { npmPackage: "@anthropic-ai/claude-code", command: "claude", label: "Claude Code" },
22956
+ codex_app_server: { npmPackage: "@openai/codex", command: "codex", label: "Codex" },
22957
+ copilot_cli: { npmPackage: "@github/copilot", command: "copilot", label: "Copilot" },
22958
+ opencode_cli: { npmPackage: "opencode-ai", command: "opencode", label: "opencode" }
22959
+ };
22960
+ var INSTALL_TIMEOUT_MS = 12e4;
22961
+ var installOutcomes = /* @__PURE__ */ new Map();
22962
+ function canInstallProviderCli(backendKind) {
22963
+ return Boolean(backendKind && INSTALLABLE_PROVIDER_PACKAGES[backendKind]);
22964
+ }
22965
+ function pinnedVersion(npmPackage) {
22966
+ const raw = process.env[CLI_RUNTIME_VERSIONS_ENV];
22967
+ if (!raw) return "latest";
22968
+ try {
22969
+ const parsed = JSON.parse(raw);
22970
+ const version = parsed[npmPackage];
22971
+ if (typeof version === "string" && version.trim()) return version.trim();
22972
+ } catch {
22973
+ }
22974
+ return "latest";
22975
+ }
22976
+ function runNpmInstall(installSpec, env, timeoutMs) {
22977
+ return new Promise((resolve6) => {
22978
+ const isWin = (0, import_node_os4.platform)() === "win32";
22979
+ let child;
22980
+ try {
22981
+ child = (0, import_node_child_process3.spawn)("npm", ["install", "-g", installSpec], {
22982
+ env,
22983
+ shell: isWin,
22984
+ windowsHide: isWin ? true : void 0,
22985
+ timeout: timeoutMs,
22986
+ stdio: ["ignore", "pipe", "pipe"]
22987
+ });
22988
+ } catch (error) {
22989
+ resolve6({ status: null, stdout: "", stderr: "", error });
22990
+ return;
22991
+ }
22992
+ let stdout = "";
22993
+ let stderr = "";
22994
+ child.stdout?.setEncoding("utf8").on("data", (chunk) => {
22995
+ stdout += chunk;
22996
+ });
22997
+ child.stderr?.setEncoding("utf8").on("data", (chunk) => {
22998
+ stderr += chunk;
22999
+ });
23000
+ child.once("error", (error) => resolve6({ status: null, stdout, stderr, error }));
23001
+ child.once(
23002
+ "close",
23003
+ (status, signal) => resolve6({
23004
+ status,
23005
+ stdout,
23006
+ stderr,
23007
+ // Spawn `timeout` kills by signal without an `error` event; surface it
23008
+ // so a timed-out install is reported as a failure, not status null + ok.
23009
+ ...signal ? { error: new Error(`npm install killed by ${signal}`) } : {}
23010
+ })
23011
+ );
23012
+ });
23013
+ }
23014
+ async function attemptInstall(spec, options) {
23015
+ const installSpec = `${spec.npmPackage}@${pinnedVersion(spec.npmPackage)}`;
23016
+ const notify = options?.notify ?? ((message) => console.info(`[cli-ensure-install] ${message}`));
23017
+ notify(`Installing ${spec.label} CLI (${installSpec})\u2026`);
23018
+ const runner = options?.runner ?? runNpmInstall;
23019
+ const result = await runner(installSpec, getDaemonCliEnvironment(), INSTALL_TIMEOUT_MS);
23020
+ if (result.error || result.status !== 0) {
23021
+ console.warn(`[cli-ensure-install] npm install -g ${installSpec} failed`, {
23022
+ status: result.status,
23023
+ error: result.error?.message,
23024
+ output: `${result.stderr || result.stdout}`.trim().slice(-500)
23025
+ });
23026
+ notify(`Failed to install ${spec.label} CLI (${installSpec}).`);
23027
+ return false;
23028
+ }
23029
+ invalidateResolvedCli(spec.command);
23030
+ notify(`Installed ${spec.label} CLI (${installSpec}).`);
23031
+ return true;
23032
+ }
23033
+ function ensureProviderCliInstalled(backendKind, options) {
23034
+ if (!backendKind) return Promise.resolve(false);
23035
+ const spec = INSTALLABLE_PROVIDER_PACKAGES[backendKind];
23036
+ if (!spec) return Promise.resolve(false);
23037
+ const existing = installOutcomes.get(spec.npmPackage);
23038
+ if (existing) return existing;
23039
+ const attempt = attemptInstall(spec, options).catch((error) => {
23040
+ console.warn(`[cli-ensure-install] install attempt for ${spec.npmPackage} threw`, error);
23041
+ return false;
23042
+ });
23043
+ installOutcomes.set(spec.npmPackage, attempt);
23044
+ return attempt;
23045
+ }
23046
+
22699
23047
  // src/computer-readiness.ts
22700
23048
  var import_node_fs5 = require("fs");
22701
23049
 
22702
23050
  // src/runtime-config.ts
22703
23051
  var import_node_fs4 = require("fs");
22704
- var import_node_os4 = require("os");
23052
+ var import_node_os5 = require("os");
22705
23053
  var import_node_path4 = require("path");
22706
- var DEFAULT_AGENT_DIR = (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".alan", "agent");
22707
- var LEGACY_AGENT_DIR = (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".aiden", "agent");
23054
+ var DEFAULT_AGENT_DIR = (0, import_node_path4.join)((0, import_node_os5.homedir)(), ".alan", "agent");
23055
+ var LEGACY_AGENT_DIR = (0, import_node_path4.join)((0, import_node_os5.homedir)(), ".aiden", "agent");
22708
23056
  var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Map([
22709
23057
  ["apiUrl", "string"],
22710
23058
  ["wsUrl", "string"],
@@ -23012,7 +23360,7 @@ function buildComputerReadiness(input) {
23012
23360
  }
23013
23361
 
23014
23362
  // src/core-agent.ts
23015
- var import_node_os5 = require("os");
23363
+ var import_node_os6 = require("os");
23016
23364
  var CoreAgent = class _CoreAgent extends BaseMachineAgent {
23017
23365
  childProcess = null;
23018
23366
  _aborted = false;
@@ -23064,7 +23412,18 @@ var CoreAgent = class _CoreAgent extends BaseMachineAgent {
23064
23412
  * and re-resolving once lets the current run recover against the new location.
23065
23413
  */
23066
23414
  revalidateRuntimeCommand() {
23067
- return revalidateBackendRuntimeCommand(this.runtime.backendKind) ?? null;
23415
+ const resolved = revalidateBackendRuntimeCommand(this.runtime.backendKind) ?? null;
23416
+ if (!resolved && this.runtime.backendKind) {
23417
+ const backendKind = this.runtime.backendKind;
23418
+ void ensureProviderCliInstalled(backendKind).then((installed) => {
23419
+ if (installed) {
23420
+ console.info("[CoreAgent] Installed missing provider CLI after spawn ENOENT", {
23421
+ backendKind
23422
+ });
23423
+ }
23424
+ });
23425
+ }
23426
+ return resolved;
23068
23427
  }
23069
23428
  // ── Interactive tool response (WS relay → stdin) ───────────────────────
23070
23429
  /**
@@ -23151,7 +23510,7 @@ var CoreAgent = class _CoreAgent extends BaseMachineAgent {
23151
23510
  if (this.runtime.backendKind === "grok_cli" && this.providerApiKey) {
23152
23511
  augmented.XAI_API_KEY = this.providerApiKey;
23153
23512
  }
23154
- if ((0, import_node_os5.platform)() !== "win32") {
23513
+ if ((0, import_node_os6.platform)() !== "win32") {
23155
23514
  const home = augmented.HOME ?? "/home/user";
23156
23515
  const extraPaths = [`${home}/.local/node/bin`, `${home}/.local/bin`];
23157
23516
  const separator = ":";
@@ -23706,11 +24065,97 @@ var EncryptedEventOutbox = class _EncryptedEventOutbox {
23706
24065
  }
23707
24066
  };
23708
24067
 
23709
- // src/mcp-registration.ts
23710
- var import_node_crypto2 = require("crypto");
24068
+ // src/local-skill-scan.ts
23711
24069
  var import_node_fs7 = require("fs");
23712
- var import_node_os6 = require("os");
23713
24070
  var import_node_path6 = require("path");
24071
+ var SKILL_READ_LIMIT_BYTES = 8192;
24072
+ var SKILL_CACHE_TTL_MS = 3e4;
24073
+ function readSkillHead(path) {
24074
+ const fd = (0, import_node_fs7.openSync)(path, "r");
24075
+ try {
24076
+ const buffer = Buffer.alloc(SKILL_READ_LIMIT_BYTES);
24077
+ const bytesRead = (0, import_node_fs7.readSync)(fd, buffer, 0, SKILL_READ_LIMIT_BYTES, 0);
24078
+ return buffer.subarray(0, bytesRead).toString("utf-8");
24079
+ } finally {
24080
+ (0, import_node_fs7.closeSync)(fd);
24081
+ }
24082
+ }
24083
+ function scanSkillDir(dirPath, source, repoRoot, entries, errors) {
24084
+ if (!(0, import_node_fs7.existsSync)(dirPath)) return;
24085
+ let names;
24086
+ try {
24087
+ names = (0, import_node_fs7.readdirSync)(dirPath);
24088
+ } catch (error) {
24089
+ errors.push(`Failed to scan ${dirPath}: ${error instanceof Error ? error.message : error}`);
24090
+ return;
24091
+ }
24092
+ for (const name of names) {
24093
+ const skillPath = (0, import_node_path6.join)(dirPath, name, "SKILL.md");
24094
+ let isDirectory = false;
24095
+ try {
24096
+ isDirectory = (0, import_node_fs7.statSync)((0, import_node_path6.join)(dirPath, name)).isDirectory();
24097
+ } catch {
24098
+ continue;
24099
+ }
24100
+ if (!isDirectory || !(0, import_node_fs7.existsSync)(skillPath)) continue;
24101
+ try {
24102
+ entries.push({ content: readSkillHead(skillPath), source, sourcePath: skillPath, repoRoot });
24103
+ } catch (error) {
24104
+ errors.push(`Failed to read ${skillPath}: ${error instanceof Error ? error.message : error}`);
24105
+ }
24106
+ }
24107
+ }
24108
+ function discoverChildRepos(parentPath) {
24109
+ let names;
24110
+ try {
24111
+ names = (0, import_node_fs7.readdirSync)(parentPath);
24112
+ } catch {
24113
+ return [];
24114
+ }
24115
+ const children = [];
24116
+ for (const name of names) {
24117
+ if (name.startsWith(".")) continue;
24118
+ const childPath = (0, import_node_path6.join)(parentPath, name);
24119
+ try {
24120
+ if ((0, import_node_fs7.statSync)(childPath).isDirectory()) children.push(childPath);
24121
+ } catch {
24122
+ }
24123
+ }
24124
+ return children;
24125
+ }
24126
+ function scanLocalSkills(homeDir, projectPath) {
24127
+ const rawEntries = [];
24128
+ const errors = [];
24129
+ for (const root of SKILL_DIRECTORY_ROOTS) {
24130
+ scanSkillDir((0, import_node_path6.join)(homeDir, root), "global", void 0, rawEntries, errors);
24131
+ }
24132
+ if (projectPath) {
24133
+ for (const dir of [projectPath, ...discoverChildRepos(projectPath)]) {
24134
+ for (const root of SKILL_DIRECTORY_ROOTS) {
24135
+ scanSkillDir((0, import_node_path6.join)(dir, root), "team", dir, rawEntries, errors);
24136
+ }
24137
+ }
24138
+ }
24139
+ return { skills: normalizeSkillEntries(rawEntries, "team"), errors };
24140
+ }
24141
+ var skillScanCache = /* @__PURE__ */ new Map();
24142
+ function scanLocalSkillsCached(homeDir, projectPath, options) {
24143
+ const key = projectPath ?? "";
24144
+ const now = Date.now();
24145
+ if (!options?.refresh) {
24146
+ const cached = skillScanCache.get(key);
24147
+ if (cached && cached.expiresAt > now) return cached.result;
24148
+ }
24149
+ const result = scanLocalSkills(homeDir, projectPath);
24150
+ skillScanCache.set(key, { expiresAt: now + SKILL_CACHE_TTL_MS, result });
24151
+ return result;
24152
+ }
24153
+
24154
+ // src/mcp-registration.ts
24155
+ var import_node_crypto2 = require("crypto");
24156
+ var import_node_fs8 = require("fs");
24157
+ var import_node_os7 = require("os");
24158
+ var import_node_path7 = require("path");
23714
24159
 
23715
24160
  // ../../node_modules/smol-toml/dist/error.js
23716
24161
  function getLineColFromPtr(string, ptr) {
@@ -24507,18 +24952,18 @@ var inspectAlanMcpThroughProviderCli = async (backendKind, expectedUrl, home) =>
24507
24952
  return result;
24508
24953
  };
24509
24954
  async function waitForConfigLock(lockPath) {
24510
- (0, import_node_fs7.mkdirSync)((0, import_node_path6.dirname)(lockPath), { recursive: true });
24955
+ (0, import_node_fs8.mkdirSync)((0, import_node_path7.dirname)(lockPath), { recursive: true });
24511
24956
  const startedAt = Date.now();
24512
24957
  while (true) {
24513
24958
  try {
24514
- (0, import_node_fs7.mkdirSync)(lockPath);
24515
- return () => (0, import_node_fs7.rmSync)(lockPath, { recursive: true, force: true });
24959
+ (0, import_node_fs8.mkdirSync)(lockPath);
24960
+ return () => (0, import_node_fs8.rmSync)(lockPath, { recursive: true, force: true });
24516
24961
  } catch (error) {
24517
24962
  const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
24518
24963
  if (code !== "EEXIST") throw error;
24519
24964
  try {
24520
- if (Date.now() - (0, import_node_fs7.statSync)(lockPath).mtimeMs > CONFIG_LOCK_STALE_MS) {
24521
- (0, import_node_fs7.rmSync)(lockPath, { recursive: true, force: true });
24965
+ if (Date.now() - (0, import_node_fs8.statSync)(lockPath).mtimeMs > CONFIG_LOCK_STALE_MS) {
24966
+ (0, import_node_fs8.rmSync)(lockPath, { recursive: true, force: true });
24522
24967
  continue;
24523
24968
  }
24524
24969
  } catch {
@@ -24534,7 +24979,7 @@ async function waitForConfigLock(lockPath) {
24534
24979
  function saveMalformedBackup(path, content) {
24535
24980
  const hash = (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 12);
24536
24981
  const backupPath = `${path}.alan-backup-${hash}`;
24537
- if (!(0, import_node_fs7.existsSync)(backupPath)) (0, import_node_fs7.copyFileSync)(path, backupPath);
24982
+ if (!(0, import_node_fs8.existsSync)(backupPath)) (0, import_node_fs8.copyFileSync)(path, backupPath);
24538
24983
  return backupPath;
24539
24984
  }
24540
24985
  function parseJsonObject2(path, content) {
@@ -24581,22 +25026,22 @@ function mergeCodexAlanSection(path, existingContent, desiredContent) {
24581
25026
  function atomicWriteManagedConfig(path, content) {
24582
25027
  const pendingPath = `${path}.alan-pending-${process.pid}-${(0, import_node_crypto2.randomBytes)(6).toString("hex")}`;
24583
25028
  try {
24584
- (0, import_node_fs7.writeFileSync)(pendingPath, content, { encoding: "utf8", mode: 384, flag: "wx" });
24585
- (0, import_node_fs7.renameSync)(pendingPath, path);
25029
+ (0, import_node_fs8.writeFileSync)(pendingPath, content, { encoding: "utf8", mode: 384, flag: "wx" });
25030
+ (0, import_node_fs8.renameSync)(pendingPath, path);
24586
25031
  } finally {
24587
- (0, import_node_fs7.rmSync)(pendingPath, { force: true });
25032
+ (0, import_node_fs8.rmSync)(pendingPath, { force: true });
24588
25033
  }
24589
25034
  }
24590
- async function ensureAlanMcpRegistered(backendKind, registration, home = (0, import_node_os6.homedir)(), inspectCli = inspectAlanMcpThroughProviderCli) {
25035
+ async function ensureAlanMcpRegistered(backendKind, registration, home = (0, import_node_os7.homedir)(), inspectCli = inspectAlanMcpThroughProviderCli) {
24591
25036
  try {
24592
25037
  const mcpServers = buildAlanMcpServers(registration);
24593
25038
  const files = alanMcpConfigFilesForBackend(backendKind, mcpServers, home);
24594
25039
  const paths = [];
24595
25040
  for (const file of files) {
24596
- (0, import_node_fs7.mkdirSync)((0, import_node_path6.dirname)(file.path), { recursive: true });
25041
+ (0, import_node_fs8.mkdirSync)((0, import_node_path7.dirname)(file.path), { recursive: true });
24597
25042
  const releaseLock = await waitForConfigLock(`${file.path}.alan-lock`);
24598
25043
  try {
24599
- const current = (0, import_node_fs7.existsSync)(file.path) ? (0, import_node_fs7.readFileSync)(file.path, "utf8") : null;
25044
+ const current = (0, import_node_fs8.existsSync)(file.path) ? (0, import_node_fs8.readFileSync)(file.path, "utf8") : null;
24600
25045
  const content = file.path.endsWith(".toml") ? mergeCodexAlanSection(file.path, current, file.content) : mergeJsonConfig(file.path, current, file.content);
24601
25046
  if (current !== content) atomicWriteManagedConfig(file.path, content);
24602
25047
  paths.push(file.path);
@@ -24647,13 +25092,13 @@ async function ensureAlanMcpRegistered(backendKind, registration, home = (0, imp
24647
25092
  return { ok: false, paths: [], error: err instanceof Error ? err.message : String(err) };
24648
25093
  }
24649
25094
  }
24650
- async function verifyAlanMcpRegistered(backendKind, home = (0, import_node_os6.homedir)(), inspectCli = inspectAlanMcpThroughProviderCli) {
25095
+ async function verifyAlanMcpRegistered(backendKind, home = (0, import_node_os7.homedir)(), inspectCli = inspectAlanMcpThroughProviderCli) {
24651
25096
  const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
24652
- const missing = files.map((f) => f.path).filter((p) => !(0, import_node_fs7.existsSync)(p));
25097
+ const missing = files.map((f) => f.path).filter((p) => !(0, import_node_fs8.existsSync)(p));
24653
25098
  const presentEntries = files.map((file) => ({
24654
25099
  path: file.path,
24655
- url: (0, import_node_fs7.existsSync)(file.path) ? readUsableAlanMcpUrl(file.path) : null
24656
- })).filter((entry) => (0, import_node_fs7.existsSync)(entry.path));
25100
+ url: (0, import_node_fs8.existsSync)(file.path) ? readUsableAlanMcpUrl(file.path) : null
25101
+ })).filter((entry) => (0, import_node_fs8.existsSync)(entry.path));
24657
25102
  let invalid = presentEntries.filter((entry) => !entry.url).map((entry) => entry.path);
24658
25103
  const urls = new Set(presentEntries.flatMap((entry) => entry.url ? [entry.url] : []));
24659
25104
  if (urls.size > 1) invalid = presentEntries.map((entry) => entry.path);
@@ -24709,15 +25154,15 @@ function removeAlanFromJsonConfig(path, existingContent) {
24709
25154
  }
24710
25155
  return JSON.stringify(config);
24711
25156
  }
24712
- async function unregisterAlanMcp(backendKind, home = (0, import_node_os6.homedir)()) {
25157
+ async function unregisterAlanMcp(backendKind, home = (0, import_node_os7.homedir)()) {
24713
25158
  const removedPaths = [];
24714
25159
  const errors = [];
24715
25160
  const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
24716
25161
  for (const file of files) {
24717
- if (!(0, import_node_fs7.existsSync)(file.path)) continue;
25162
+ if (!(0, import_node_fs8.existsSync)(file.path)) continue;
24718
25163
  const releaseLock = await waitForConfigLock(`${file.path}.alan-lock`);
24719
25164
  try {
24720
- const current = (0, import_node_fs7.readFileSync)(file.path, "utf8");
25165
+ const current = (0, import_node_fs8.readFileSync)(file.path, "utf8");
24721
25166
  let next;
24722
25167
  if (file.path.endsWith(".toml")) {
24723
25168
  const stripped = stripCodexAlanMcpSection(current);
@@ -24748,7 +25193,7 @@ function isHttpUrl(value2) {
24748
25193
  }
24749
25194
  }
24750
25195
  function readUsableAlanMcpUrl(path) {
24751
- const content = (0, import_node_fs7.readFileSync)(path, "utf8");
25196
+ const content = (0, import_node_fs8.readFileSync)(path, "utf8");
24752
25197
  if (path.endsWith(".toml")) {
24753
25198
  const lines = content.split("\n");
24754
25199
  const start = lines.findIndex((line) => line.trim() === "[mcp_servers.alan]");
@@ -25039,7 +25484,7 @@ ${result.stderr}`.trim();
25039
25484
  }
25040
25485
 
25041
25486
  // src/provider-model-discovery.ts
25042
- var import_node_fs8 = require("fs");
25487
+ var import_node_fs9 = require("fs");
25043
25488
  var MODEL_DISCOVERY_TIMEOUT_MS = 8e3;
25044
25489
  var ANTIGRAVITY_MODEL_DISCOVERY_TIMEOUT_MS = 12e3;
25045
25490
  var OPENCODE_ZEN_MODEL_IDS = OPENCODE_ZEN_MODELS.map((model) => model.id);
@@ -25253,7 +25698,7 @@ function filterClaudeDatedModelIds(ids) {
25253
25698
  }
25254
25699
  function resolveExecutableForModelScan(executable) {
25255
25700
  try {
25256
- return (0, import_node_fs8.realpathSync)(executable);
25701
+ return (0, import_node_fs9.realpathSync)(executable);
25257
25702
  } catch {
25258
25703
  return executable;
25259
25704
  }
@@ -25261,7 +25706,7 @@ function resolveExecutableForModelScan(executable) {
25261
25706
  function extractClaudeModelIdsFromExecutable(executable) {
25262
25707
  try {
25263
25708
  const resolved = resolveExecutableForModelScan(executable);
25264
- const text = (0, import_node_fs8.readFileSync)(resolved).toString("latin1");
25709
+ const text = (0, import_node_fs9.readFileSync)(resolved).toString("latin1");
25265
25710
  const matches = text.match(CLAUDE_MODEL_ID_PATTERN) ?? [];
25266
25711
  const unique = [
25267
25712
  ...new Set(matches.map((id) => id.trim()).filter((id) => isValidClaudeModelId(id))),
@@ -25361,7 +25806,7 @@ async function discoverGenericModels(executable, env) {
25361
25806
  ["models"]
25362
25807
  ]);
25363
25808
  }
25364
- var CATALOG_ONLY_PROVIDERS = /* @__PURE__ */ new Set(["supatest_cli", "droid_cli"]);
25809
+ var CATALOG_ONLY_PROVIDERS = /* @__PURE__ */ new Set(["supatest_cli", "droid_cli", "copilot_cli"]);
25365
25810
  function supportsProviderModelDiscovery(provider) {
25366
25811
  return provider in CATALOG_MODEL_IDS_BY_PROVIDER;
25367
25812
  }
@@ -25380,7 +25825,7 @@ async function discoverProviderModels(provider, executable, env) {
25380
25825
  probed = await discoverCodexModels(executable, env);
25381
25826
  } else if (provider === "claude_cli") {
25382
25827
  probed = await discoverClaudeModels(executable, env);
25383
- } else if (provider === "kimi_cli" || provider === "grok_cli" || provider === "copilot_cli" || provider === "droid_cli") {
25828
+ } else if (provider === "kimi_cli" || provider === "grok_cli") {
25384
25829
  probed = await discoverGenericModels(executable, env);
25385
25830
  }
25386
25831
  if (probed.length > 0) return probed;
@@ -25388,8 +25833,8 @@ async function discoverProviderModels(provider, executable, env) {
25388
25833
  }
25389
25834
 
25390
25835
  // src/run-journal.ts
25391
- var import_node_fs9 = require("fs");
25392
- var import_node_path7 = require("path");
25836
+ var import_node_fs10 = require("fs");
25837
+ var import_node_path8 = require("path");
25393
25838
  var JOURNAL_VERSION = 1;
25394
25839
  var INTERRUPTED_ENTRY_TTL_MS = 24 * 60 * 6e4;
25395
25840
  function isEntry(value2) {
@@ -25408,9 +25853,9 @@ function isEntry(value2) {
25408
25853
  var RuntimeRunJournal = class {
25409
25854
  constructor(path, nowMs = Date.now()) {
25410
25855
  this.path = path;
25411
- if (!(0, import_node_fs9.existsSync)(path)) return;
25856
+ if (!(0, import_node_fs10.existsSync)(path)) return;
25412
25857
  try {
25413
- const parsed = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
25858
+ const parsed = JSON.parse((0, import_node_fs10.readFileSync)(path, "utf8"));
25414
25859
  if (parsed.version !== JOURNAL_VERSION || !Array.isArray(parsed.entries)) {
25415
25860
  throw new Error("unsupported run journal format");
25416
25861
  }
@@ -25533,18 +25978,18 @@ var RuntimeRunJournal = class {
25533
25978
  }
25534
25979
  persist() {
25535
25980
  const pendingPath = `${this.path}.pending`;
25536
- (0, import_node_fs9.mkdirSync)((0, import_node_path7.dirname)(this.path), { recursive: true });
25981
+ (0, import_node_fs10.mkdirSync)((0, import_node_path8.dirname)(this.path), { recursive: true });
25537
25982
  try {
25538
- (0, import_node_fs9.writeFileSync)(
25983
+ (0, import_node_fs10.writeFileSync)(
25539
25984
  pendingPath,
25540
25985
  `${JSON.stringify({ version: JOURNAL_VERSION, entries: [...this.entries.values()] })}
25541
25986
  `,
25542
25987
  { mode: 384 }
25543
25988
  );
25544
- (0, import_node_fs9.chmodSync)(pendingPath, 384);
25545
- (0, import_node_fs9.renameSync)(pendingPath, this.path);
25989
+ (0, import_node_fs10.chmodSync)(pendingPath, 384);
25990
+ (0, import_node_fs10.renameSync)(pendingPath, this.path);
25546
25991
  } catch (error) {
25547
- (0, import_node_fs9.rmSync)(pendingPath, { force: true });
25992
+ (0, import_node_fs10.rmSync)(pendingPath, { force: true });
25548
25993
  throw error;
25549
25994
  }
25550
25995
  }
@@ -25571,17 +26016,17 @@ var RunStartGate = class {
25571
26016
  };
25572
26017
 
25573
26018
  // src/version.ts
25574
- var AGENT_VERSION = "0.1.48";
26019
+ var AGENT_VERSION = "0.1.49";
25575
26020
 
25576
26021
  // src/workspace-relocation.ts
25577
- var import_node_child_process3 = require("child_process");
25578
- var import_node_fs10 = require("fs");
25579
- var import_node_path8 = require("path");
26022
+ var import_node_child_process4 = require("child_process");
26023
+ var import_node_fs11 = require("fs");
26024
+ var import_node_path9 = require("path");
25580
26025
  var MAX_RELOCATION_CANDIDATES = 200;
25581
26026
  var DEFAULT_PROBE = {
25582
- childDirectories: (parentPath, limit) => (0, import_node_fs10.readdirSync)(parentPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).slice(0, limit).map((entry) => (0, import_node_path8.join)(parentPath, entry.name)),
26027
+ childDirectories: (parentPath, limit) => (0, import_node_fs11.readdirSync)(parentPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).slice(0, limit).map((entry) => (0, import_node_path9.join)(parentPath, entry.name)),
25583
26028
  gitRemoteUrl: (candidatePath) => {
25584
- const result = (0, import_node_child_process3.spawnSync)("git", ["-C", candidatePath, "config", "--get", "remote.origin.url"], {
26029
+ const result = (0, import_node_child_process4.spawnSync)("git", ["-C", candidatePath, "config", "--get", "remote.origin.url"], {
25585
26030
  encoding: "utf8",
25586
26031
  env: getDaemonCliEnvironment(),
25587
26032
  timeout: 2e3
@@ -25610,7 +26055,7 @@ function workspaceMatchesExpectedRepositories(input) {
25610
26055
  input.expectedRepoUrls.map(normalizeRepositoryIdentity).filter((identity2) => Boolean(identity2))
25611
26056
  );
25612
26057
  if (identities.size === 0) return false;
25613
- const remote = (input.probe ?? DEFAULT_PROBE).gitRemoteUrl((0, import_node_path8.resolve)(input.workspacePath));
26058
+ const remote = (input.probe ?? DEFAULT_PROBE).gitRemoteUrl((0, import_node_path9.resolve)(input.workspacePath));
25614
26059
  const identity = remote ? normalizeRepositoryIdentity(remote) : null;
25615
26060
  return Boolean(identity && identities.has(identity));
25616
26061
  }
@@ -25620,8 +26065,8 @@ function discoverRelocatedWorkspace(input) {
25620
26065
  );
25621
26066
  if (identities.size === 0) return null;
25622
26067
  const searchRoots = [
25623
- (0, import_node_path8.dirname)((0, import_node_path8.resolve)(input.missingPath)),
25624
- ...(input.approvedWorkspacePaths ?? []).map((path) => (0, import_node_path8.dirname)((0, import_node_path8.resolve)(path)))
26068
+ (0, import_node_path9.dirname)((0, import_node_path9.resolve)(input.missingPath)),
26069
+ ...(input.approvedWorkspacePaths ?? []).map((path) => (0, import_node_path9.dirname)((0, import_node_path9.resolve)(path)))
25625
26070
  ].filter((path, index, paths) => paths.indexOf(path) === index);
25626
26071
  const candidates = [];
25627
26072
  for (const root of searchRoots) {
@@ -25633,19 +26078,19 @@ function discoverRelocatedWorkspace(input) {
25633
26078
  }
25634
26079
  }
25635
26080
  const matches = [];
25636
- for (const candidate of new Set(candidates.map((path) => (0, import_node_path8.resolve)(path)))) {
26081
+ for (const candidate of new Set(candidates.map((path) => (0, import_node_path9.resolve)(path)))) {
25637
26082
  const remote = (input.probe ?? DEFAULT_PROBE).gitRemoteUrl(candidate);
25638
26083
  const identity = remote ? normalizeRepositoryIdentity(remote) : null;
25639
- if (identity && identities.has(identity)) matches.push((0, import_node_path8.resolve)(candidate));
26084
+ if (identity && identities.has(identity)) matches.push((0, import_node_path9.resolve)(candidate));
25640
26085
  if (matches.length > 1) return null;
25641
26086
  }
25642
26087
  return matches[0] ?? null;
25643
26088
  }
25644
26089
 
25645
26090
  // src/workspace-run-lease.ts
25646
- var import_node_child_process4 = require("child_process");
25647
- var import_node_fs11 = require("fs");
25648
- var import_node_path9 = require("path");
26091
+ var import_node_child_process5 = require("child_process");
26092
+ var import_node_fs12 = require("fs");
26093
+ var import_node_path10 = require("path");
25649
26094
  var WORKSPACE_LEASE_CHECK_INTERVAL_MS = 1e3;
25650
26095
  function parsePositiveMs(value2, fallback) {
25651
26096
  const parsed = value2 ? Number(value2) : Number.NaN;
@@ -25656,7 +26101,7 @@ var WORKSPACE_LEASE_GIT_TIMEOUT_MS = parsePositiveMs(
25656
26101
  2e3
25657
26102
  );
25658
26103
  function gitValue(cwd, args) {
25659
- const result = (0, import_node_child_process4.spawnSync)("git", ["-C", cwd, ...args], {
26104
+ const result = (0, import_node_child_process5.spawnSync)("git", ["-C", cwd, ...args], {
25660
26105
  encoding: "utf8",
25661
26106
  env: getDaemonCliEnvironment(),
25662
26107
  timeout: WORKSPACE_LEASE_GIT_TIMEOUT_MS
@@ -25665,17 +26110,17 @@ function gitValue(cwd, args) {
25665
26110
  return result.stdout.trim() || null;
25666
26111
  }
25667
26112
  function filesystemIdentity(path) {
25668
- const canonicalPath = import_node_fs11.realpathSync.native(path);
25669
- const stat = (0, import_node_fs11.statSync)(canonicalPath);
26113
+ const canonicalPath = import_node_fs12.realpathSync.native(path);
26114
+ const stat = (0, import_node_fs12.statSync)(canonicalPath);
25670
26115
  return `${canonicalPath}:${stat.dev}:${stat.ino}`;
25671
26116
  }
25672
26117
  function resolveGitPath(workspacePath, value2) {
25673
- return (0, import_node_path9.isAbsolute)(value2) ? value2 : (0, import_node_path9.resolve)(workspacePath, value2);
26118
+ return (0, import_node_path10.isAbsolute)(value2) ? value2 : (0, import_node_path10.resolve)(workspacePath, value2);
25674
26119
  }
25675
26120
  var DEFAULT_PROBE2 = {
25676
26121
  snapshot: (workspacePath) => {
25677
26122
  try {
25678
- const canonicalWorkspacePath = import_node_fs11.realpathSync.native((0, import_node_path9.resolve)(workspacePath));
26123
+ const canonicalWorkspacePath = import_node_fs12.realpathSync.native((0, import_node_path10.resolve)(workspacePath));
25679
26124
  const repositoryRoot = gitValue(canonicalWorkspacePath, ["rev-parse", "--show-toplevel"]);
25680
26125
  const gitDirectory = gitValue(canonicalWorkspacePath, ["rev-parse", "--git-dir"]);
25681
26126
  const commonDirectory = gitValue(canonicalWorkspacePath, ["rev-parse", "--git-common-dir"]);
@@ -25684,7 +26129,7 @@ var DEFAULT_PROBE2 = {
25684
26129
  const remote = gitValue(canonicalWorkspacePath, ["config", "--get", "remote.origin.url"]);
25685
26130
  const remoteIdentity = remote ? normalizeRepositoryIdentity(remote) : null;
25686
26131
  return {
25687
- workspacePath: (0, import_node_path9.resolve)(workspacePath),
26132
+ workspacePath: (0, import_node_path10.resolve)(workspacePath),
25688
26133
  workspaceIdentity: filesystemIdentity(canonicalWorkspacePath),
25689
26134
  repositoryIdentity: `${filesystemIdentity(
25690
26135
  resolveGitPath(canonicalWorkspacePath, commonDirectory)
@@ -25698,7 +26143,7 @@ var DEFAULT_PROBE2 = {
25698
26143
  },
25699
26144
  pathExists: (workspacePath) => {
25700
26145
  try {
25701
- return (0, import_node_fs11.existsSync)((0, import_node_path9.resolve)(workspacePath));
26146
+ return (0, import_node_fs12.existsSync)((0, import_node_path10.resolve)(workspacePath));
25702
26147
  } catch {
25703
26148
  return true;
25704
26149
  }
@@ -25729,7 +26174,7 @@ var WorkspaceRunLease = class _WorkspaceRunLease {
25729
26174
  static capture(targets, probe = DEFAULT_PROBE2) {
25730
26175
  const captured = [];
25731
26176
  for (const target of targets) {
25732
- const normalizedTarget = { ...target, workspacePath: (0, import_node_path9.resolve)(target.workspacePath) };
26177
+ const normalizedTarget = { ...target, workspacePath: (0, import_node_path10.resolve)(target.workspacePath) };
25733
26178
  const identity = probe.snapshot(normalizedTarget.workspacePath);
25734
26179
  if (!identity) return { lease: null, failure: unavailableFailure(normalizedTarget) };
25735
26180
  if (normalizedTarget.expectedBranch && identity.branch !== normalizedTarget.expectedBranch) {
@@ -25882,15 +26327,15 @@ function assertValidGitRef(ref, label) {
25882
26327
  if (!ref || ref.startsWith("-") || ref.includes("\0")) {
25883
26328
  throw new Error(`${label} is not a safe git ref`);
25884
26329
  }
25885
- const result = (0, import_node_child_process5.spawnSync)("git", ["check-ref-format", "--branch", ref], {
26330
+ const result = (0, import_node_child_process6.spawnSync)("git", ["check-ref-format", "--branch", ref], {
25886
26331
  env: getDaemonCliEnvironment(),
25887
26332
  encoding: "utf8"
25888
26333
  });
25889
26334
  if (result.status !== 0) throw new Error(`${label} is not a valid git ref`);
25890
26335
  }
25891
26336
  function isPathContained(childPath, parentPath) {
25892
- const rel = (0, import_node_path10.relative)((0, import_node_path10.resolve)(parentPath), (0, import_node_path10.resolve)(childPath));
25893
- return rel === "" || !(0, import_node_path10.isAbsolute)(rel) && rel !== ".." && !rel.startsWith(`..${import_node_path10.sep}`);
26337
+ const rel = (0, import_node_path11.relative)((0, import_node_path11.resolve)(parentPath), (0, import_node_path11.resolve)(childPath));
26338
+ return rel === "" || !(0, import_node_path11.isAbsolute)(rel) && rel !== ".." && !rel.startsWith(`..${import_node_path11.sep}`);
25894
26339
  }
25895
26340
  function assertPathContained(childPath, parentPath, label) {
25896
26341
  if (!isPathContained(childPath, parentPath)) {
@@ -25898,7 +26343,7 @@ function assertPathContained(childPath, parentPath, label) {
25898
26343
  }
25899
26344
  }
25900
26345
  function git(cwd, args) {
25901
- const result = (0, import_node_child_process5.spawnSync)("git", args, {
26346
+ const result = (0, import_node_child_process6.spawnSync)("git", args, {
25902
26347
  cwd,
25903
26348
  env: getDaemonCliEnvironment(),
25904
26349
  encoding: "utf8",
@@ -25917,14 +26362,14 @@ function gitSafe(cwd, args) {
25917
26362
  }
25918
26363
  }
25919
26364
  function worktreeDir(projectPath) {
25920
- const resolved = (0, import_node_path10.resolve)(projectPath);
25921
- return (0, import_node_path10.join)((0, import_node_path10.dirname)(resolved), `${(0, import_node_path10.basename)(resolved)}-alan-worktrees`);
26365
+ const resolved = (0, import_node_path11.resolve)(projectPath);
26366
+ return (0, import_node_path11.join)((0, import_node_path11.dirname)(resolved), `${(0, import_node_path11.basename)(resolved)}-alan-worktrees`);
25922
26367
  }
25923
26368
  function pathSegment(value2) {
25924
26369
  return value2.toLowerCase().replace(/^alan\//, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "task";
25925
26370
  }
25926
26371
  function repoName(repoUrl, localPath) {
25927
- return repoUrl.replace(/\.git$/, "").split("/").pop() || (0, import_node_path10.basename)(localPath) || "repo";
26372
+ return repoUrl.replace(/\.git$/, "").split("/").pop() || (0, import_node_path11.basename)(localPath) || "repo";
25928
26373
  }
25929
26374
  function findWorktreeByBranch(repoPath, branchName) {
25930
26375
  const output = gitSafe(repoPath, ["worktree", "list", "--porcelain"]);
@@ -25940,21 +26385,21 @@ function findWorktreeByBranch(repoPath, branchName) {
25940
26385
  function ensureGitWorktree(input) {
25941
26386
  assertValidGitRef(input.branchName, "branchName");
25942
26387
  assertValidGitRef(input.baseBranch, "baseBranch");
25943
- const repoPath = (0, import_node_path10.resolve)(input.repoPath);
26388
+ const repoPath = (0, import_node_path11.resolve)(input.repoPath);
25944
26389
  const root = input.allowedRoot ?? worktreeDir(repoPath);
25945
26390
  const existingPath = findWorktreeByBranch(input.repoPath, input.branchName);
25946
26391
  if (existingPath) {
25947
- if ((0, import_node_path10.resolve)(existingPath) === repoPath) {
26392
+ if ((0, import_node_path11.resolve)(existingPath) === repoPath) {
25948
26393
  throw new Error(`${input.branchName} is checked out in the shared checkout`);
25949
26394
  }
25950
26395
  return existingPath;
25951
26396
  }
25952
26397
  assertPathContained(input.worktreePath, root, "worktreePath");
25953
- (0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(input.worktreePath), { recursive: true });
26398
+ (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(input.worktreePath), { recursive: true });
25954
26399
  if (!gitSafe(input.repoPath, ["branch", "--list", input.branchName])) {
25955
26400
  git(input.repoPath, ["branch", input.branchName, input.baseBranch]);
25956
26401
  }
25957
- if ((0, import_node_fs12.existsSync)(input.worktreePath)) {
26402
+ if ((0, import_node_fs13.existsSync)(input.worktreePath)) {
25958
26403
  if (gitSafe(input.worktreePath, ["branch", "--show-current"]) === input.branchName) {
25959
26404
  return input.worktreePath;
25960
26405
  }
@@ -25964,15 +26409,15 @@ function ensureGitWorktree(input) {
25964
26409
  return input.worktreePath;
25965
26410
  }
25966
26411
  function resolveProjectWorktreePaths(projectPath, gitRoot, gitWorktreePath) {
25967
- const resolvedProjectPath = (0, import_node_path10.resolve)(projectPath);
25968
- const resolvedGitRoot = (0, import_node_path10.resolve)(gitRoot);
25969
- const projectRelativePath = (0, import_node_path10.relative)(resolvedGitRoot, resolvedProjectPath);
25970
- if (projectRelativePath === ".." || projectRelativePath.startsWith(`..${import_node_path10.sep}`) || (0, import_node_path10.isAbsolute)(projectRelativePath)) {
26412
+ const resolvedProjectPath = (0, import_node_path11.resolve)(projectPath);
26413
+ const resolvedGitRoot = (0, import_node_path11.resolve)(gitRoot);
26414
+ const projectRelativePath = (0, import_node_path11.relative)(resolvedGitRoot, resolvedProjectPath);
26415
+ if (projectRelativePath === ".." || projectRelativePath.startsWith(`..${import_node_path11.sep}`) || (0, import_node_path11.isAbsolute)(projectRelativePath)) {
25971
26416
  throw new Error("The selected Project folder is outside its Git repository");
25972
26417
  }
25973
26418
  return {
25974
26419
  repoPath: resolvedGitRoot,
25975
- projectPath: projectRelativePath ? (0, import_node_path10.join)((0, import_node_path10.resolve)(gitWorktreePath), projectRelativePath) : (0, import_node_path10.resolve)(gitWorktreePath)
26420
+ projectPath: projectRelativePath ? (0, import_node_path11.join)((0, import_node_path11.resolve)(gitWorktreePath), projectRelativePath) : (0, import_node_path11.resolve)(gitWorktreePath)
25976
26421
  };
25977
26422
  }
25978
26423
  function ensureWorktree(params) {
@@ -25984,7 +26429,7 @@ function ensureWorktree(params) {
25984
26429
  repoLocalPaths: Array.isArray(params.repoLocalPaths) ? params.repoLocalPaths : []
25985
26430
  };
25986
26431
  if (input.repoLocalPaths?.length) {
25987
- const taskRoot2 = (0, import_node_path10.join)(
26432
+ const taskRoot2 = (0, import_node_path11.join)(
25988
26433
  worktreeDir(input.projectPath),
25989
26434
  `${pathSegment(input.branchName)}-${input.taskId.slice(0, 8)}`
25990
26435
  );
@@ -25993,7 +26438,7 @@ function ensureWorktree(params) {
25993
26438
  const baseBranch2 = repo.branch || input.baseBranch || "main";
25994
26439
  const worktreePath2 = ensureGitWorktree({
25995
26440
  repoPath: repo.localPath,
25996
- worktreePath: (0, import_node_path10.join)(taskRoot2, pathSegment(repoName(repo.repoUrl, repo.localPath))),
26441
+ worktreePath: (0, import_node_path11.join)(taskRoot2, pathSegment(repoName(repo.repoUrl, repo.localPath))),
25997
26442
  branchName: input.branchName,
25998
26443
  baseBranch: baseBranch2,
25999
26444
  allowedRoot: worktreeDir(input.projectPath)
@@ -26008,13 +26453,13 @@ function ensureWorktree(params) {
26008
26453
  })
26009
26454
  };
26010
26455
  }
26011
- const gitRoot = gitSafe((0, import_node_path10.resolve)(input.projectPath), ["rev-parse", "--show-toplevel"]);
26456
+ const gitRoot = gitSafe((0, import_node_path11.resolve)(input.projectPath), ["rev-parse", "--show-toplevel"]);
26012
26457
  if (!gitRoot) {
26013
26458
  throw new Error("The selected Project folder is not inside a Git repository");
26014
26459
  }
26015
- const repoPath = (0, import_node_path10.resolve)(gitRoot);
26460
+ const repoPath = (0, import_node_path11.resolve)(gitRoot);
26016
26461
  const baseBranch = input.baseBranch || gitSafe(repoPath, ["branch", "--show-current"]) || "HEAD";
26017
- const taskRoot = (0, import_node_path10.join)(
26462
+ const taskRoot = (0, import_node_path11.join)(
26018
26463
  worktreeDir(repoPath),
26019
26464
  `${pathSegment(input.branchName)}-${input.taskId.slice(0, 8)}`
26020
26465
  );
@@ -26043,7 +26488,7 @@ function ensureWorktree(params) {
26043
26488
  function ensureSharedCheckoutBranch(input) {
26044
26489
  assertValidGitRef(input.branchName, "branchName");
26045
26490
  assertValidGitRef(input.baseBranch, "baseBranch");
26046
- const repoPath = (0, import_node_path10.resolve)(input.repoPath);
26491
+ const repoPath = (0, import_node_path11.resolve)(input.repoPath);
26047
26492
  if (gitSafe(repoPath, ["rev-parse", "--is-inside-work-tree"]) !== "true") {
26048
26493
  return { checkedOut: false, reason: `${repoPath} is not a git work tree` };
26049
26494
  }
@@ -26088,7 +26533,7 @@ function resolveSharedCheckoutTargets(input) {
26088
26533
  }
26089
26534
  function resolveRuntimeCwd(payload) {
26090
26535
  const multiRepoWorktree = payload.worktreeInfo?.allWorktrees?.[0]?.worktreePath;
26091
- if (multiRepoWorktree) return (0, import_node_path10.dirname)(multiRepoWorktree);
26536
+ if (multiRepoWorktree) return (0, import_node_path11.dirname)(multiRepoWorktree);
26092
26537
  if (isAlanDefaultWorkspace(payload.projectPath)) return resolveAlanDefaultWorkspacePath();
26093
26538
  return payload.worktreeInfo?.worktreePath ?? payload.projectPath ?? process.cwd();
26094
26539
  }
@@ -26310,12 +26755,12 @@ function getConfigPath() {
26310
26755
  return process.env.ALAN_AGENT_CONFIG_PATH ?? boundAgentConfigPath ?? resolveAgentConfigPath({ profile: "production" });
26311
26756
  }
26312
26757
  function getEndpointDefaultsPath() {
26313
- return process.env.ALAN_AGENT_ENDPOINTS_PATH ?? (0, import_node_path10.join)((0, import_node_path10.dirname)(getConfigPath()), "endpoints.json");
26758
+ return process.env.ALAN_AGENT_ENDPOINTS_PATH ?? (0, import_node_path11.join)((0, import_node_path11.dirname)(getConfigPath()), "endpoints.json");
26314
26759
  }
26315
26760
  function readConfig() {
26316
26761
  const configPath = getConfigPath();
26317
- if (!(0, import_node_fs12.existsSync)(configPath)) return {};
26318
- const parsed = JSON.parse((0, import_node_fs12.readFileSync)(configPath, "utf8"));
26762
+ if (!(0, import_node_fs13.existsSync)(configPath)) return {};
26763
+ const parsed = JSON.parse((0, import_node_fs13.readFileSync)(configPath, "utf8"));
26319
26764
  const sanitized = sanitizeAgentConfig(parsed);
26320
26765
  if (Object.keys(parsed).some((key) => !Object.hasOwn(sanitized, key))) {
26321
26766
  writeConfig(sanitized);
@@ -26331,27 +26776,27 @@ function readConfigForRegistration() {
26331
26776
  }
26332
26777
  function readEndpointDefaults() {
26333
26778
  const endpointsPath = getEndpointDefaultsPath();
26334
- if (!(0, import_node_fs12.existsSync)(endpointsPath)) return {};
26779
+ if (!(0, import_node_fs13.existsSync)(endpointsPath)) return {};
26335
26780
  try {
26336
- return JSON.parse((0, import_node_fs12.readFileSync)(endpointsPath, "utf8"));
26781
+ return JSON.parse((0, import_node_fs13.readFileSync)(endpointsPath, "utf8"));
26337
26782
  } catch {
26338
26783
  return {};
26339
26784
  }
26340
26785
  }
26341
26786
  function writeConfig(config) {
26342
26787
  const configPath = getConfigPath();
26343
- (0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(configPath), { recursive: true, mode: 448 });
26788
+ (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(configPath), { recursive: true, mode: 448 });
26344
26789
  const pendingPath = `${configPath}.pending-${process.pid}-${(0, import_node_crypto3.randomBytes)(6).toString("hex")}`;
26345
26790
  try {
26346
- (0, import_node_fs12.writeFileSync)(
26791
+ (0, import_node_fs13.writeFileSync)(
26347
26792
  pendingPath,
26348
26793
  JSON.stringify(sanitizeAgentConfig(config), null, 2),
26349
26794
  { mode: 384 }
26350
26795
  );
26351
- (0, import_node_fs12.chmodSync)(pendingPath, 384);
26352
- (0, import_node_fs12.renameSync)(pendingPath, configPath);
26796
+ (0, import_node_fs13.chmodSync)(pendingPath, 384);
26797
+ (0, import_node_fs13.renameSync)(pendingPath, configPath);
26353
26798
  } finally {
26354
- (0, import_node_fs12.rmSync)(pendingPath, { force: true });
26799
+ (0, import_node_fs13.rmSync)(pendingPath, { force: true });
26355
26800
  }
26356
26801
  }
26357
26802
  function maintenanceLeasePath(configPath) {
@@ -26359,9 +26804,9 @@ function maintenanceLeasePath(configPath) {
26359
26804
  }
26360
26805
  function readLocalDaemonMaintenanceLease(configPath, nowMs = Date.now()) {
26361
26806
  const path = maintenanceLeasePath(configPath);
26362
- if (!(0, import_node_fs12.existsSync)(path)) return null;
26807
+ if (!(0, import_node_fs13.existsSync)(path)) return null;
26363
26808
  try {
26364
- const lease = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
26809
+ const lease = JSON.parse((0, import_node_fs13.readFileSync)(path, "utf8"));
26365
26810
  if (lease.version !== 1 || typeof lease.id !== "string" || lease.reason !== "credential_cleanup" && lease.reason !== "daemon_startup" && lease.reason !== "watchdog" || !(lease.reason === "credential_cleanup" && lease.expiresAtMs === null || typeof lease.expiresAtMs === "number" && Number.isFinite(lease.expiresAtMs))) {
26366
26811
  throw new Error("invalid maintenance lease");
26367
26812
  }
@@ -26378,8 +26823,8 @@ function readLocalDaemonMaintenanceLease(configPath, nowMs = Date.now()) {
26378
26823
  expiresAtMs: nowMs + 5e3
26379
26824
  };
26380
26825
  try {
26381
- (0, import_node_fs12.writeFileSync)(path, JSON.stringify(fallback), { mode: 384 });
26382
- (0, import_node_fs12.chmodSync)(path, 384);
26826
+ (0, import_node_fs13.writeFileSync)(path, JSON.stringify(fallback), { mode: 384 });
26827
+ (0, import_node_fs13.chmodSync)(path, 384);
26383
26828
  } catch {
26384
26829
  }
26385
26830
  return fallback;
@@ -26395,10 +26840,10 @@ function acquireLocalDaemonMaintenanceLease(configPath, reason, ttlMs = 5e3) {
26395
26840
  expiresAtMs: reason === "credential_cleanup" ? null : Date.now() + Math.max(1e3, Math.min(ttlMs, 6e4))
26396
26841
  };
26397
26842
  const path = maintenanceLeasePath(configPath);
26398
- (0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(path), { recursive: true, mode: 448 });
26843
+ (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(path), { recursive: true, mode: 448 });
26399
26844
  try {
26400
- (0, import_node_fs12.writeFileSync)(path, JSON.stringify(lease), { mode: 384, flag: "wx" });
26401
- (0, import_node_fs12.chmodSync)(path, 384);
26845
+ (0, import_node_fs13.writeFileSync)(path, JSON.stringify(lease), { mode: 384, flag: "wx" });
26846
+ (0, import_node_fs13.chmodSync)(path, 384);
26402
26847
  return { acquired: true, lease };
26403
26848
  } catch (error) {
26404
26849
  const raced = readLocalDaemonMaintenanceLease(configPath);
@@ -26414,9 +26859,9 @@ function daemonOwnerLeasePath(configPath) {
26414
26859
  }
26415
26860
  function readLocalDaemonOwnerLease(configPath) {
26416
26861
  const path = daemonOwnerLeasePath(configPath);
26417
- if (!(0, import_node_fs12.existsSync)(path)) return null;
26862
+ if (!(0, import_node_fs13.existsSync)(path)) return null;
26418
26863
  try {
26419
- const lease = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
26864
+ const lease = JSON.parse((0, import_node_fs13.readFileSync)(path, "utf8"));
26420
26865
  if (lease.version !== 1 || typeof lease.id !== "string" || typeof lease.daemonSessionId !== "string" || !Number.isSafeInteger(lease.pid) || lease.pid <= 0 || typeof lease.startedAtMs !== "number" || !Number.isFinite(lease.startedAtMs) || typeof lease.runtimeId !== "string" || typeof lease.localServiceId !== "string" || !Number.isSafeInteger(lease.localApiPort) || lease.localApiPort < 0 || lease.localApiPort > 65535 || typeof lease.localApiToken !== "string" || lease.localApiToken.length === 0) {
26421
26866
  throw new Error("invalid daemon owner lease");
26422
26867
  }
@@ -26468,10 +26913,10 @@ async function acquireLocalDaemonOwnerLease(input) {
26468
26913
  localApiToken: input.localApiToken
26469
26914
  };
26470
26915
  const path = daemonOwnerLeasePath(input.configPath);
26471
- (0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(path), { recursive: true, mode: 448 });
26916
+ (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(path), { recursive: true, mode: 448 });
26472
26917
  try {
26473
- (0, import_node_fs12.writeFileSync)(path, JSON.stringify(lease), { mode: 384, flag: "wx" });
26474
- (0, import_node_fs12.chmodSync)(path, 384);
26918
+ (0, import_node_fs13.writeFileSync)(path, JSON.stringify(lease), { mode: 384, flag: "wx" });
26919
+ (0, import_node_fs13.chmodSync)(path, 384);
26475
26920
  return { acquired: true, lease };
26476
26921
  } catch (error) {
26477
26922
  const raced = readLocalDaemonOwnerLease(input.configPath);
@@ -26486,12 +26931,12 @@ function updateLocalDaemonOwnerEndpoint(configPath, lease, localApiPort) {
26486
26931
  const path = daemonOwnerLeasePath(configPath);
26487
26932
  const pendingPath = `${path}.pending-${lease.id}`;
26488
26933
  try {
26489
- (0, import_node_fs12.writeFileSync)(pendingPath, JSON.stringify(updated), { mode: 384 });
26490
- (0, import_node_fs12.chmodSync)(pendingPath, 384);
26491
- (0, import_node_fs12.renameSync)(pendingPath, path);
26934
+ (0, import_node_fs13.writeFileSync)(pendingPath, JSON.stringify(updated), { mode: 384 });
26935
+ (0, import_node_fs13.chmodSync)(pendingPath, 384);
26936
+ (0, import_node_fs13.renameSync)(pendingPath, path);
26492
26937
  return updated;
26493
26938
  } finally {
26494
- (0, import_node_fs12.rmSync)(pendingPath, { force: true });
26939
+ (0, import_node_fs13.rmSync)(pendingPath, { force: true });
26495
26940
  }
26496
26941
  }
26497
26942
  function releaseLocalDaemonOwnerLease(configPath, leaseId) {
@@ -26500,13 +26945,13 @@ function releaseLocalDaemonOwnerLease(configPath, leaseId) {
26500
26945
  function releaseOwnedJsonFile(path, ownerId, ownerField = "id") {
26501
26946
  const releasePath = `${path}.release-${process.pid}-${(0, import_node_crypto3.randomBytes)(6).toString("hex")}`;
26502
26947
  try {
26503
- (0, import_node_fs12.renameSync)(path, releasePath);
26948
+ (0, import_node_fs13.renameSync)(path, releasePath);
26504
26949
  } catch (error) {
26505
26950
  if (error.code === "ENOENT") return;
26506
26951
  throw error;
26507
26952
  }
26508
26953
  try {
26509
- const content = (0, import_node_fs12.readFileSync)(releasePath, "utf8");
26954
+ const content = (0, import_node_fs13.readFileSync)(releasePath, "utf8");
26510
26955
  let capturedOwnerId = null;
26511
26956
  try {
26512
26957
  const captured = JSON.parse(content);
@@ -26515,13 +26960,13 @@ function releaseOwnedJsonFile(path, ownerId, ownerField = "id") {
26515
26960
  }
26516
26961
  if (capturedOwnerId === ownerId) return;
26517
26962
  try {
26518
- (0, import_node_fs12.writeFileSync)(path, content, { mode: 384, flag: "wx" });
26519
- (0, import_node_fs12.chmodSync)(path, 384);
26963
+ (0, import_node_fs13.writeFileSync)(path, content, { mode: 384, flag: "wx" });
26964
+ (0, import_node_fs13.chmodSync)(path, 384);
26520
26965
  } catch (error) {
26521
26966
  if (error.code !== "EEXIST") throw error;
26522
26967
  }
26523
26968
  } finally {
26524
- (0, import_node_fs12.rmSync)(releasePath, { force: true });
26969
+ (0, import_node_fs13.rmSync)(releasePath, { force: true });
26525
26970
  }
26526
26971
  }
26527
26972
  function inspectLocalDaemonRunJournal(configPath) {
@@ -26531,8 +26976,8 @@ function inspectLocalDaemonRunJournal(configPath) {
26531
26976
  try {
26532
26977
  let activeRunCount = 0;
26533
26978
  for (const artifact of [path, pendingPath]) {
26534
- if (!(0, import_node_fs12.existsSync)(artifact)) continue;
26535
- const parsed = JSON.parse((0, import_node_fs12.readFileSync)(artifact, "utf8"));
26979
+ if (!(0, import_node_fs13.existsSync)(artifact)) continue;
26980
+ const parsed = JSON.parse((0, import_node_fs13.readFileSync)(artifact, "utf8"));
26536
26981
  if (parsed.version !== 1 || !Array.isArray(parsed.entries))
26537
26982
  throw new Error("invalid journal");
26538
26983
  if (artifact === path) {
@@ -26551,7 +26996,7 @@ function inspectLocalDaemonRunJournal(configPath) {
26551
26996
  }).length;
26552
26997
  continue;
26553
26998
  }
26554
- const legacyStartedAtMs = (0, import_node_fs12.statSync)(pendingPath).mtimeMs;
26999
+ const legacyStartedAtMs = (0, import_node_fs13.statSync)(pendingPath).mtimeMs;
26555
27000
  activeRunCount += parsed.entries.filter((entry) => {
26556
27001
  if (!entry || typeof entry !== "object") throw new Error("invalid pending run entry");
26557
27002
  const startedAtMs = entry.startedAtMs;
@@ -26578,9 +27023,9 @@ function setupTokenHash(setupToken) {
26578
27023
  function getOrCreateSetupAttemptId(setupToken) {
26579
27024
  const intentPath = pendingSetupIntentPath();
26580
27025
  const tokenHash = setupTokenHash(setupToken);
26581
- if ((0, import_node_fs12.existsSync)(intentPath)) {
27026
+ if ((0, import_node_fs13.existsSync)(intentPath)) {
26582
27027
  try {
26583
- const stored = JSON.parse((0, import_node_fs12.readFileSync)(intentPath, "utf8"));
27028
+ const stored = JSON.parse((0, import_node_fs13.readFileSync)(intentPath, "utf8"));
26584
27029
  if (stored.setupTokenHash === tokenHash && typeof stored.registrationAttemptId === "string" && stored.registrationAttemptId.length > 0) {
26585
27030
  return stored.registrationAttemptId;
26586
27031
  }
@@ -26588,23 +27033,23 @@ function getOrCreateSetupAttemptId(setupToken) {
26588
27033
  }
26589
27034
  }
26590
27035
  const registrationAttemptId = (0, import_node_crypto3.randomUUID)();
26591
- (0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(intentPath), { recursive: true, mode: 448 });
27036
+ (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(intentPath), { recursive: true, mode: 448 });
26592
27037
  const writePath = `${intentPath}.pending-${process.pid}-${(0, import_node_crypto3.randomBytes)(6).toString("hex")}`;
26593
27038
  try {
26594
- (0, import_node_fs12.writeFileSync)(
27039
+ (0, import_node_fs13.writeFileSync)(
26595
27040
  writePath,
26596
27041
  JSON.stringify({ setupTokenHash: tokenHash, registrationAttemptId }, null, 2),
26597
27042
  { mode: 384 }
26598
27043
  );
26599
- (0, import_node_fs12.chmodSync)(writePath, 384);
26600
- (0, import_node_fs12.renameSync)(writePath, intentPath);
27044
+ (0, import_node_fs13.chmodSync)(writePath, 384);
27045
+ (0, import_node_fs13.renameSync)(writePath, intentPath);
26601
27046
  } finally {
26602
- (0, import_node_fs12.rmSync)(writePath, { force: true });
27047
+ (0, import_node_fs13.rmSync)(writePath, { force: true });
26603
27048
  }
26604
27049
  return registrationAttemptId;
26605
27050
  }
26606
27051
  function clearPendingSetupIntent() {
26607
- (0, import_node_fs12.rmSync)(pendingSetupIntentPath(), { force: true });
27052
+ (0, import_node_fs13.rmSync)(pendingSetupIntentPath(), { force: true });
26608
27053
  }
26609
27054
  function pendingDeviceAuthorizationPath() {
26610
27055
  return `${getConfigPath()}.device-auth-pending`;
@@ -26625,8 +27070,8 @@ function parseDeviceAuthorizationOperationContent(content) {
26625
27070
  }
26626
27071
  function restoreCapturedFileUnlessReplaced(path, content) {
26627
27072
  try {
26628
- (0, import_node_fs12.writeFileSync)(path, content, { mode: 384, flag: "wx" });
26629
- (0, import_node_fs12.chmodSync)(path, 384);
27073
+ (0, import_node_fs13.writeFileSync)(path, content, { mode: 384, flag: "wx" });
27074
+ (0, import_node_fs13.chmodSync)(path, 384);
26630
27075
  } catch (error) {
26631
27076
  if (error.code !== "EEXIST") throw error;
26632
27077
  }
@@ -26634,26 +27079,26 @@ function restoreCapturedFileUnlessReplaced(path, content) {
26634
27079
  function quarantineCorruptDeviceAuthorizationOperation(path) {
26635
27080
  const quarantinePath = `${path}.corrupt-${process.pid}-${(0, import_node_crypto3.randomBytes)(6).toString("hex")}`;
26636
27081
  try {
26637
- (0, import_node_fs12.renameSync)(path, quarantinePath);
27082
+ (0, import_node_fs13.renameSync)(path, quarantinePath);
26638
27083
  } catch (error) {
26639
27084
  if (error.code === "ENOENT") return null;
26640
27085
  throw error;
26641
27086
  }
26642
27087
  try {
26643
- const capturedContent = (0, import_node_fs12.readFileSync)(quarantinePath, "utf8");
27088
+ const capturedContent = (0, import_node_fs13.readFileSync)(quarantinePath, "utf8");
26644
27089
  if (parseDeviceAuthorizationOperationContent(capturedContent)) {
26645
27090
  restoreCapturedFileUnlessReplaced(path, capturedContent);
26646
27091
  }
26647
27092
  } finally {
26648
- (0, import_node_fs12.rmSync)(quarantinePath, { force: true });
27093
+ (0, import_node_fs13.rmSync)(quarantinePath, { force: true });
26649
27094
  }
26650
- if (!(0, import_node_fs12.existsSync)(path)) return null;
26651
- return parseDeviceAuthorizationOperationContent((0, import_node_fs12.readFileSync)(path, "utf8"));
27095
+ if (!(0, import_node_fs13.existsSync)(path)) return null;
27096
+ return parseDeviceAuthorizationOperationContent((0, import_node_fs13.readFileSync)(path, "utf8"));
26652
27097
  }
26653
27098
  function readDeviceAuthorizationOperation() {
26654
27099
  const path = deviceAuthorizationOperationPath();
26655
- if (!(0, import_node_fs12.existsSync)(path)) return null;
26656
- const parsed = parseDeviceAuthorizationOperationContent((0, import_node_fs12.readFileSync)(path, "utf8"));
27100
+ if (!(0, import_node_fs13.existsSync)(path)) return null;
27101
+ const parsed = parseDeviceAuthorizationOperationContent((0, import_node_fs13.readFileSync)(path, "utf8"));
26657
27102
  const lease = parsed ?? quarantineCorruptDeviceAuthorizationOperation(path);
26658
27103
  if (!lease) return null;
26659
27104
  if (lease.expiresAtMs <= Date.now()) {
@@ -26671,10 +27116,10 @@ function acquireDeviceAuthorizationOperation() {
26671
27116
  expiresAtMs: Date.now() + 6e4
26672
27117
  };
26673
27118
  const path = deviceAuthorizationOperationPath();
26674
- (0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(path), { recursive: true, mode: 448 });
27119
+ (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(path), { recursive: true, mode: 448 });
26675
27120
  try {
26676
- (0, import_node_fs12.writeFileSync)(path, JSON.stringify(lease), { mode: 384, flag: "wx" });
26677
- (0, import_node_fs12.chmodSync)(path, 384);
27121
+ (0, import_node_fs13.writeFileSync)(path, JSON.stringify(lease), { mode: 384, flag: "wx" });
27122
+ (0, import_node_fs13.chmodSync)(path, 384);
26678
27123
  return lease;
26679
27124
  } catch (error) {
26680
27125
  if (error.code === "EEXIST") return null;
@@ -26689,15 +27134,15 @@ function extendDeviceAuthorizationOperation(operationId, expiresAtMs) {
26689
27134
  const path = deviceAuthorizationOperationPath();
26690
27135
  const pendingPath = `${path}.pending-${process.pid}-${(0, import_node_crypto3.randomBytes)(6).toString("hex")}`;
26691
27136
  try {
26692
- (0, import_node_fs12.writeFileSync)(
27137
+ (0, import_node_fs13.writeFileSync)(
26693
27138
  pendingPath,
26694
27139
  JSON.stringify({ ...current, expiresAtMs: Math.max(expiresAtMs, Date.now() + 6e4) }),
26695
27140
  { mode: 384 }
26696
27141
  );
26697
- (0, import_node_fs12.chmodSync)(pendingPath, 384);
26698
- (0, import_node_fs12.renameSync)(pendingPath, path);
27142
+ (0, import_node_fs13.chmodSync)(pendingPath, 384);
27143
+ (0, import_node_fs13.renameSync)(pendingPath, path);
26699
27144
  } finally {
26700
- (0, import_node_fs12.rmSync)(pendingPath, { force: true });
27145
+ (0, import_node_fs13.rmSync)(pendingPath, { force: true });
26701
27146
  }
26702
27147
  }
26703
27148
  function assertDeviceAuthorizationOperationOwner(operationId) {
@@ -26708,12 +27153,12 @@ function assertDeviceAuthorizationOperationOwner(operationId) {
26708
27153
  function clearPendingDeviceAuthorization(operationId) {
26709
27154
  const path = pendingDeviceAuthorizationPath();
26710
27155
  if (!operationId) {
26711
- (0, import_node_fs12.rmSync)(path, { force: true });
27156
+ (0, import_node_fs13.rmSync)(path, { force: true });
26712
27157
  return;
26713
27158
  }
26714
- if (!(0, import_node_fs12.existsSync)(path)) return;
27159
+ if (!(0, import_node_fs13.existsSync)(path)) return;
26715
27160
  try {
26716
- const pending = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
27161
+ const pending = JSON.parse((0, import_node_fs13.readFileSync)(path, "utf8"));
26717
27162
  if (pending.operationId !== operationId) return;
26718
27163
  } catch {
26719
27164
  return;
@@ -26722,9 +27167,9 @@ function clearPendingDeviceAuthorization(operationId) {
26722
27167
  }
26723
27168
  function readPendingDeviceAuthorization(apiUrl) {
26724
27169
  const path = pendingDeviceAuthorizationPath();
26725
- if (!(0, import_node_fs12.existsSync)(path)) return null;
27170
+ if (!(0, import_node_fs13.existsSync)(path)) return null;
26726
27171
  try {
26727
- const pending = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
27172
+ const pending = JSON.parse((0, import_node_fs13.readFileSync)(path, "utf8"));
26728
27173
  const expiresAtMs = Date.parse(pending.expiresAt ?? "");
26729
27174
  if (pending.apiUrl !== apiUrl || typeof pending.deviceCode !== "string" || typeof pending.userCode !== "string" || typeof pending.verificationUri !== "string" || typeof pending.installationId !== "string" || pending.operationId !== void 0 && typeof pending.operationId !== "string" || !Number.isFinite(pending.intervalSeconds) || !Number.isFinite(expiresAtMs) || expiresAtMs <= Date.now()) {
26730
27175
  return null;
@@ -26736,20 +27181,20 @@ function readPendingDeviceAuthorization(apiUrl) {
26736
27181
  }
26737
27182
  function writePendingDeviceAuthorization(pending) {
26738
27183
  const path = pendingDeviceAuthorizationPath();
26739
- (0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(path), { recursive: true, mode: 448 });
27184
+ (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(path), { recursive: true, mode: 448 });
26740
27185
  const writePath = `${path}.pending-${process.pid}-${(0, import_node_crypto3.randomBytes)(6).toString("hex")}`;
26741
27186
  try {
26742
- (0, import_node_fs12.writeFileSync)(writePath, JSON.stringify(pending, null, 2), { mode: 384 });
26743
- (0, import_node_fs12.chmodSync)(writePath, 384);
26744
- (0, import_node_fs12.renameSync)(writePath, path);
27187
+ (0, import_node_fs13.writeFileSync)(writePath, JSON.stringify(pending, null, 2), { mode: 384 });
27188
+ (0, import_node_fs13.chmodSync)(writePath, 384);
27189
+ (0, import_node_fs13.renameSync)(writePath, path);
26745
27190
  } finally {
26746
- (0, import_node_fs12.rmSync)(writePath, { force: true });
27191
+ (0, import_node_fs13.rmSync)(writePath, { force: true });
26747
27192
  }
26748
27193
  }
26749
27194
  function removeAlanOwnedLocalArtifacts() {
26750
27195
  const configPath = getConfigPath();
26751
- const directory = (0, import_node_path10.dirname)(configPath);
26752
- const configName = (0, import_node_path10.basename)(configPath).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27196
+ const directory = (0, import_node_path11.dirname)(configPath);
27197
+ const configName = (0, import_node_path11.basename)(configPath).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26753
27198
  const alanAtomicSidecarPattern = new RegExp(
26754
27199
  `^${configName}(?:|\\.setup-pending|\\.device-auth-pending|\\.device-auth-operation\\.json|\\.maintenance-lease\\.json|\\.event-outbox\\.enc)\\.(?:pending|release|corrupt)-\\d+-[a-f0-9]{12}$`
26755
27200
  );
@@ -26766,13 +27211,13 @@ function removeAlanOwnedLocalArtifacts() {
26766
27211
  `${configPath}.pending-runs.json.pending`,
26767
27212
  maintenanceLeasePath(configPath)
26768
27213
  ]);
26769
- if ((0, import_node_fs12.existsSync)(directory)) {
26770
- for (const entry of (0, import_node_fs12.readdirSync)(directory)) {
26771
- if (alanAtomicSidecarPattern.test(entry)) artifacts.add((0, import_node_path10.join)(directory, entry));
27214
+ if ((0, import_node_fs13.existsSync)(directory)) {
27215
+ for (const entry of (0, import_node_fs13.readdirSync)(directory)) {
27216
+ if (alanAtomicSidecarPattern.test(entry)) artifacts.add((0, import_node_path11.join)(directory, entry));
26772
27217
  }
26773
27218
  }
26774
- for (const artifact of artifacts) (0, import_node_fs12.rmSync)(artifact, { force: true });
26775
- (0, import_node_fs12.rmSync)(configPath, { force: true });
27219
+ for (const artifact of artifacts) (0, import_node_fs13.rmSync)(artifact, { force: true });
27220
+ (0, import_node_fs13.rmSync)(configPath, { force: true });
26776
27221
  }
26777
27222
  function argValue(args, name) {
26778
27223
  const index = args.indexOf(name);
@@ -26994,13 +27439,13 @@ var DurablePendingRunStarts = class extends Map {
26994
27439
  }
26995
27440
  persist() {
26996
27441
  if (this.size === 0) {
26997
- (0, import_node_fs12.rmSync)(this.path, { force: true });
27442
+ (0, import_node_fs13.rmSync)(this.path, { force: true });
26998
27443
  return;
26999
27444
  }
27000
27445
  const pendingPath = `${this.path}.pending`;
27001
- (0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(this.path), { recursive: true, mode: 448 });
27446
+ (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(this.path), { recursive: true, mode: 448 });
27002
27447
  try {
27003
- (0, import_node_fs12.writeFileSync)(
27448
+ (0, import_node_fs13.writeFileSync)(
27004
27449
  pendingPath,
27005
27450
  JSON.stringify({
27006
27451
  version: 1,
@@ -27013,10 +27458,10 @@ var DurablePendingRunStarts = class extends Map {
27013
27458
  }),
27014
27459
  { mode: 384 }
27015
27460
  );
27016
- (0, import_node_fs12.chmodSync)(pendingPath, 384);
27017
- (0, import_node_fs12.renameSync)(pendingPath, this.path);
27461
+ (0, import_node_fs13.chmodSync)(pendingPath, 384);
27462
+ (0, import_node_fs13.renameSync)(pendingPath, this.path);
27018
27463
  } finally {
27019
- (0, import_node_fs12.rmSync)(pendingPath, { force: true });
27464
+ (0, import_node_fs13.rmSync)(pendingPath, { force: true });
27020
27465
  }
27021
27466
  }
27022
27467
  };
@@ -27251,21 +27696,21 @@ function reconcileActiveRuns(input) {
27251
27696
  }
27252
27697
  }
27253
27698
  function collectRuntimeMetadata() {
27254
- const cpuList = (0, import_node_os7.cpus)();
27699
+ const cpuList = (0, import_node_os8.cpus)();
27255
27700
  const metadata = {
27256
- platform: (0, import_node_os7.platform)(),
27257
- arch: (0, import_node_os7.arch)(),
27258
- osVersion: (0, import_node_os7.release)(),
27701
+ platform: (0, import_node_os8.platform)(),
27702
+ arch: (0, import_node_os8.arch)(),
27703
+ osVersion: (0, import_node_os8.release)(),
27259
27704
  agentVersion: AGENT_VERSION,
27260
- memoryBytes: (0, import_node_os7.totalmem)(),
27261
- memoryFreeBytes: (0, import_node_os7.freemem)()
27705
+ memoryBytes: (0, import_node_os8.totalmem)(),
27706
+ memoryFreeBytes: (0, import_node_os8.freemem)()
27262
27707
  };
27263
27708
  if (cpuList.length > 0) {
27264
27709
  metadata.cpuModel = cpuList[0]?.model;
27265
27710
  metadata.cpuCores = cpuList.length;
27266
27711
  }
27267
27712
  try {
27268
- const disk = (0, import_node_fs12.statfsSync)((0, import_node_os7.homedir)());
27713
+ const disk = (0, import_node_fs13.statfsSync)((0, import_node_os8.homedir)());
27269
27714
  metadata.diskFreeBytes = disk.bavail * disk.bsize;
27270
27715
  metadata.diskTotalBytes = disk.blocks * disk.bsize;
27271
27716
  } catch {
@@ -27521,7 +27966,7 @@ async function setupDaemon(args) {
27521
27966
  const teamId = argValue(args, "--team") ?? process.env.ALAN_TEAM_ID;
27522
27967
  const setupToken = argValue(args, "--setup-token") ?? process.env.ALAN_RUNTIME_SETUP_TOKEN;
27523
27968
  const token = argValue(args, "--token") ?? process.env.ALAN_SETUP_TOKEN;
27524
- const displayName = argValue(args, "--name") ?? (0, import_node_os7.hostname)();
27969
+ const displayName = argValue(args, "--name") ?? (0, import_node_os8.hostname)();
27525
27970
  const scope = argValue(args, "--scope") ?? process.env.ALAN_RUNTIME_SCOPE ?? (setupToken ? "team" : "user");
27526
27971
  const installationId = argValue(args, "--installation-id") ?? previousConfig.installationId ?? (0, import_node_crypto3.randomUUID)();
27527
27972
  if (scope !== "team" && scope !== "user") {
@@ -27544,7 +27989,7 @@ async function setupDaemon(args) {
27544
27989
  ...teamId ? { teamId } : {},
27545
27990
  ...setupToken ? { setupToken, registrationAttemptId } : {},
27546
27991
  displayName,
27547
- hostname: (0, import_node_os7.hostname)(),
27992
+ hostname: (0, import_node_os8.hostname)(),
27548
27993
  runtimeKind: "machine",
27549
27994
  managementKind: "user_managed",
27550
27995
  hostKind: "daemon",
@@ -27628,7 +28073,7 @@ async function loginWithDeviceCode(args) {
27628
28073
  }
27629
28074
  const previousConfig = readConfigForRegistration();
27630
28075
  const { apiUrl, wsUrl, endpointProfile } = resolveEndpoints(args);
27631
- const displayName = argValue(args, "--name") ?? (0, import_node_os7.hostname)();
28076
+ const displayName = argValue(args, "--name") ?? (0, import_node_os8.hostname)();
27632
28077
  const maxPolls = Number.parseInt(argValue(args, "--max-polls") ?? "300", 10);
27633
28078
  const resumed = readPendingDeviceAuthorization(apiUrl);
27634
28079
  const installationId = resumed?.installationId ?? previousConfig.installationId ?? (0, import_node_crypto3.randomUUID)();
@@ -27640,7 +28085,7 @@ async function loginWithDeviceCode(args) {
27640
28085
  headers: { "content-type": "application/json" },
27641
28086
  body: JSON.stringify({
27642
28087
  displayName,
27643
- hostname: (0, import_node_os7.hostname)(),
28088
+ hostname: (0, import_node_os8.hostname)(),
27644
28089
  capabilities: discoverCapabilities(),
27645
28090
  metadata: {
27646
28091
  ...await collectRuntimeMetadataWithProviderLimits(),
@@ -27874,7 +28319,7 @@ async function startDaemon(args) {
27874
28319
  const workspaceRelocations = /* @__PURE__ */ new Map();
27875
28320
  for (const relocation of stored.workspaceRelocations ?? []) {
27876
28321
  if (relocation?.conversationId && relocation.runId && relocation.previousPath && relocation.canonicalPath) {
27877
- workspaceRelocations.set((0, import_node_path10.resolve)(relocation.previousPath), relocation);
28322
+ workspaceRelocations.set((0, import_node_path11.resolve)(relocation.previousPath), relocation);
27878
28323
  }
27879
28324
  }
27880
28325
  if (stored.localApiPort !== localApiPort || stored.localApiToken !== localApiToken || stored.localServiceId !== localServiceId || stored.localServiceSecret !== localServiceSecret || stored.eventSpoolKey !== eventSpoolKey || stored.daemonEpoch !== daemonEpoch || stored.installationId !== installationId) {
@@ -28270,7 +28715,26 @@ async function startDaemon(args) {
28270
28715
  return;
28271
28716
  }
28272
28717
  const backendKind = normalizeBackendKind2(payload.backendKind);
28273
- const runtimeCommand = resolveBackendRuntimeCommand(backendKind ?? payload.backendKind);
28718
+ let runtimeCommand = resolveBackendRuntimeCommand(backendKind ?? payload.backendKind);
28719
+ if (!runtimeCommand && backendKind) {
28720
+ runtimeCommand = revalidateBackendRuntimeCommand(backendKind);
28721
+ }
28722
+ if (!runtimeCommand && backendKind && canInstallProviderCli(backendKind)) {
28723
+ pendingRunStarts.set(payload.runId, payload.conversationId);
28724
+ try {
28725
+ const installed = await ensureProviderCliInstalled(backendKind, {
28726
+ notify: (message) => {
28727
+ pushLog(message);
28728
+ console.info(`[alan-agent] ${message}`);
28729
+ }
28730
+ });
28731
+ if (installed) {
28732
+ runtimeCommand = revalidateBackendRuntimeCommand(backendKind);
28733
+ }
28734
+ } finally {
28735
+ pendingRunStarts.delete(payload.runId);
28736
+ }
28737
+ }
28274
28738
  if (!runtimeCommand && backendKind) {
28275
28739
  socket.emit("agent.rejected", {
28276
28740
  runId: payload.runId,
@@ -28284,31 +28748,31 @@ async function startDaemon(args) {
28284
28748
  const workspaceRequired = Boolean(explicitWorkspacePath);
28285
28749
  if (isAlanDefaultWorkspace(payload.projectPath)) {
28286
28750
  try {
28287
- (0, import_node_fs12.mkdirSync)(cwd, { recursive: true });
28751
+ (0, import_node_fs13.mkdirSync)(cwd, { recursive: true });
28288
28752
  } catch {
28289
28753
  }
28290
28754
  }
28291
28755
  let workspaceReadiness = inspectWorkspaceReadiness(workspaceRequired ? cwd : void 0);
28292
28756
  const expectedRepoUrls = [
28293
28757
  ...(payload.repoLocalPaths ?? []).filter(
28294
- (repo) => explicitWorkspacePath ? (0, import_node_path10.resolve)(repo.localPath) === (0, import_node_path10.resolve)(explicitWorkspacePath) : false
28758
+ (repo) => explicitWorkspacePath ? (0, import_node_path11.resolve)(repo.localPath) === (0, import_node_path11.resolve)(explicitWorkspacePath) : false
28295
28759
  ).map((repo) => repo.repoUrl),
28296
28760
  ...payload.localRepoUrls ?? []
28297
28761
  ];
28298
28762
  if (workspaceReadiness.code === "workspace_missing" && explicitWorkspacePath && !payload.worktreeInfo) {
28299
- const previousPath = (0, import_node_path10.resolve)(explicitWorkspacePath);
28763
+ const previousPath = (0, import_node_path11.resolve)(explicitWorkspacePath);
28300
28764
  const remembered = workspaceRelocations.get(previousPath);
28301
28765
  const rememberedPath = remembered?.canonicalPath;
28302
28766
  const relocated = rememberedPath && workspaceMatchesExpectedRepositories({
28303
28767
  workspacePath: rememberedPath,
28304
28768
  expectedRepoUrls
28305
- }) ? (0, import_node_path10.resolve)(rememberedPath) : discoverRelocatedWorkspace({
28769
+ }) ? (0, import_node_path11.resolve)(rememberedPath) : discoverRelocatedWorkspace({
28306
28770
  missingPath: explicitWorkspacePath,
28307
28771
  approvedWorkspacePaths: payload.approvedWorkspacePaths,
28308
28772
  expectedRepoUrls
28309
28773
  });
28310
28774
  if (relocated) {
28311
- if (!remembered || (0, import_node_path10.resolve)(remembered.canonicalPath) !== relocated) {
28775
+ if (!remembered || (0, import_node_path11.resolve)(remembered.canonicalPath) !== relocated) {
28312
28776
  workspaceRelocations.set(previousPath, {
28313
28777
  conversationId: payload.conversationId,
28314
28778
  runId: payload.runId,
@@ -28769,6 +29233,29 @@ async function startDaemon(args) {
28769
29233
  })();
28770
29234
  }
28771
29235
  );
29236
+ socket.on(
29237
+ "runtime.skills.scan",
29238
+ (payload) => {
29239
+ if (!payload?.requestId) return;
29240
+ const requestId = payload.requestId;
29241
+ try {
29242
+ const projectPath = isAlanDefaultWorkspace(payload.projectPath) ? resolveAlanDefaultWorkspacePath() : payload.projectPath;
29243
+ const result = scanLocalSkillsCached((0, import_node_os8.homedir)(), projectPath, {
29244
+ refresh: payload.refresh === true
29245
+ });
29246
+ socket.emit("runtime.skills.result", {
29247
+ requestId,
29248
+ skills: result.skills,
29249
+ errors: result.errors
29250
+ });
29251
+ } catch (error) {
29252
+ socket.emit("runtime.skills.result", {
29253
+ requestId,
29254
+ error: error instanceof Error ? error.message : String(error)
29255
+ });
29256
+ }
29257
+ }
29258
+ );
28772
29259
  const adoptionChallenges = /* @__PURE__ */ new Map();
28773
29260
  const localServer = import_node_http.default.createServer((req, res) => {
28774
29261
  if (req.method === "GET" && req.url === "/owner") {
@@ -29762,7 +30249,7 @@ var agentProbeAckSchema = external_exports.object({
29762
30249
 
29763
30250
  // src/sandbox-outbox.ts
29764
30251
  var import_node_crypto5 = require("crypto");
29765
- var import_node_fs13 = require("fs");
30252
+ var import_node_fs14 = require("fs");
29766
30253
  var SANDBOX_EVENT_OUTBOX_ENV = "ALAN_EVENT_OUTBOX_ENABLED";
29767
30254
  function isSandboxEventOutboxEnabled(env = process.env) {
29768
30255
  return env[SANDBOX_EVENT_OUTBOX_ENV] === "true";
@@ -29785,7 +30272,7 @@ function createSandboxEventOutbox(input) {
29785
30272
  input.pushLog?.(
29786
30273
  `sandbox_outbox_spool_reset ${error instanceof Error ? error.message : String(error)}`
29787
30274
  );
29788
- (0, import_node_fs13.rmSync)(path, { force: true });
30275
+ (0, import_node_fs14.rmSync)(path, { force: true });
29789
30276
  return new EncryptedEventOutbox(path, key, input.pushLog, input.options);
29790
30277
  }
29791
30278
  }
@@ -30482,10 +30969,10 @@ async function runSandbox(config) {
30482
30969
  }
30483
30970
 
30484
30971
  // src/service-manager.ts
30485
- var import_node_child_process6 = require("child_process");
30486
- var import_node_fs14 = require("fs");
30487
- var import_node_os8 = require("os");
30488
- var import_node_path11 = require("path");
30972
+ var import_node_child_process7 = require("child_process");
30973
+ var import_node_fs15 = require("fs");
30974
+ var import_node_os9 = require("os");
30975
+ var import_node_path12 = require("path");
30489
30976
  var SERVICE_LABEL = "ai.tryalan.agent";
30490
30977
  var SYSTEMD_UNIT = "alan-agent.service";
30491
30978
  var WINDOWS_TASK = "Alan Agent";
@@ -30505,8 +30992,8 @@ function buildDaemonServicePlan(input) {
30505
30992
  if (userId === void 0) throw new Error("Cannot determine the current macOS user ID");
30506
30993
  const domain = `gui/${userId}`;
30507
30994
  const serviceTarget = `${domain}/${SERVICE_LABEL}`;
30508
- const manifestPath = (0, import_node_path11.join)(input.homeDir, "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
30509
- const logDir = (0, import_node_path11.join)(input.homeDir, ".alan", "agent", "logs");
30995
+ const manifestPath = (0, import_node_path12.join)(input.homeDir, "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
30996
+ const logDir = (0, import_node_path12.join)(input.homeDir, ".alan", "agent", "logs");
30510
30997
  const programArguments = daemonArgs.map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
30511
30998
  const manifest = `<?xml version="1.0" encoding="UTF-8"?>
30512
30999
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -30528,9 +31015,9 @@ ${programArguments}
30528
31015
  <key>ThrottleInterval</key>
30529
31016
  <integer>5</integer>
30530
31017
  <key>StandardOutPath</key>
30531
- <string>${xml((0, import_node_path11.join)(logDir, "daemon.log"))}</string>
31018
+ <string>${xml((0, import_node_path12.join)(logDir, "daemon.log"))}</string>
30532
31019
  <key>StandardErrorPath</key>
30533
- <string>${xml((0, import_node_path11.join)(logDir, "daemon-error.log"))}</string>
31020
+ <string>${xml((0, import_node_path12.join)(logDir, "daemon-error.log"))}</string>
30534
31021
  </dict>
30535
31022
  </plist>
30536
31023
  `;
@@ -30569,7 +31056,7 @@ ${programArguments}
30569
31056
  };
30570
31057
  }
30571
31058
  if (input.platform === "linux") {
30572
- const manifestPath = (0, import_node_path11.join)(input.homeDir, ".config", "systemd", "user", SYSTEMD_UNIT);
31059
+ const manifestPath = (0, import_node_path12.join)(input.homeDir, ".config", "systemd", "user", SYSTEMD_UNIT);
30573
31060
  const manifest = `[Unit]
30574
31061
  Description=Alan local agent daemon
30575
31062
  After=network-online.target
@@ -30666,7 +31153,7 @@ WantedBy=default.target
30666
31153
  };
30667
31154
  }
30668
31155
  function currentServicePlan(args) {
30669
- const currentPlatform = (0, import_node_os8.platform)();
31156
+ const currentPlatform = (0, import_node_os9.platform)();
30670
31157
  if (currentPlatform !== "darwin" && currentPlatform !== "linux" && currentPlatform !== "win32") {
30671
31158
  throw new Error(`Daemon service installation is not supported on ${currentPlatform}`);
30672
31159
  }
@@ -30675,25 +31162,25 @@ function currentServicePlan(args) {
30675
31162
  if (!cliEntry) throw new Error("Cannot determine the alan-agent executable path");
30676
31163
  return buildDaemonServicePlan({
30677
31164
  platform: currentPlatform,
30678
- homeDir: (0, import_node_os8.homedir)(),
31165
+ homeDir: (0, import_node_os9.homedir)(),
30679
31166
  nodeExecutable: process.execPath,
30680
- cliEntry: (0, import_node_path11.resolve)(cliEntry),
31167
+ cliEntry: (0, import_node_path12.resolve)(cliEntry),
30681
31168
  profile
30682
31169
  });
30683
31170
  }
30684
31171
  function writeManifest(path, contents) {
30685
- (0, import_node_fs14.mkdirSync)((0, import_node_path11.dirname)(path), { recursive: true, mode: 448 });
31172
+ (0, import_node_fs15.mkdirSync)((0, import_node_path12.dirname)(path), { recursive: true, mode: 448 });
30686
31173
  const pendingPath = `${path}.pending-${process.pid}`;
30687
31174
  try {
30688
- (0, import_node_fs14.writeFileSync)(pendingPath, contents, { mode: 384 });
30689
- (0, import_node_fs14.chmodSync)(pendingPath, 384);
30690
- (0, import_node_fs14.renameSync)(pendingPath, path);
31175
+ (0, import_node_fs15.writeFileSync)(pendingPath, contents, { mode: 384 });
31176
+ (0, import_node_fs15.chmodSync)(pendingPath, 384);
31177
+ (0, import_node_fs15.renameSync)(pendingPath, path);
30691
31178
  } finally {
30692
- (0, import_node_fs14.rmSync)(pendingPath, { force: true });
31179
+ (0, import_node_fs15.rmSync)(pendingPath, { force: true });
30693
31180
  }
30694
31181
  }
30695
31182
  function runServiceCommand(command) {
30696
- const result = (0, import_node_child_process6.spawnSync)(command.command, command.args, { encoding: "utf8" });
31183
+ const result = (0, import_node_child_process7.spawnSync)(command.command, command.args, { encoding: "utf8" });
30697
31184
  const status = result.status ?? 1;
30698
31185
  const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
30699
31186
  if (status !== 0 && !command.tolerateFailure) {
@@ -30711,8 +31198,8 @@ function installDaemonService(args = []) {
30711
31198
  }
30712
31199
  const plan = currentServicePlan(args);
30713
31200
  if (plan.manifestPath && plan.manifest) {
30714
- if ((0, import_node_os8.platform)() === "darwin") {
30715
- (0, import_node_fs14.mkdirSync)((0, import_node_path11.join)((0, import_node_os8.homedir)(), ".alan", "agent", "logs"), { recursive: true, mode: 448 });
31201
+ if ((0, import_node_os9.platform)() === "darwin") {
31202
+ (0, import_node_fs15.mkdirSync)((0, import_node_path12.join)((0, import_node_os9.homedir)(), ".alan", "agent", "logs"), { recursive: true, mode: 448 });
30716
31203
  }
30717
31204
  writeManifest(plan.manifestPath, plan.manifest);
30718
31205
  }
@@ -30722,7 +31209,7 @@ function installDaemonService(args = []) {
30722
31209
  function uninstallDaemonService(args = []) {
30723
31210
  const plan = currentServicePlan(args);
30724
31211
  for (const command of plan.uninstallCommands) runServiceCommand(command);
30725
- if (plan.manifestPath) (0, import_node_fs14.rmSync)(plan.manifestPath, { force: true });
31212
+ if (plan.manifestPath) (0, import_node_fs15.rmSync)(plan.manifestPath, { force: true });
30726
31213
  console.info("[alan-agent] Per-user daemon service removed");
30727
31214
  }
30728
31215
  function printDaemonServiceStatus(args = []) {