@aiden-ade/sandbox-agent 0.1.76 → 0.1.77

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 +168 -45
  2. package/package.json +3 -3
package/dist/index.cjs CHANGED
@@ -15231,7 +15231,7 @@ function describeError(error61) {
15231
15231
  }
15232
15232
 
15233
15233
  // src/version.ts
15234
- var AGENT_VERSION = "0.1.76";
15234
+ var AGENT_VERSION = "0.1.77";
15235
15235
 
15236
15236
  // src/daemon-worktree.ts
15237
15237
  var import_node_child_process3 = require("child_process");
@@ -37172,6 +37172,13 @@ var ACTIVE_AGENT_RUN_STATUSES = [
37172
37172
  ];
37173
37173
  var STOPPABLE_AGENT_RUN_STATUSES = [...ACTIVE_AGENT_RUN_STATUSES, "paused"];
37174
37174
 
37175
+ // ../shared/dist/agent/types.js
37176
+ function resolveEffectiveBackendKind(backendKind) {
37177
+ if (backendKind === "codex")
37178
+ return "codex_app_server";
37179
+ return backendKind || "claude_cli";
37180
+ }
37181
+
37175
37182
  // ../shared/dist/effort.js
37176
37183
  var EFFORT_LEVELS = ["minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
37177
37184
  var CLAUDE_EFFORT_LEVELS = [
@@ -41653,7 +41660,7 @@ function createClaudeCliBackend(command = "claude", defaultArgs = []) {
41653
41660
  } else if (resumeId && context.config.systemPromptAppend?.trim()) {
41654
41661
  args.push("--append-system-prompt", context.config.systemPromptAppend.trim());
41655
41662
  }
41656
- if (context.config.teamId?.trim() && await cliSupportsFlag(command, "--approve-mcps", context.env)) {
41663
+ if (context.config.alanMcp && await cliSupportsFlag(command, "--approve-mcps", context.env)) {
41657
41664
  args.push("--approve-mcps");
41658
41665
  }
41659
41666
  const baseModel = collapseModelIdToBase(context.config.selectedModel?.trim() || "");
@@ -42389,7 +42396,15 @@ function emitCodexFileChanges(context, state, itemId, changes) {
42389
42396
  const toolName = kind === "add" ? "write_file" : "edit_file";
42390
42397
  const toolId = changes.length > 1 ? `${itemId}:${index}` : itemId;
42391
42398
  trackCodexStreamedToolId(state, toolId);
42392
- void context.presenter.onToolUse(toolName, { file_path: filePath, kind }, toolId);
42399
+ const diffMetadata = {};
42400
+ for (const key of ["additions", "deletions", "patch", "diff"]) {
42401
+ if (record2[key] !== void 0) diffMetadata[key] = record2[key];
42402
+ }
42403
+ void context.presenter.onToolUse(
42404
+ toolName,
42405
+ { file_path: filePath, kind, ...diffMetadata },
42406
+ toolId
42407
+ );
42393
42408
  void context.presenter.onToolResult?.(toolId, `${kind} ${filePath}`);
42394
42409
  });
42395
42410
  }
@@ -43794,7 +43809,7 @@ function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = [])
43794
43809
  context.config.images,
43795
43810
  context.cwd
43796
43811
  );
43797
- const approveMcps = context.config.teamId?.trim() ? await cliSupportsFlag(command, "--approve-mcps", context.env) : false;
43812
+ const approveMcps = context.config.alanMcp ? await cliSupportsFlag(command, "--approve-mcps", context.env) : false;
43798
43813
  try {
43799
43814
  return await createGenericCliBackend({
43800
43815
  kind: "cursor_agent_cli",
@@ -46251,10 +46266,6 @@ ${ctx.promptText}` : ctx.promptText;
46251
46266
  }
46252
46267
  var DROID_DEFAULT_MODEL = "claude-opus-4-8";
46253
46268
  var AGENT_ACTIVITY_HEARTBEAT_MS = 1e4;
46254
- function normalizeBackendKind(kind) {
46255
- if (kind === "codex") return "codex_app_server";
46256
- return kind || "claude_cli";
46257
- }
46258
46269
  function firstCatalogModelId(backendKind) {
46259
46270
  const fromPicker = catalogModelsForProvider(backendKind, PROVIDER_MODELS[backendKind]).find(
46260
46271
  (model) => model.id.trim()
@@ -46501,7 +46512,7 @@ var BaseMachineAgent = class _BaseMachineAgent {
46501
46512
  return message.includes("aborted") || message.includes("interrupted") || message.includes("cancelled") || message.includes("canceled");
46502
46513
  }
46503
46514
  resolveBackendKind(config2) {
46504
- return normalizeBackendKind(this.runtime.backendKind || config2.backendKind);
46515
+ return resolveEffectiveBackendKind(this.runtime.backendKind || config2.backendKind);
46505
46516
  }
46506
46517
  getDefaultSupportTier(backendKind) {
46507
46518
  switch (backendKind) {
@@ -49447,7 +49458,7 @@ async function discoverProviderModelDetails(provider, executable, env) {
49447
49458
  }
49448
49459
 
49449
49460
  // src/daemon-provider-scan.ts
49450
- function normalizeBackendKind2(backendKind) {
49461
+ function normalizeBackendKind(backendKind) {
49451
49462
  if (backendKind === "codex") return "codex_app_server";
49452
49463
  return backendKind;
49453
49464
  }
@@ -55677,7 +55688,7 @@ function removeAlanFromJsonConfig(path2, existingContent) {
55677
55688
  }
55678
55689
  return JSON.stringify(config2);
55679
55690
  }
55680
- async function unregisterAlanMcp(backendKind, home = (0, import_node_os8.homedir)()) {
55691
+ async function unregisterAlanMcp(backendKind, home = (0, import_node_os8.homedir)(), options = {}) {
55681
55692
  const removedPaths = [];
55682
55693
  const errors = [];
55683
55694
  const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
@@ -55686,6 +55697,12 @@ async function unregisterAlanMcp(backendKind, home = (0, import_node_os8.homedir
55686
55697
  const releaseLock = await waitForConfigLock(`${file2.path}.alan-lock`);
55687
55698
  try {
55688
55699
  const current = (0, import_node_fs14.readFileSync)(file2.path, "utf8");
55700
+ if (options.onlyIfRegistration && !registrationsMatch(
55701
+ parseAlanMcpRegistration(file2.path, current),
55702
+ options.onlyIfRegistration
55703
+ )) {
55704
+ continue;
55705
+ }
55689
55706
  let next;
55690
55707
  if (file2.path.endsWith(".toml")) {
55691
55708
  const stripped = stripCodexAlanMcpSection(current);
@@ -55742,7 +55759,9 @@ function hasUsableRegistrationCredential(headers) {
55742
55759
  return Boolean(normalized.get("x-alan-run-reference") || normalized.get("authorization"));
55743
55760
  }
55744
55761
  function readAlanMcpRegistration(path2) {
55745
- const content = (0, import_node_fs14.readFileSync)(path2, "utf8");
55762
+ return parseAlanMcpRegistration(path2, (0, import_node_fs14.readFileSync)(path2, "utf8"));
55763
+ }
55764
+ function parseAlanMcpRegistration(path2, content) {
55746
55765
  if (path2.endsWith(".toml")) {
55747
55766
  try {
55748
55767
  const parsed = parse5(content);
@@ -72928,7 +72947,7 @@ async function handleAgentExecute(ctx, payload) {
72928
72947
  });
72929
72948
  return;
72930
72949
  }
72931
- const backendKind = normalizeBackendKind2(payload.backendKind);
72950
+ const backendKind = normalizeBackendKind(payload.backendKind);
72932
72951
  let runtimeCommand = resolveBackendRuntimeCommand(backendKind ?? payload.backendKind);
72933
72952
  if (!runtimeCommand && backendKind) {
72934
72953
  runtimeCommand = revalidateBackendRuntimeCommand(backendKind);
@@ -73113,12 +73132,13 @@ async function handleAgentExecute(ctx, payload) {
73113
73132
  });
73114
73133
  return;
73115
73134
  }
73135
+ const registrationBackendKind = resolveEffectiveBackendKind(payload.backendKind);
73116
73136
  const authPromise = backendKind && runtimeCommand && !payload.providerApiKey ? preflightProviderAuth({
73117
73137
  backendKind,
73118
73138
  executable: runtimeCommand,
73119
73139
  env: getDaemonCliEnvironment()
73120
73140
  }).catch(() => ({ status: "unknown" })) : Promise.resolve({ status: "unknown" });
73121
- const mcpPromise = backendKind ? prepareAlanMcpForRun(backendKind, alanMcp, {
73141
+ const mcpPromise = prepareAlanMcpForRun(registrationBackendKind, alanMcp, {
73122
73142
  // Observe mode resolves after the run has already been accepted, so
73123
73143
  // the verdict is logged here rather than folded into the result.
73124
73144
  onObservedResult: (failure) => {
@@ -73129,7 +73149,7 @@ async function handleAgentExecute(ctx, payload) {
73129
73149
  stage: failure.stage
73130
73150
  });
73131
73151
  }
73132
- }) : Promise.resolve(null);
73152
+ });
73133
73153
  const skillTaskPromise = prepareSkillInvocationTask({
73134
73154
  manager: runtimeSkillManager,
73135
73155
  runtimeId,
@@ -73165,7 +73185,7 @@ async function handleAgentExecute(ctx, payload) {
73165
73185
  });
73166
73186
  return;
73167
73187
  }
73168
- if (backendKind && mcpOutcome && !mcpOutcome.ok) {
73188
+ if (!mcpOutcome.ok) {
73169
73189
  pendingRunStarts.delete(payload.runId);
73170
73190
  releaseRunStart();
73171
73191
  const readinessCode = mcpOutcome.code;
@@ -73182,7 +73202,7 @@ async function handleAgentExecute(ctx, payload) {
73182
73202
  });
73183
73203
  return;
73184
73204
  }
73185
- if (backendKind && alanMcp && mcpOutcome && mcpOutcome.ok && "paths" in mcpOutcome) {
73205
+ if ("paths" in mcpOutcome) {
73186
73206
  pushLog(
73187
73207
  `mcp-ready run=${payload.runId} verification=${"verification" in mcpOutcome ? mcpOutcome.verification : "file"}`
73188
73208
  );
@@ -73351,10 +73371,8 @@ async function handleAgentExecute(ctx, payload) {
73351
73371
  workspaceLease: workspaceLeaseCapture.lease
73352
73372
  });
73353
73373
  pendingRunStarts.delete(payload.runId);
73354
- if (payload.alanMcp && backendKind) {
73355
- alanMcpRegisteredRuns.add(payload.runId);
73356
- alanMcpRegisteredBackends.add(backendKind);
73357
- }
73374
+ alanMcpRegisteredRuns.set(payload.runId, alanMcp);
73375
+ alanMcpRegisteredBackends.add(registrationBackendKind);
73358
73376
  void agent.run({
73359
73377
  task: preparedTask,
73360
73378
  maxIterations: payload.maxIterations ?? 50,
@@ -74771,6 +74789,15 @@ var SKILL_PROVIDER_SOURCE_DEFINITIONS = [
74771
74789
  userDirectories: [".cursor/skills"],
74772
74790
  repositoryDirectories: [".cursor/skills"]
74773
74791
  },
74792
+ {
74793
+ // Canonical kind, not the `codex_cli` / `codex` aliases: LEGACY_PROVIDER_KIND_MAP
74794
+ // normalizes both to this, and the enabled-provider set is built from
74795
+ // normalized kinds, so a source declared under an alias never matches.
74796
+ providerKind: "codex_app_server",
74797
+ label: "Codex skills",
74798
+ userDirectories: [".codex/skills"],
74799
+ repositoryDirectories: [".codex/skills"]
74800
+ },
74774
74801
  {
74775
74802
  providerKind: "copilot_cli",
74776
74803
  label: "Copilot skills",
@@ -74812,8 +74839,6 @@ function providerKindsForUserDirectory(relativeDirectory) {
74812
74839
  const providers = SKILL_PROVIDER_SOURCE_DEFINITIONS.flatMap((definition) => definition.userDirectories.includes(normalized) ? [definition.providerKind] : []);
74813
74840
  if (providers.length > 0)
74814
74841
  return providers;
74815
- if (normalized === ".codex/skills")
74816
- return ["cursor_agent_cli"];
74817
74842
  if (normalized === ".gemini/config/skills")
74818
74843
  return [];
74819
74844
  return void 0;
@@ -74849,6 +74874,11 @@ path = "~/.cursor/skills"
74849
74874
  label = "Cursor skills"
74850
74875
  providers = ["cursor_agent_cli"]
74851
74876
 
74877
+ [[skills.sources]]
74878
+ path = "~/.codex/skills"
74879
+ label = "Codex skills"
74880
+ providers = ["codex_app_server"]
74881
+
74852
74882
  [[skills.sources]]
74853
74883
  path = "~/.copilot/skills"
74854
74884
  label = "Copilot skills"
@@ -74869,12 +74899,15 @@ var LocalSkillSource = class {
74869
74899
  const candidates = [];
74870
74900
  const visitedDirectories = /* @__PURE__ */ new Set();
74871
74901
  let incompleteRoot = false;
74872
- for (const resolvedRoot of resolveScanRoots(input2.roots, input2.enabledProviderKinds ?? [])) {
74902
+ const resolvedRoots = resolveScanRoots(input2.roots, input2.enabledProviderKinds ?? []);
74903
+ const declaredRoots = await canonicalDirectories(resolvedRoots.map((root) => root.directory));
74904
+ for (const resolvedRoot of resolvedRoots) {
74873
74905
  const rootResult = await this.scanRoot(
74874
74906
  resolvedRoot,
74875
74907
  visitedDirectories,
74876
74908
  input2.ignoredPaths ?? [],
74877
- input2.mode
74909
+ input2.mode,
74910
+ declaredRoots
74878
74911
  );
74879
74912
  candidates.push(...rootResult.candidates);
74880
74913
  diagnostics.push(...rootResult.diagnostics);
@@ -74929,7 +74962,63 @@ var LocalSkillSource = class {
74929
74962
  if (cached2?.contentHash === expectedContentHash) return cached2;
74930
74963
  return inspectSkillPackage(absolutePath, { expectedName: null });
74931
74964
  }
74932
- async scanRoot(resolvedRoot, visitedDirectories, ignoredPaths, mode) {
74965
+ /**
74966
+ * The root read as one package, or null when it does not hold a SKILL.md.
74967
+ *
74968
+ * `expectedName` is null here on purpose. A package inside a library must be
74969
+ * named for its directory, but a root is whatever the person called the
74970
+ * folder, so that rule would reject it with DIRECTORY_NAME_MISMATCH.
74971
+ */
74972
+ async scanRootAsPackage(resolvedRoot, ignoredPaths, mode) {
74973
+ const directory = resolvedRoot.directory;
74974
+ if (!await pathExists((0, import_node_path16.join)(directory, "SKILL.md"))) return null;
74975
+ const canonicalRoot = await (0, import_promises2.realpath)(directory).catch(() => directory);
74976
+ const name = import_node_path16.default.basename(canonicalRoot);
74977
+ try {
74978
+ const fingerprint = await fingerprintPackage(canonicalRoot);
74979
+ const ignoreSignature = JSON.stringify(ignoredPaths);
74980
+ const cached2 = this.inspectionCache.get(canonicalRoot);
74981
+ const inspected = mode === "incremental" && cached2?.fingerprint === fingerprint && cached2.ignoreSignature === ignoreSignature ? cached2.inspected : await inspectSkillPackage(canonicalRoot, { ignoredPaths, expectedName: null });
74982
+ this.inspectionCache.set(canonicalRoot, { fingerprint, ignoreSignature, inspected });
74983
+ return {
74984
+ candidates: [
74985
+ {
74986
+ absolutePath: canonicalRoot,
74987
+ rootPath: canonicalRoot,
74988
+ order: `${resolvedRoot.order}:${name}`,
74989
+ candidate: {
74990
+ localKey: createLocalKey(resolvedRoot.root, inspected.metadata.name),
74991
+ name: inspected.metadata.name,
74992
+ description: inspected.metadata.description,
74993
+ contentHash: inspected.contentHash,
74994
+ fileCount: inspected.fileCount,
74995
+ totalBytes: inspected.totalBytes,
74996
+ origin: candidateOrigin(resolvedRoot.root),
74997
+ diagnostics: inspected.diagnostics
74998
+ }
74999
+ }
75000
+ ],
75001
+ diagnostics: [],
75002
+ incomplete: false
75003
+ };
75004
+ } catch (error61) {
75005
+ return {
75006
+ candidates: [],
75007
+ diagnostics: [
75008
+ {
75009
+ code: "INVALID_SKILL",
75010
+ message: error61 instanceof Error ? error61.message : String(error61),
75011
+ rootLabel: resolvedRoot.root.label,
75012
+ localKey: createLocalKey(resolvedRoot.root, name)
75013
+ }
75014
+ ],
75015
+ incomplete: false
75016
+ };
75017
+ }
75018
+ }
75019
+ async scanRoot(resolvedRoot, visitedDirectories, ignoredPaths, mode, declaredRoots) {
75020
+ const rootPackage = await this.scanRootAsPackage(resolvedRoot, ignoredPaths, mode);
75021
+ if (rootPackage) return rootPackage;
74933
75022
  let entries;
74934
75023
  try {
74935
75024
  entries = await (0, import_promises2.readdir)(resolvedRoot.directory, { withFileTypes: true });
@@ -74964,10 +75053,11 @@ var LocalSkillSource = class {
74964
75053
  });
74965
75054
  continue;
74966
75055
  }
74967
- if (!isWithinDirectory(canonicalRoot, canonicalPath)) {
75056
+ const roots = declaredRoots.length > 0 ? declaredRoots : [canonicalRoot];
75057
+ if (!roots.some((root) => isWithinDirectory(root, canonicalPath))) {
74968
75058
  diagnostics.push({
74969
75059
  code: "SOURCE_OUTSIDE_DECLARED_ROOT",
74970
- message: `Skill ${entry.name} resolves outside ${resolvedRoot.root.label}`,
75060
+ message: `Skill ${entry.name} resolves outside every declared Skill source`,
74971
75061
  rootLabel: resolvedRoot.root.label
74972
75062
  });
74973
75063
  continue;
@@ -74985,6 +75075,7 @@ var LocalSkillSource = class {
74985
75075
  const localKey = createLocalKey(resolvedRoot.root, entry.name);
74986
75076
  candidates.push({
74987
75077
  absolutePath: canonicalPath,
75078
+ rootPath: canonicalRoot,
74988
75079
  order: `${resolvedRoot.order}:${entry.name}`,
74989
75080
  candidate: {
74990
75081
  localKey,
@@ -75027,6 +75118,21 @@ async function fingerprintPackage(root) {
75027
75118
  }
75028
75119
  }
75029
75120
  }
75121
+ var CODEX_SOURCE_LABEL = "Codex skills";
75122
+ var STALE_CODEX_LABEL = "Legacy Cursor-compatible skills";
75123
+ var STALE_CODEX_PROVIDER = "cursor_agent_cli";
75124
+ function staleCodexDeclaration(relativeDirectory, label, providerKinds) {
75125
+ const normalized = relativeDirectory.replaceAll("\\", "/").replace(/^\.\//, "");
75126
+ if (normalized !== ".codex/skills") return false;
75127
+ const ownedByCursor = providerKinds !== void 0 && providerKinds.length === 1 && providerKinds[0] === STALE_CODEX_PROVIDER;
75128
+ return ownedByCursor || label === STALE_CODEX_LABEL;
75129
+ }
75130
+ async function canonicalDirectories(directories) {
75131
+ const canonical = await Promise.all(
75132
+ directories.map((directory) => (0, import_promises2.realpath)(directory).catch(() => directory))
75133
+ );
75134
+ return [...new Set(canonical)];
75135
+ }
75030
75136
  function isWithinDirectory(parent, candidate) {
75031
75137
  const path2 = (0, import_node_path16.relative)(parent, candidate);
75032
75138
  return path2 !== ".." && !path2.startsWith(`..${import_node_path16.sep}`) && !(0, import_node_path16.isAbsolute)(path2);
@@ -75124,12 +75230,14 @@ function parseSkillSourceConfig(content, homeDirectory) {
75124
75230
  );
75125
75231
  const relativeDirectory = import_node_path16.default.relative(homeDirectory, sourcePath);
75126
75232
  const inferredProviders = providerKindsForUserDirectory(relativeDirectory);
75233
+ const declaredProviders = value2.providers === void 0 ? inferredProviders : [...new Set(explicitProviders.map((provider) => provider.trim()).filter(Boolean))];
75234
+ const stale = staleCodexDeclaration(relativeDirectory, label, declaredProviders);
75127
75235
  return {
75128
75236
  declarationId,
75129
75237
  path: sourcePath,
75130
- label,
75238
+ label: stale ? CODEX_SOURCE_LABEL : label,
75131
75239
  precedence: index,
75132
- providerKinds: value2.providers === void 0 ? inferredProviders : [...new Set(explicitProviders.map((provider) => provider.trim()).filter(Boolean))]
75240
+ providerKinds: stale ? inferredProviders : declaredProviders
75133
75241
  };
75134
75242
  });
75135
75243
  const duplicateId = sources.find(
@@ -75224,12 +75332,23 @@ function hashInventory(candidates) {
75224
75332
  return `sha256:${hash2.digest("hex")}`;
75225
75333
  }
75226
75334
  function deduplicateCandidatesByContent(candidates) {
75227
- const seen = /* @__PURE__ */ new Set();
75228
- return candidates.filter(({ candidate }) => {
75229
- if (seen.has(candidate.contentHash)) return false;
75230
- seen.add(candidate.contentHash);
75335
+ const bestByContent = /* @__PURE__ */ new Map();
75336
+ for (const located of candidates) {
75337
+ const existing = bestByContent.get(located.candidate.contentHash);
75338
+ if (!existing || located.rootPath.length > existing.rootPath.length) {
75339
+ bestByContent.set(located.candidate.contentHash, located);
75340
+ }
75341
+ }
75342
+ const winners = new Set(bestByContent.values());
75343
+ return candidates.filter((located) => winners.has(located));
75344
+ }
75345
+ async function pathExists(candidate) {
75346
+ try {
75347
+ await (0, import_promises2.access)(candidate);
75231
75348
  return true;
75232
- });
75349
+ } catch {
75350
+ return false;
75351
+ }
75233
75352
  }
75234
75353
  async function hasManagedMarker(directory) {
75235
75354
  try {
@@ -76065,7 +76184,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76065
76184
  }
76066
76185
  async adoptLegacyProjection(entry) {
76067
76186
  const destination = (0, import_node_path19.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76068
- if (await pathExists(destination)) return;
76187
+ if (await pathExists2(destination)) return;
76069
76188
  const manifest = await readLegacyManifest(this.legacyManifestPath);
76070
76189
  const legacy = manifest?.skills[entry.slug];
76071
76190
  if (!legacy) return;
@@ -76155,7 +76274,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76155
76274
  if (!await packageMatches(pending, entry.contentHash)) {
76156
76275
  throw new Error("Pending managed Skill projection failed content verification");
76157
76276
  }
76158
- if (await pathExists(destination)) {
76277
+ if (await pathExists2(destination)) {
76159
76278
  await (0, import_promises5.rename)(destination, backup);
76160
76279
  movedExisting = true;
76161
76280
  }
@@ -76177,7 +76296,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76177
76296
  } catch (error61) {
76178
76297
  if (!completed && installedPending) {
76179
76298
  await (0, import_promises5.rm)(destination, { recursive: true, force: true });
76180
- if (movedExisting && await pathExists(backup)) await (0, import_promises5.rename)(backup, destination);
76299
+ if (movedExisting && await pathExists2(backup)) await (0, import_promises5.rename)(backup, destination);
76181
76300
  }
76182
76301
  throw error61;
76183
76302
  } finally {
@@ -76189,7 +76308,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76189
76308
  }
76190
76309
  async requireReplaceable(entry) {
76191
76310
  const destination = (0, import_node_path19.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76192
- if (!await pathExists(destination)) return;
76311
+ if (!await pathExists2(destination)) return;
76193
76312
  const marker = await readMarker(destination);
76194
76313
  if (!marker) throw unmanagedPathConflict(entry.runtimeDirectory);
76195
76314
  if (marker.skillId === entry.skillId && marker.versionId === entry.versionId && marker.contentHash === entry.contentHash && await packageMatches(destination, entry.contentHash)) {
@@ -76208,7 +76327,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76208
76327
  const destination = (0, import_node_path19.join)(this.managedSkillsDirectory, runtimeDirectory);
76209
76328
  const compatibility = (0, import_node_path19.join)(this.compatibilitySkillsDirectory, runtimeDirectory);
76210
76329
  const marker = await readMarker(destination);
76211
- if (!await pathExists(destination)) continue;
76330
+ if (!await pathExists2(destination)) continue;
76212
76331
  if (marker && markerMatchesProjection(marker, recorded)) {
76213
76332
  if (!await removeCompatibilityProjection(compatibility, destination, recorded)) {
76214
76333
  failures.push({
@@ -76238,7 +76357,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76238
76357
  async ensureCompatibilityProjection(entry, canonicalDirectory) {
76239
76358
  await (0, import_promises5.mkdir)(this.compatibilitySkillsDirectory, { recursive: true, mode: 448 });
76240
76359
  const destination = (0, import_node_path19.join)(this.compatibilitySkillsDirectory, entry.runtimeDirectory);
76241
- if (await pathExists(destination)) {
76360
+ if (await pathExists2(destination)) {
76242
76361
  if (await compatibilityProjectionMatches(destination, canonicalDirectory, entry)) return;
76243
76362
  throw unmanagedCompatibilityConflict(entry.runtimeDirectory);
76244
76363
  }
@@ -76355,7 +76474,7 @@ async function compatibilityProjectionMatches(destination, canonicalDirectory, e
76355
76474
  return marker?.canonicalDirectory === canonicalDirectory && marker.skillId === entry.skillId && marker.versionId === entry.versionId && marker.contentHash === entry.contentHash && await packageMatches(destination, entry.contentHash);
76356
76475
  }
76357
76476
  async function removeCompatibilityProjection(destination, canonicalDirectory, projection) {
76358
- if (!await pathExists(destination)) return true;
76477
+ if (!await pathExists2(destination)) return true;
76359
76478
  const metadata = await (0, import_promises5.lstat)(destination);
76360
76479
  if (metadata.isSymbolicLink()) {
76361
76480
  const target = await (0, import_promises5.readlink)(destination);
@@ -76442,7 +76561,7 @@ function localRasterMediaType(path2) {
76442
76561
  function localTextMediaType(path2) {
76443
76562
  return path2.toLowerCase().endsWith(".md") ? "text/markdown; charset=utf-8" : "text/plain; charset=utf-8";
76444
76563
  }
76445
- async function pathExists(path2) {
76564
+ async function pathExists2(path2) {
76446
76565
  try {
76447
76566
  await (0, import_promises5.lstat)(path2);
76448
76567
  return true;
@@ -76685,17 +76804,21 @@ async function startDaemon(args) {
76685
76804
  if (sleepInhibitor.backendNames().length > 0) {
76686
76805
  pushLog(`sleep-inhibitor backends=${sleepInhibitor.backendNames().join(",")}`);
76687
76806
  }
76688
- const alanMcpRegisteredRuns = /* @__PURE__ */ new Set();
76807
+ const alanMcpRegisteredRuns = /* @__PURE__ */ new Map();
76689
76808
  const alanMcpRegisteredBackends = /* @__PURE__ */ new Set();
76690
76809
  const finalizeAlanMcpForRun = (runId) => {
76810
+ const registration = alanMcpRegisteredRuns.get(runId);
76691
76811
  if (!alanMcpRegisteredRuns.delete(runId)) return;
76692
76812
  if (alanMcpRegisteredRuns.size > 0) return;
76693
76813
  const backends = [...alanMcpRegisteredBackends];
76694
76814
  alanMcpRegisteredBackends.clear();
76695
76815
  void (async () => {
76696
76816
  for (const backend of backends) {
76817
+ if (alanMcpRegisteredRuns.size > 0) return;
76697
76818
  try {
76698
- const { removedPaths } = await unregisterAlanMcp(backend);
76819
+ const { removedPaths } = await unregisterAlanMcp(backend, void 0, {
76820
+ onlyIfRegistration: registration
76821
+ });
76699
76822
  if (removedPaths.length > 0) {
76700
76823
  pushLog(`mcp-unregistered backend=${backend} run=${runId}`);
76701
76824
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiden-ade/sandbox-agent",
3
- "version": "0.1.76",
3
+ "version": "0.1.77",
4
4
  "type": "module",
5
5
  "description": "Alan agent runtime — cloud sandbox and local daemon (alan-agent CLI)",
6
6
  "bin": {
@@ -25,8 +25,8 @@
25
25
  "tsup": "^8.5.1",
26
26
  "tsx": "^4.19.0",
27
27
  "typescript": "~5.9.3",
28
- "@alan-ai/agent-core": "0.1.0",
29
- "@alan-ai/shared": "0.1.0"
28
+ "@alan-ai/shared": "0.1.0",
29
+ "@alan-ai/agent-core": "0.1.0"
30
30
  },
31
31
  "deprecated": "Use @alan-ai-hq/agent-manager instead.",
32
32
  "scripts": {