@aiden-ade/sandbox-agent 0.1.77 → 0.1.78

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 +2184 -336
  2. package/package.json +3 -3
package/dist/index.cjs CHANGED
@@ -1022,7 +1022,7 @@ var require_node = __commonJS({
1022
1022
  var tty = require("tty");
1023
1023
  var util = require("util");
1024
1024
  exports2.init = init;
1025
- exports2.log = log;
1025
+ exports2.log = log2;
1026
1026
  exports2.formatArgs = formatArgs;
1027
1027
  exports2.save = save;
1028
1028
  exports2.load = load;
@@ -1157,7 +1157,7 @@ var require_node = __commonJS({
1157
1157
  }
1158
1158
  return (/* @__PURE__ */ new Date()).toISOString() + " ";
1159
1159
  }
1160
- function log(...args) {
1160
+ function log2(...args) {
1161
1161
  return process.stderr.write(util.formatWithOptions(exports2.inspectOpts, ...args) + "\n");
1162
1162
  }
1163
1163
  function save(namespaces) {
@@ -3404,7 +3404,7 @@ var require_websocket = __commonJS({
3404
3404
  var http2 = require("http");
3405
3405
  var net = require("net");
3406
3406
  var tls = require("tls");
3407
- var { randomBytes: randomBytes10, createHash: createHash13 } = require("crypto");
3407
+ var { randomBytes: randomBytes13, createHash: createHash16 } = require("crypto");
3408
3408
  var { Duplex, Readable: Readable2 } = require("stream");
3409
3409
  var { URL: URL2 } = require("url");
3410
3410
  var PerMessageDeflate = require_permessage_deflate();
@@ -3931,7 +3931,7 @@ var require_websocket = __commonJS({
3931
3931
  }
3932
3932
  }
3933
3933
  const defaultPort = isSecure ? 443 : 80;
3934
- const key = randomBytes10(16).toString("base64");
3934
+ const key = randomBytes13(16).toString("base64");
3935
3935
  const request = isSecure ? https.request : http2.request;
3936
3936
  const protocolSet = /* @__PURE__ */ new Set();
3937
3937
  let perMessageDeflate;
@@ -4061,7 +4061,7 @@ var require_websocket = __commonJS({
4061
4061
  abortHandshake(websocket, socket, "Invalid Upgrade header");
4062
4062
  return;
4063
4063
  }
4064
- const digest = createHash13("sha1").update(key + GUID).digest("base64");
4064
+ const digest = createHash16("sha1").update(key + GUID).digest("base64");
4065
4065
  if (res.headers["sec-websocket-accept"] !== digest) {
4066
4066
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
4067
4067
  return;
@@ -4428,7 +4428,7 @@ var require_websocket_server = __commonJS({
4428
4428
  var EventEmitter = require("events");
4429
4429
  var http2 = require("http");
4430
4430
  var { Duplex } = require("stream");
4431
- var { createHash: createHash13 } = require("crypto");
4431
+ var { createHash: createHash16 } = require("crypto");
4432
4432
  var extension = require_extension();
4433
4433
  var PerMessageDeflate = require_permessage_deflate();
4434
4434
  var subprotocol = require_subprotocol();
@@ -4725,7 +4725,7 @@ var require_websocket_server = __commonJS({
4725
4725
  );
4726
4726
  }
4727
4727
  if (this._state > RUNNING) return abortHandshake(socket, 503);
4728
- const digest = createHash13("sha1").update(key + GUID).digest("base64");
4728
+ const digest = createHash16("sha1").update(key + GUID).digest("base64");
4729
4729
  const headers = [
4730
4730
  "HTTP/1.1 101 Switching Protocols",
4731
4731
  "Upgrade: websocket",
@@ -6530,7 +6530,7 @@ var require_merge = __commonJS({
6530
6530
  var require_addPairToJSMap = __commonJS({
6531
6531
  "../../node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports2) {
6532
6532
  "use strict";
6533
- var log = require_log();
6533
+ var log2 = require_log();
6534
6534
  var merge2 = require_merge();
6535
6535
  var stringify2 = require_stringify();
6536
6536
  var identity = require_identity();
@@ -6579,7 +6579,7 @@ var require_addPairToJSMap = __commonJS({
6579
6579
  let jsonStr = JSON.stringify(strKey);
6580
6580
  if (jsonStr.length > 40)
6581
6581
  jsonStr = jsonStr.substring(0, 36) + '..."';
6582
- log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
6582
+ log2.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
6583
6583
  ctx.mapKeyWarned = true;
6584
6584
  }
6585
6585
  return strKey;
@@ -11995,7 +11995,7 @@ var require_public_api = __commonJS({
11995
11995
  var composer = require_composer();
11996
11996
  var Document = require_Document();
11997
11997
  var errors = require_errors2();
11998
- var log = require_log();
11998
+ var log2 = require_log();
11999
11999
  var identity = require_identity();
12000
12000
  var lineCounter = require_line_counter();
12001
12001
  var parser = require_parser();
@@ -12047,7 +12047,7 @@ var require_public_api = __commonJS({
12047
12047
  const doc = parseDocument(src, options);
12048
12048
  if (!doc)
12049
12049
  return null;
12050
- doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning));
12050
+ doc.warnings.forEach((warning) => log2.warn(doc.options.logLevel, warning));
12051
12051
  if (doc.errors.length > 0) {
12052
12052
  if (doc.options.logLevel !== "silent")
12053
12053
  throw doc.errors[0];
@@ -13386,8 +13386,8 @@ var require_streamx = __commonJS({
13386
13386
  return this;
13387
13387
  },
13388
13388
  next() {
13389
- return new Promise(function(resolve14, reject) {
13390
- promiseResolve = resolve14;
13389
+ return new Promise(function(resolve15, reject) {
13390
+ promiseResolve = resolve15;
13391
13391
  promiseReject = reject;
13392
13392
  const data = stream.read();
13393
13393
  if (data !== null) ondata(data);
@@ -13420,11 +13420,11 @@ var require_streamx = __commonJS({
13420
13420
  }
13421
13421
  function destroy(err) {
13422
13422
  stream.destroy(err);
13423
- return new Promise((resolve14, reject) => {
13424
- if (stream._duplexState & DESTROYED) return resolve14({ value: void 0, done: true });
13423
+ return new Promise((resolve15, reject) => {
13424
+ if (stream._duplexState & DESTROYED) return resolve15({ value: void 0, done: true });
13425
13425
  stream.once("close", function() {
13426
13426
  if (err) reject(err);
13427
- else resolve14({ value: void 0, done: true });
13427
+ else resolve15({ value: void 0, done: true });
13428
13428
  });
13429
13429
  });
13430
13430
  }
@@ -13468,8 +13468,8 @@ var require_streamx = __commonJS({
13468
13468
  const writes = pending + (ws._duplexState & WRITE_WRITING ? 1 : 0);
13469
13469
  if (writes === 0) return Promise.resolve(true);
13470
13470
  if (state.drains === null) state.drains = [];
13471
- return new Promise((resolve14) => {
13472
- state.drains.push({ writes, resolve: resolve14 });
13471
+ return new Promise((resolve15) => {
13472
+ state.drains.push({ writes, resolve: resolve15 });
13473
13473
  });
13474
13474
  }
13475
13475
  write(data) {
@@ -13574,10 +13574,10 @@ var require_streamx = __commonJS({
13574
13574
  cb(null);
13575
13575
  }
13576
13576
  function pipelinePromise(...streams) {
13577
- return new Promise((resolve14, reject) => {
13577
+ return new Promise((resolve15, reject) => {
13578
13578
  return pipeline(...streams, (err) => {
13579
13579
  if (err) return reject(err);
13580
- resolve14();
13580
+ resolve15();
13581
13581
  });
13582
13582
  });
13583
13583
  }
@@ -14249,16 +14249,16 @@ var require_extract = __commonJS({
14249
14249
  entryCallback = null;
14250
14250
  cb(err);
14251
14251
  }
14252
- function onnext(resolve14, reject) {
14252
+ function onnext(resolve15, reject) {
14253
14253
  if (error61) {
14254
14254
  return reject(error61);
14255
14255
  }
14256
14256
  if (entryStream) {
14257
- resolve14({ value: entryStream, done: false });
14257
+ resolve15({ value: entryStream, done: false });
14258
14258
  entryStream = null;
14259
14259
  return;
14260
14260
  }
14261
- promiseResolve = resolve14;
14261
+ promiseResolve = resolve15;
14262
14262
  promiseReject = reject;
14263
14263
  consumeCallback(null);
14264
14264
  if (extract2._finished && promiseResolve) {
@@ -14286,11 +14286,11 @@ var require_extract = __commonJS({
14286
14286
  function destroy(err) {
14287
14287
  extract2.destroy(err);
14288
14288
  consumeCallback(err);
14289
- return new Promise((resolve14, reject) => {
14290
- if (extract2.destroyed) return resolve14({ value: void 0, done: true });
14289
+ return new Promise((resolve15, reject) => {
14290
+ if (extract2.destroyed) return resolve15({ value: void 0, done: true });
14291
14291
  extract2.once("close", function() {
14292
14292
  if (err) reject(err);
14293
- else resolve14({ value: void 0, done: true });
14293
+ else resolve15({ value: void 0, done: true });
14294
14294
  });
14295
14295
  });
14296
14296
  }
@@ -14719,7 +14719,7 @@ function resolveViaWhere(command, env) {
14719
14719
  return null;
14720
14720
  }
14721
14721
  function runCliProbeAsync(executable, env, args = ["--version"], timeoutMs = 3e3, spawnOptions) {
14722
- return new Promise((resolve14) => {
14722
+ return new Promise((resolve15) => {
14723
14723
  const isWin = (0, import_node_os.platform)() === "win32";
14724
14724
  let command = executable;
14725
14725
  let commandArgs = args;
@@ -14739,7 +14739,7 @@ function runCliProbeAsync(executable, env, args = ["--version"], timeoutMs = 3e3
14739
14739
  stdio: closeStdin ? ["pipe", "pipe", "pipe"] : void 0
14740
14740
  });
14741
14741
  } catch (error61) {
14742
- resolve14({ status: null, stdout: "", stderr: "", error: error61 });
14742
+ resolve15({ status: null, stdout: "", stderr: "", error: error61 });
14743
14743
  return;
14744
14744
  }
14745
14745
  if (closeStdin) {
@@ -14753,10 +14753,10 @@ function runCliProbeAsync(executable, env, args = ["--version"], timeoutMs = 3e3
14753
14753
  child.stderr?.setEncoding("utf8").on("data", (chunk) => {
14754
14754
  stderr += chunk;
14755
14755
  });
14756
- child.once("error", (error61) => resolve14({ status: null, stdout, stderr, error: error61 }));
14756
+ child.once("error", (error61) => resolve15({ status: null, stdout, stderr, error: error61 }));
14757
14757
  child.once(
14758
14758
  "close",
14759
- (status, signal) => resolve14({ status, stdout, stderr, signal: signal ?? null })
14759
+ (status, signal) => resolve15({ status, stdout, stderr, signal: signal ?? null })
14760
14760
  );
14761
14761
  });
14762
14762
  }
@@ -14920,10 +14920,13 @@ Usage:
14920
14920
  alan-agent <command> [options]
14921
14921
 
14922
14922
  Commands:
14923
+ install Login/setup, install the daemon service, MCP, and supported hooks
14923
14924
  setup Register this computer with a setup token from Alan
14924
14925
  login Register this computer through browser authorization
14925
14926
  daemon Start the durable runtime daemon
14926
14927
  service Install, uninstall, or inspect the per-user daemon service
14928
+ integrations Reconcile, inspect, or uninstall Alan CLI integrations
14929
+ hook Internal: durably spool a native CLI hook event (fail-open)
14927
14930
  stop Stop the local daemon (or active runs only via the local API /stop)
14928
14931
  recover Redial and safely restart a stuck local daemon connection
14929
14932
  status Show local runtime configuration without secrets
@@ -14935,11 +14938,17 @@ Commands:
14935
14938
  run-session Internal: run one ephemeral sandbox/session from ALAN_* env vars
14936
14939
 
14937
14940
  Common flows:
14941
+ alan-agent install
14942
+ alan-agent install --setup-token <token>
14938
14943
  alan-agent login
14939
14944
  alan-agent login --cancel
14940
14945
  alan-agent setup --setup-token <token>
14941
14946
  alan-agent setup --setup-token <token> --scope user
14942
14947
  alan-agent service install
14948
+ alan-agent integrations reconcile
14949
+ alan-agent integrations status
14950
+ alan-agent integrations capabilities
14951
+ alan-agent integrations uninstall
14943
14952
  alan-agent stop
14944
14953
 
14945
14954
  Local development:
@@ -14963,7 +14972,7 @@ function resolveCommand(args, env) {
14963
14972
  }
14964
14973
 
14965
14974
  // src/daemon.ts
14966
- var import_node_crypto14 = require("crypto");
14975
+ var import_node_crypto17 = require("crypto");
14967
14976
  var import_node_os11 = require("os");
14968
14977
 
14969
14978
  // src/sleep-inhibitor.ts
@@ -14986,8 +14995,8 @@ var ChildProcessBackend = class {
14986
14995
  */
14987
14996
  unavailable = false;
14988
14997
  log;
14989
- constructor(log) {
14990
- this.log = log;
14998
+ constructor(log2) {
14999
+ this.log = log2;
14991
15000
  }
14992
15001
  /**
14993
15002
  * Let a multi-candidate backend react when its active helper terminates.
@@ -15120,14 +15129,14 @@ var WindowsPowerRequestBackend = class extends ChildProcessBackend {
15120
15129
  }
15121
15130
  };
15122
15131
  var externalBackends = /* @__PURE__ */ new Set();
15123
- function createPlatformBackends(log, hasExternalBackend) {
15132
+ function createPlatformBackends(log2, hasExternalBackend) {
15124
15133
  switch (process.platform) {
15125
15134
  case "darwin":
15126
- return [new MacCaffeinateBackend(log)];
15135
+ return [new MacCaffeinateBackend(log2)];
15127
15136
  case "linux":
15128
- return [new LinuxIdleInhibitBackend(log)];
15137
+ return [new LinuxIdleInhibitBackend(log2)];
15129
15138
  case "win32":
15130
- return hasExternalBackend ? [] : [new WindowsPowerRequestBackend(log)];
15139
+ return hasExternalBackend ? [] : [new WindowsPowerRequestBackend(log2)];
15131
15140
  default:
15132
15141
  return [];
15133
15142
  }
@@ -15231,7 +15240,7 @@ function describeError(error61) {
15231
15240
  }
15232
15241
 
15233
15242
  // src/version.ts
15234
- var AGENT_VERSION = "0.1.77";
15243
+ var AGENT_VERSION = "0.1.78";
15235
15244
 
15236
15245
  // src/daemon-worktree.ts
15237
15246
  var import_node_child_process3 = require("child_process");
@@ -16513,8 +16522,8 @@ async function withTimeout(promise2, timeoutMs, fallback) {
16513
16522
  try {
16514
16523
  return await Promise.race([
16515
16524
  promise2,
16516
- new Promise((resolve14) => {
16517
- timer = setTimeout(() => resolve14(fallback), timeoutMs);
16525
+ new Promise((resolve15) => {
16526
+ timer = setTimeout(() => resolve15(fallback), timeoutMs);
16518
16527
  })
16519
16528
  ]);
16520
16529
  } finally {
@@ -16527,7 +16536,7 @@ async function collectCodexLimits(env) {
16527
16536
  const codexCommand = resolveExecutable("codex", runtimeEnv, "ALAN_CODEX_PATH");
16528
16537
  if (!codexCommand)
16529
16538
  return [];
16530
- return withTimeout(new Promise((resolve14) => {
16539
+ return withTimeout(new Promise((resolve15) => {
16531
16540
  const child = (0, import_node_child_process4.spawn)(codexCommand, ["-s", "read-only", "-a", "untrusted", "app-server"], {
16532
16541
  env: runtimeEnv,
16533
16542
  stdio: ["pipe", "pipe", "ignore"]
@@ -16539,7 +16548,7 @@ async function collectCodexLimits(env) {
16539
16548
  pending.clear();
16540
16549
  if (!child.killed)
16541
16550
  child.kill();
16542
- resolve14(windows);
16551
+ resolve15(windows);
16543
16552
  };
16544
16553
  const request = (method, params = {}) => {
16545
16554
  const id = nextId++;
@@ -16686,7 +16695,7 @@ function antigravityWindow(groupLabel, raw) {
16686
16695
  };
16687
16696
  }
16688
16697
  function runOneShotCli(command, args, env, timeoutMs) {
16689
- return new Promise((resolve14) => {
16698
+ return new Promise((resolve15) => {
16690
16699
  const child = (0, import_node_child_process4.spawn)(command, args, { env, stdio: ["ignore", "pipe", "ignore"] });
16691
16700
  let stdout = "";
16692
16701
  let settled = false;
@@ -16695,7 +16704,7 @@ function runOneShotCli(command, args, env, timeoutMs) {
16695
16704
  return;
16696
16705
  settled = true;
16697
16706
  clearTimeout(timer);
16698
- resolve14(value2);
16707
+ resolve15(value2);
16699
16708
  };
16700
16709
  const timer = setTimeout(() => {
16701
16710
  if (!child.killed)
@@ -35745,7 +35754,18 @@ function resolveEndpointProfileFromArgs(args) {
35745
35754
  const raw = index === -1 ? process.env.ALAN_AGENT_PROFILE : args[index + 1];
35746
35755
  return normalizeAgentEndpointProfile(raw) ?? "production";
35747
35756
  }
35757
+ function explicitConfigPathFromArgs(args) {
35758
+ const index = args.indexOf("--config-path");
35759
+ if (index >= 0) return args[index + 1]?.trim() || void 0;
35760
+ const prefix = "--config-path=";
35761
+ return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length).trim() || void 0;
35762
+ }
35748
35763
  function bindAgentConfigPathFromArgs(args) {
35764
+ const argumentPath = explicitConfigPathFromArgs(args);
35765
+ if (argumentPath) {
35766
+ process.env.ALAN_AGENT_CONFIG_PATH = argumentPath;
35767
+ return argumentPath;
35768
+ }
35749
35769
  const explicit = process.env.ALAN_AGENT_CONFIG_PATH?.trim();
35750
35770
  if (explicit) return explicit;
35751
35771
  const profile = resolveEndpointProfileFromArgs(args);
@@ -49816,7 +49836,7 @@ function checkStoredEndpointConsistency(stored, endpointDefaults) {
49816
49836
  }
49817
49837
  function sleep(ms) {
49818
49838
  if (ms <= 0) return Promise.resolve();
49819
- return new Promise((resolve14) => setTimeout(resolve14, ms));
49839
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
49820
49840
  }
49821
49841
  function scheduleRuntimeModelScan(getBaseMetadata, options) {
49822
49842
  const stored = readConfig();
@@ -49837,11 +49857,11 @@ function scheduleRuntimeModelScan(getBaseMetadata, options) {
49837
49857
  }
49838
49858
 
49839
49859
  // src/daemon-start.ts
49840
- var import_node_crypto13 = require("crypto");
49841
- var import_node_fs17 = require("fs");
49860
+ var import_node_crypto16 = require("crypto");
49861
+ var import_node_fs20 = require("fs");
49842
49862
  var import_node_http = __toESM(require("http"), 1);
49843
49863
  var import_node_os10 = require("os");
49844
- var import_node_path20 = require("path");
49864
+ var import_node_path22 = require("path");
49845
49865
 
49846
49866
  // ../../node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.node.js
49847
49867
  var XMLHttpRequestModule = __toESM(require_XMLHttpRequest(), 1);
@@ -52397,9 +52417,9 @@ var Socket2 = class extends Emitter {
52397
52417
  * @return a Promise that will be fulfilled when the server acknowledges the event
52398
52418
  */
52399
52419
  emitWithAck(ev, ...args) {
52400
- return new Promise((resolve14, reject) => {
52420
+ return new Promise((resolve15, reject) => {
52401
52421
  const fn = (arg1, arg2) => {
52402
- return arg1 ? reject(arg1) : resolve14(arg2);
52422
+ return arg1 ? reject(arg1) : resolve15(arg2);
52403
52423
  };
52404
52424
  fn.withError = true;
52405
52425
  args.push(fn);
@@ -54564,7 +54584,7 @@ function versionPolicy(npmPackage) {
54564
54584
  return "latest";
54565
54585
  }
54566
54586
  function runNpmInstall(installSpec, env, timeoutMs) {
54567
- return new Promise((resolve14) => {
54587
+ return new Promise((resolve15) => {
54568
54588
  const isWin = (0, import_node_os7.platform)() === "win32";
54569
54589
  let child;
54570
54590
  try {
@@ -54576,7 +54596,7 @@ function runNpmInstall(installSpec, env, timeoutMs) {
54576
54596
  stdio: ["ignore", "pipe", "pipe"]
54577
54597
  });
54578
54598
  } catch (error61) {
54579
- resolve14({ status: null, stdout: "", stderr: "", error: error61 });
54599
+ resolve15({ status: null, stdout: "", stderr: "", error: error61 });
54580
54600
  return;
54581
54601
  }
54582
54602
  let stdout = "";
@@ -54587,10 +54607,10 @@ function runNpmInstall(installSpec, env, timeoutMs) {
54587
54607
  child.stderr?.setEncoding("utf8").on("data", (chunk) => {
54588
54608
  stderr += chunk;
54589
54609
  });
54590
- child.once("error", (error61) => resolve14({ status: null, stdout, stderr, error: error61 }));
54610
+ child.once("error", (error61) => resolve15({ status: null, stdout, stderr, error: error61 }));
54591
54611
  child.once(
54592
54612
  "close",
54593
- (status, signal) => resolve14({
54613
+ (status, signal) => resolve15({
54594
54614
  status,
54595
54615
  stdout,
54596
54616
  stderr,
@@ -55333,6 +55353,147 @@ function parse5(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
55333
55353
  return res;
55334
55354
  }
55335
55355
 
55356
+ // ../../node_modules/smol-toml/dist/stringify.js
55357
+ var BARE_KEY = /^[a-z0-9-_]+$/i;
55358
+ function extendedTypeOf(obj) {
55359
+ let type = typeof obj;
55360
+ if (type === "object") {
55361
+ if (Array.isArray(obj))
55362
+ return "array";
55363
+ if (obj instanceof Date)
55364
+ return "date";
55365
+ }
55366
+ return type;
55367
+ }
55368
+ function isArrayOfTables(obj) {
55369
+ for (let i = 0; i < obj.length; i++) {
55370
+ if (extendedTypeOf(obj[i]) !== "object")
55371
+ return false;
55372
+ }
55373
+ return obj.length != 0;
55374
+ }
55375
+ function formatString(s) {
55376
+ return JSON.stringify(s).replace(/\x7f/g, "\\u007f");
55377
+ }
55378
+ function stringifyValue(val, type, depth, numberAsFloat) {
55379
+ if (depth === 0) {
55380
+ throw new Error("Could not stringify the object: maximum object depth exceeded");
55381
+ }
55382
+ if (type === "number") {
55383
+ if (isNaN(val))
55384
+ return "nan";
55385
+ if (val === Infinity)
55386
+ return "inf";
55387
+ if (val === -Infinity)
55388
+ return "-inf";
55389
+ if (numberAsFloat && Number.isInteger(val))
55390
+ return val.toFixed(1);
55391
+ return val.toString();
55392
+ }
55393
+ if (type === "bigint" || type === "boolean") {
55394
+ return val.toString();
55395
+ }
55396
+ if (type === "string") {
55397
+ return formatString(val);
55398
+ }
55399
+ if (type === "date") {
55400
+ if (isNaN(val.getTime())) {
55401
+ throw new TypeError("cannot serialize invalid date");
55402
+ }
55403
+ return val.toISOString();
55404
+ }
55405
+ if (type === "object") {
55406
+ return stringifyInlineTable(val, depth, numberAsFloat);
55407
+ }
55408
+ if (type === "array") {
55409
+ return stringifyArray(val, depth, numberAsFloat);
55410
+ }
55411
+ }
55412
+ function stringifyInlineTable(obj, depth, numberAsFloat) {
55413
+ let keys = Object.keys(obj);
55414
+ if (keys.length === 0)
55415
+ return "{}";
55416
+ let res = "{ ";
55417
+ for (let i = 0; i < keys.length; i++) {
55418
+ let k = keys[i];
55419
+ if (i)
55420
+ res += ", ";
55421
+ res += BARE_KEY.test(k) ? k : formatString(k);
55422
+ res += " = ";
55423
+ res += stringifyValue(obj[k], extendedTypeOf(obj[k]), depth - 1, numberAsFloat);
55424
+ }
55425
+ return res + " }";
55426
+ }
55427
+ function stringifyArray(array2, depth, numberAsFloat) {
55428
+ if (array2.length === 0)
55429
+ return "[]";
55430
+ let res = "[ ";
55431
+ for (let i = 0; i < array2.length; i++) {
55432
+ if (i)
55433
+ res += ", ";
55434
+ if (array2[i] === null || array2[i] === void 0) {
55435
+ throw new TypeError("arrays cannot contain null or undefined values");
55436
+ }
55437
+ res += stringifyValue(array2[i], extendedTypeOf(array2[i]), depth - 1, numberAsFloat);
55438
+ }
55439
+ return res + " ]";
55440
+ }
55441
+ function stringifyArrayTable(array2, key, depth, numberAsFloat) {
55442
+ if (depth === 0) {
55443
+ throw new Error("Could not stringify the object: maximum object depth exceeded");
55444
+ }
55445
+ let res = "";
55446
+ for (let i = 0; i < array2.length; i++) {
55447
+ res += `${res && "\n"}[[${key}]]
55448
+ `;
55449
+ res += stringifyTable(0, array2[i], key, depth, numberAsFloat);
55450
+ }
55451
+ return res;
55452
+ }
55453
+ function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat) {
55454
+ if (depth === 0) {
55455
+ throw new Error("Could not stringify the object: maximum object depth exceeded");
55456
+ }
55457
+ let preamble = "";
55458
+ let tables = "";
55459
+ let keys = Object.keys(obj);
55460
+ for (let i = 0; i < keys.length; i++) {
55461
+ let k = keys[i];
55462
+ if (obj[k] !== null && obj[k] !== void 0) {
55463
+ let type = extendedTypeOf(obj[k]);
55464
+ if (type === "symbol" || type === "function") {
55465
+ throw new TypeError(`cannot serialize values of type '${type}'`);
55466
+ }
55467
+ let key = BARE_KEY.test(k) ? k : formatString(k);
55468
+ if (type === "array" && isArrayOfTables(obj[k])) {
55469
+ tables += (tables && "\n") + stringifyArrayTable(obj[k], prefix ? `${prefix}.${key}` : key, depth - 1, numberAsFloat);
55470
+ } else if (type === "object") {
55471
+ let tblKey = prefix ? `${prefix}.${key}` : key;
55472
+ tables += (tables && "\n") + stringifyTable(tblKey, obj[k], tblKey, depth - 1, numberAsFloat);
55473
+ } else {
55474
+ preamble += key;
55475
+ preamble += " = ";
55476
+ preamble += stringifyValue(obj[k], type, depth, numberAsFloat);
55477
+ preamble += "\n";
55478
+ }
55479
+ }
55480
+ }
55481
+ if (tableKey && (preamble || !tables))
55482
+ preamble = preamble ? `[${tableKey}]
55483
+ ${preamble}` : `[${tableKey}]`;
55484
+ return preamble && tables ? `${preamble}
55485
+ ${tables}` : preamble || tables;
55486
+ }
55487
+ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
55488
+ if (extendedTypeOf(obj) !== "object") {
55489
+ throw new TypeError("stringify can only be called with an object");
55490
+ }
55491
+ let str = stringifyTable(0, obj, "", maxDepth, numbersAsFloat);
55492
+ if (str[str.length - 1] !== "\n")
55493
+ return str + "\n";
55494
+ return str;
55495
+ }
55496
+
55336
55497
  // src/mcp-registration.ts
55337
55498
  var CONFIG_LOCK_STALE_MS = 3e4;
55338
55499
  var CONFIG_LOCK_WAIT_MS = 2e3;
@@ -55461,7 +55622,7 @@ async function waitForConfigLock(lockPath) {
55461
55622
  if (Date.now() - startedAt >= CONFIG_LOCK_WAIT_MS) {
55462
55623
  throw new Error(`Alan could not safely update ${lockPath.slice(0, -".alan-lock".length)}`);
55463
55624
  }
55464
- await new Promise((resolve14) => setTimeout(resolve14, CONFIG_LOCK_POLL_MS));
55625
+ await new Promise((resolve15) => setTimeout(resolve15, CONFIG_LOCK_POLL_MS));
55465
55626
  }
55466
55627
  }
55467
55628
  }
@@ -55607,6 +55768,22 @@ async function ensureAlanMcpRegistered(backendKind, registration, home = (0, imp
55607
55768
  return { ok: false, paths: [], error: err instanceof Error ? err.message : String(err) };
55608
55769
  }
55609
55770
  }
55771
+ async function restoreStaticAlanMcp(backendKind, apiUrl, home = (0, import_node_os8.homedir)()) {
55772
+ let url3;
55773
+ try {
55774
+ url3 = new URL("/mcp", apiUrl).toString();
55775
+ } catch (err) {
55776
+ return {
55777
+ ok: false,
55778
+ paths: [],
55779
+ error: err instanceof Error ? err.message : String(err)
55780
+ };
55781
+ }
55782
+ return ensureAlanMcpRegistered(backendKind, { url: url3, headers: {} }, home, () => ({
55783
+ ok: true,
55784
+ verification: { method: "file" }
55785
+ }));
55786
+ }
55610
55787
  async function verifyAlanMcpRegistered(backendKind, home = (0, import_node_os8.homedir)(), inspectCli = inspectAlanMcpThroughProviderCli) {
55611
55788
  const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
55612
55789
  const missing = files.map((f) => f.path).filter((p) => !(0, import_node_fs14.existsSync)(p));
@@ -55678,51 +55855,6 @@ async function verifyAlanMcpRegistered(backendKind, home = (0, import_node_os8.h
55678
55855
  providerLoadStatus: providerLoadStatusForBackend(backendKind)
55679
55856
  };
55680
55857
  }
55681
- function removeAlanFromJsonConfig(path2, existingContent) {
55682
- const config2 = parseJsonObject2(path2, existingContent);
55683
- for (const containerKey of ["mcpServers", "mcp"]) {
55684
- const container = config2[containerKey];
55685
- if (container && typeof container === "object" && !Array.isArray(container)) {
55686
- delete container.alan;
55687
- }
55688
- }
55689
- return JSON.stringify(config2);
55690
- }
55691
- async function unregisterAlanMcp(backendKind, home = (0, import_node_os8.homedir)(), options = {}) {
55692
- const removedPaths = [];
55693
- const errors = [];
55694
- const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
55695
- for (const file2 of files) {
55696
- if (!(0, import_node_fs14.existsSync)(file2.path)) continue;
55697
- const releaseLock = await waitForConfigLock(`${file2.path}.alan-lock`);
55698
- try {
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
- }
55706
- let next;
55707
- if (file2.path.endsWith(".toml")) {
55708
- const stripped = stripCodexAlanMcpSection(current);
55709
- next = stripped ? `${stripped}
55710
- ` : "";
55711
- } else {
55712
- next = removeAlanFromJsonConfig(file2.path, current);
55713
- }
55714
- if (next !== current) {
55715
- atomicWriteManagedConfig(file2.path, next);
55716
- removedPaths.push(file2.path);
55717
- }
55718
- } catch (err) {
55719
- errors.push(err instanceof Error ? err.message : String(err));
55720
- } finally {
55721
- releaseLock();
55722
- }
55723
- }
55724
- return { removedPaths, errors };
55725
- }
55726
55858
  function isHttpUrl(value2) {
55727
55859
  if (typeof value2 !== "string") return false;
55728
55860
  try {
@@ -55814,12 +55946,14 @@ function parseAlanMcpRegistration(path2, content) {
55814
55946
  var CoreAgent = class _CoreAgent extends BaseMachineAgent {
55815
55947
  childProcess = null;
55816
55948
  _aborted = false;
55949
+ runId;
55817
55950
  providerApiKey;
55818
55951
  onProviderSpawned;
55819
55952
  /** How long to wait after SIGTERM before escalating to SIGKILL. */
55820
55953
  static SIGKILL_DELAY_MS = 3e3;
55821
55954
  constructor(presenter, runtime) {
55822
55955
  super(presenter, runtime);
55956
+ this.runId = runtime.runId?.trim() || void 0;
55823
55957
  this.providerApiKey = runtime.providerApiKey;
55824
55958
  this.onProviderSpawned = runtime.onProviderSpawned;
55825
55959
  }
@@ -55950,6 +56084,9 @@ var CoreAgent = class _CoreAgent extends BaseMachineAgent {
55950
56084
  }
55951
56085
  async buildCliEnvironment() {
55952
56086
  const augmented = { ...getDaemonCliEnvironment() };
56087
+ augmented.ALAN_MANAGED_RUN = "1";
56088
+ if (this.runId) augmented.ALAN_RUN_ID = this.runId;
56089
+ else delete augmented.ALAN_RUN_ID;
55953
56090
  const backendKind = this.runtime.backendKind ?? "";
55954
56091
  const providerApiKey = this.providerApiKey ?? "";
55955
56092
  if (backendKind === "antigravity_cli" && providerApiKey.trim()) {
@@ -60493,14 +60630,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
60493
60630
  return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
60494
60631
  }
60495
60632
  function sleep2(ms, signal) {
60496
- return new Promise((resolve14, reject) => {
60633
+ return new Promise((resolve15, reject) => {
60497
60634
  if (signal?.aborted) {
60498
60635
  reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
60499
60636
  return;
60500
60637
  }
60501
60638
  const timer = setTimeout(() => {
60502
60639
  signal?.removeEventListener("abort", onAbort);
60503
- resolve14();
60640
+ resolve15();
60504
60641
  }, ms);
60505
60642
  const onAbort = () => {
60506
60643
  clearTimeout(timer);
@@ -61339,7 +61476,7 @@ var Protocol = class {
61339
61476
  const flowStartedAt = Date.now();
61340
61477
  let onAbort;
61341
61478
  let cleanupMessageId;
61342
- return new Promise((resolve14, reject) => {
61479
+ return new Promise((resolve15, reject) => {
61343
61480
  const earlyReject = (error61) => {
61344
61481
  reject(error61);
61345
61482
  };
@@ -61407,7 +61544,7 @@ var Protocol = class {
61407
61544
  }
61408
61545
  if (decoded.kind === "invalid") return reject(decoded.error);
61409
61546
  if (decoded.kind === "input_required") {
61410
- if (options?.allowInputRequired === true) return resolve14(manualInputRequiredValue(decoded));
61547
+ if (options?.allowInputRequired === true) return resolve15(manualInputRequiredValue(decoded));
61411
61548
  const flow = {
61412
61549
  codec: codec2,
61413
61550
  request,
@@ -61419,11 +61556,11 @@ var Protocol = class {
61419
61556
  params
61420
61557
  }, resultSchema, legOptions)
61421
61558
  };
61422
- return resolve14(this._resolveNonCompleteResult(decoded, flow));
61559
+ return resolve15(this._resolveNonCompleteResult(decoded, flow));
61423
61560
  }
61424
61561
  const result = decoded.result;
61425
61562
  validateStandardSchema(resultSchema, result).then((parseResult) => {
61426
- if (parseResult.success) resolve14(parseResult.data);
61563
+ if (parseResult.success) resolve15(parseResult.data);
61427
61564
  else reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
61428
61565
  }, reject);
61429
61566
  });
@@ -64337,7 +64474,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports2) => {
64337
64474
  ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
64338
64475
  const schOrFunc = root.refs[ref];
64339
64476
  if (schOrFunc) return schOrFunc;
64340
- let _sch = resolve14.call(this, root, ref);
64477
+ let _sch = resolve15.call(this, root, ref);
64341
64478
  if (_sch === void 0) {
64342
64479
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
64343
64480
  const { schemaId } = this.opts;
@@ -64363,7 +64500,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports2) => {
64363
64500
  function sameSchemaEnv(s1, s2) {
64364
64501
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
64365
64502
  }
64366
- function resolve14(root, ref) {
64503
+ function resolve15(root, ref) {
64367
64504
  let sch;
64368
64505
  while (typeof (sch = this.refs[ref]) == "string") ref = sch;
64369
64506
  return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
@@ -64813,7 +64950,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports2, module2) => {
64813
64950
  else if (typeof uri === "object") uri = parse7(serialize(uri, options), options);
64814
64951
  return uri;
64815
64952
  }
64816
- function resolve14(baseURI, relativeURI, options) {
64953
+ function resolve15(baseURI, relativeURI, options) {
64817
64954
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
64818
64955
  const resolved = resolveComponent(parse7(baseURI, schemelessOptions), parse7(relativeURI, schemelessOptions), schemelessOptions, true);
64819
64956
  schemelessOptions.skipEscape = true;
@@ -64987,7 +65124,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports2, module2) => {
64987
65124
  const fastUri = {
64988
65125
  SCHEMES,
64989
65126
  normalize,
64990
- resolve: resolve14,
65127
+ resolve: resolve15,
64991
65128
  resolveComponent,
64992
65129
  equal,
64993
65130
  serialize,
@@ -65463,10 +65600,10 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports2) => {
65463
65600
  Ajv2.ValidationError = validation_error_1.default;
65464
65601
  Ajv2.MissingRefError = ref_error_1.default;
65465
65602
  exports2.default = Ajv2;
65466
- function checkOptions(checkOpts, options, msg, log = "error") {
65603
+ function checkOptions(checkOpts, options, msg, log2 = "error") {
65467
65604
  for (const key in checkOpts) {
65468
65605
  const opt = key;
65469
- if (opt in options) this.logger[log](`${msg}: option ${key}. ${checkOpts[opt]}`);
65606
+ if (opt in options) this.logger[log2](`${msg}: option ${key}. ${checkOpts[opt]}`);
65470
65607
  }
65471
65608
  }
65472
65609
  function getSchEnv(keyRef) {
@@ -68790,8 +68927,8 @@ async function random(size) {
68790
68927
  const evenDistCutoff = Math.pow(2, 8) - Math.pow(2, 8) % mask.length;
68791
68928
  let result = "";
68792
68929
  while (result.length < size) {
68793
- const randomBytes10 = await getRandomValues(size - result.length);
68794
- for (const randomByte of randomBytes10) {
68930
+ const randomBytes13 = await getRandomValues(size - result.length);
68931
+ for (const randomByte of randomBytes13) {
68795
68932
  if (randomByte < evenDistCutoff) {
68796
68933
  result += mask[randomByte % mask.length];
68797
68934
  }
@@ -70429,14 +70566,14 @@ var ProbeWindow = class ProbeWindow2 {
70429
70566
  */
70430
70567
  async exchange(buildRequest, timeoutMs) {
70431
70568
  const id = `server-discover-probe-${++this._probeCounter}`;
70432
- return new Promise((resolve14) => {
70569
+ return new Promise((resolve15) => {
70433
70570
  let settled = false;
70434
70571
  const settle = (reply) => {
70435
70572
  if (settled) return;
70436
70573
  settled = true;
70437
70574
  clearTimeout(timer);
70438
70575
  if (this._pending?.id === id) this._pending = void 0;
70439
- resolve14(reply);
70576
+ resolve15(reply);
70440
70577
  };
70441
70578
  const timer = setTimeout(() => settle({ kind: "timeout" }), timeoutMs);
70442
70579
  this._pending = {
@@ -71642,13 +71779,13 @@ var Client = class extends Protocol {
71642
71779
  let onCallerAbort;
71643
71780
  let resolveOpening;
71644
71781
  let rejectOpening;
71645
- const opening = new Promise((resolve14, reject) => {
71646
- resolveOpening = resolve14;
71782
+ const opening = new Promise((resolve15, reject) => {
71783
+ resolveOpening = resolve15;
71647
71784
  rejectOpening = reject;
71648
71785
  });
71649
71786
  let resolveClosed;
71650
- const closed = new Promise((resolve14) => {
71651
- resolveClosed = resolve14;
71787
+ const closed = new Promise((resolve15) => {
71788
+ resolveClosed = resolve15;
71652
71789
  });
71653
71790
  const settle = (outcome) => {
71654
71791
  if (state === "closed") return;
@@ -72707,7 +72844,7 @@ function failureFromError(error61, paths) {
72707
72844
  };
72708
72845
  }
72709
72846
  async function waitBeforeRetry() {
72710
- await new Promise((resolve14) => setTimeout(resolve14, RETRY_DELAY_MS2));
72847
+ await new Promise((resolve15) => setTimeout(resolve15, RETRY_DELAY_MS2));
72711
72848
  }
72712
72849
  async function probeAlanMcpReadiness(registration, options = {}) {
72713
72850
  const paths = options.paths ?? [];
@@ -73329,6 +73466,7 @@ async function handleAgentExecute(ctx, payload) {
73329
73466
  agent = new CoreAgent(presenter, {
73330
73467
  backendKind,
73331
73468
  runtimeCommand,
73469
+ runId: payload.runId,
73332
73470
  providerApiKey: payload.providerApiKey,
73333
73471
  onProviderSpawned: (pid) => {
73334
73472
  releaseRunStart();
@@ -73726,6 +73864,473 @@ function respondWithLocalSkill(res, operation) {
73726
73864
  );
73727
73865
  }
73728
73866
 
73867
+ // src/native-hook-drain.ts
73868
+ var PermanentNativeHookDeliveryError = class extends Error {
73869
+ constructor(message) {
73870
+ super(message);
73871
+ this.name = "PermanentNativeHookDeliveryError";
73872
+ }
73873
+ };
73874
+ async function drainNativeHookInbox(input2) {
73875
+ return input2.inbox.drain(async (envelope) => {
73876
+ const isStart = envelope.eventType === "session_start" || envelope.eventType === "subagent_start";
73877
+ const isTerminal2 = envelope.eventType === "stop" || envelope.eventType === "session_end" || envelope.eventType === "subagent_end";
73878
+ const streamTranscript = async () => {
73879
+ if (isStart || !input2.transcriptStreamer || !envelope.transcriptPath) {
73880
+ return "unavailable";
73881
+ }
73882
+ try {
73883
+ const streamed = await input2.transcriptStreamer.stream(
73884
+ envelope,
73885
+ (event) => input2.sink.deliver(event)
73886
+ );
73887
+ return streamed.complete ? streamed.assistantOutputDelivered ? "replayed" : "unavailable" : "deferred";
73888
+ } catch (error61) {
73889
+ if (error61 instanceof PermanentNativeHookDeliveryError) throw error61;
73890
+ const normalizedError = error61 instanceof Error ? error61 : new Error(String(error61));
73891
+ input2.onTranscriptError?.({
73892
+ envelope,
73893
+ error: normalizedError
73894
+ });
73895
+ if (isMissingTranscriptError(normalizedError) && transcriptGraceExpired(
73896
+ envelope.observedAt,
73897
+ input2.now?.() ?? Date.now(),
73898
+ input2.missingTranscriptGraceMs ?? 1e4
73899
+ )) {
73900
+ return "unavailable";
73901
+ }
73902
+ return "deferred";
73903
+ }
73904
+ };
73905
+ if (isTerminal2) {
73906
+ const transcriptReplay = await streamTranscript();
73907
+ if (transcriptReplay === "deferred") return false;
73908
+ return input2.sink.deliver(
73909
+ transcriptReplay === "replayed" ? { ...envelope, assistantOutputReplayed: true } : envelope
73910
+ );
73911
+ }
73912
+ if (!await input2.sink.deliver(envelope)) return false;
73913
+ return await streamTranscript() !== "deferred";
73914
+ }, input2.limit);
73915
+ }
73916
+ function isMissingTranscriptError(error61) {
73917
+ return error61.code === "ENOENT";
73918
+ }
73919
+ function transcriptGraceExpired(observedAt, now, graceMs) {
73920
+ const observedAtMs = Date.parse(observedAt);
73921
+ if (!Number.isFinite(observedAtMs)) return true;
73922
+ return now - observedAtMs >= Math.max(0, graceMs);
73923
+ }
73924
+
73925
+ // src/native-hook-inbox.ts
73926
+ var import_node_crypto8 = require("crypto");
73927
+ var import_node_fs16 = require("fs");
73928
+ var import_node_path14 = require("path");
73929
+ var INBOX_FILE_VERSION = 1;
73930
+ var DEFAULT_MAX_BYTES = 64 * 1024 * 1024;
73931
+ var DEFAULT_MAX_ENTRIES = 1e4;
73932
+ function decodeKey2(encodedKey) {
73933
+ const key = Buffer.from(encodedKey, "base64url");
73934
+ if (key.length !== 32) throw new Error("native hook inbox key must contain exactly 32 bytes");
73935
+ return key;
73936
+ }
73937
+ function encryptedEntryNames(path2) {
73938
+ if (!(0, import_node_fs16.existsSync)(path2)) return [];
73939
+ return (0, import_node_fs16.readdirSync)(path2).filter((name) => name.endsWith(".event") || name.includes(".processing-")).sort();
73940
+ }
73941
+ var EncryptedNativeHookInbox = class {
73942
+ constructor(path2, encodedKey, options = {}) {
73943
+ this.path = path2;
73944
+ this.key = decodeKey2(encodedKey);
73945
+ this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
73946
+ this.maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
73947
+ this.onQuarantine = options.onQuarantine;
73948
+ if (!Number.isSafeInteger(this.maxBytes) || this.maxBytes <= 0) {
73949
+ throw new Error("native hook inbox maxBytes must be a positive integer");
73950
+ }
73951
+ if (!Number.isSafeInteger(this.maxEntries) || this.maxEntries <= 0) {
73952
+ throw new Error("native hook inbox maxEntries must be a positive integer");
73953
+ }
73954
+ (0, import_node_fs16.mkdirSync)(this.path, { recursive: true, mode: 448 });
73955
+ (0, import_node_fs16.chmodSync)(this.path, 448);
73956
+ if (options.recoverClaims) this.recoverClaims();
73957
+ }
73958
+ key;
73959
+ maxBytes;
73960
+ maxEntries;
73961
+ onQuarantine;
73962
+ enqueue(envelope) {
73963
+ const plaintext = Buffer.from(JSON.stringify(envelope), "utf8");
73964
+ const iv = (0, import_node_crypto8.randomBytes)(12);
73965
+ const cipher = (0, import_node_crypto8.createCipheriv)("aes-256-gcm", this.key, iv);
73966
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
73967
+ const file2 = {
73968
+ version: INBOX_FILE_VERSION,
73969
+ iv: iv.toString("base64url"),
73970
+ authTag: cipher.getAuthTag().toString("base64url"),
73971
+ ciphertext: ciphertext.toString("base64url")
73972
+ };
73973
+ const monotonic = process.hrtime.bigint().toString().padStart(20, "0");
73974
+ const prefix = `${String(Date.now()).padStart(13, "0")}-${monotonic}-${envelope.eventId}`;
73975
+ const temporaryPath = (0, import_node_path14.join)(this.path, `.${prefix}-${(0, import_node_crypto8.randomUUID)()}.tmp`);
73976
+ const finalPath = (0, import_node_path14.join)(this.path, `${prefix}.event`);
73977
+ (0, import_node_fs16.writeFileSync)(temporaryPath, JSON.stringify(file2), { mode: 384, flag: "wx" });
73978
+ (0, import_node_fs16.chmodSync)(temporaryPath, 384);
73979
+ (0, import_node_fs16.renameSync)(temporaryPath, finalPath);
73980
+ try {
73981
+ this.enforceBounds();
73982
+ } catch (error61) {
73983
+ (0, import_node_fs16.rmSync)(finalPath, { force: true });
73984
+ throw error61;
73985
+ }
73986
+ }
73987
+ size() {
73988
+ return encryptedEntryNames(this.path).length;
73989
+ }
73990
+ async drain(consume, limit = 100) {
73991
+ const result = { attempted: 0, acknowledged: 0, deferred: 0 };
73992
+ const names = encryptedEntryNames(this.path).filter((name) => name.endsWith(".event")).slice(0, Math.max(0, limit));
73993
+ for (const name of names) {
73994
+ const sourcePath = (0, import_node_path14.join)(this.path, name);
73995
+ const claimedName = `${name.slice(0, -".event".length)}.processing-${(0, import_node_crypto8.randomUUID)()}`;
73996
+ const claimedPath = (0, import_node_path14.join)(this.path, claimedName);
73997
+ try {
73998
+ (0, import_node_fs16.renameSync)(sourcePath, claimedPath);
73999
+ } catch {
74000
+ continue;
74001
+ }
74002
+ result.attempted += 1;
74003
+ try {
74004
+ const envelope = this.readEntry(claimedPath);
74005
+ if (await consume(envelope)) {
74006
+ (0, import_node_fs16.rmSync)(claimedPath, { force: true });
74007
+ result.acknowledged += 1;
74008
+ } else {
74009
+ (0, import_node_fs16.renameSync)(claimedPath, sourcePath);
74010
+ result.deferred += 1;
74011
+ break;
74012
+ }
74013
+ } catch (error61) {
74014
+ if ((0, import_node_fs16.existsSync)(claimedPath)) {
74015
+ const quarantinedPath = `${claimedPath}.quarantine`;
74016
+ (0, import_node_fs16.renameSync)(claimedPath, quarantinedPath);
74017
+ this.onQuarantine?.({
74018
+ fileName: quarantinedPath.slice(quarantinedPath.lastIndexOf("/") + 1),
74019
+ error: error61 instanceof Error ? error61 : new Error(String(error61))
74020
+ });
74021
+ }
74022
+ result.deferred += 1;
74023
+ }
74024
+ }
74025
+ return result;
74026
+ }
74027
+ readEntry(path2) {
74028
+ const file2 = JSON.parse((0, import_node_fs16.readFileSync)(path2, "utf8"));
74029
+ if (file2.version !== INBOX_FILE_VERSION) throw new Error("unsupported native hook inbox entry");
74030
+ const decipher = (0, import_node_crypto8.createDecipheriv)("aes-256-gcm", this.key, Buffer.from(file2.iv, "base64url"));
74031
+ decipher.setAuthTag(Buffer.from(file2.authTag, "base64url"));
74032
+ const plaintext = Buffer.concat([
74033
+ decipher.update(Buffer.from(file2.ciphertext, "base64url")),
74034
+ decipher.final()
74035
+ ]);
74036
+ return JSON.parse(plaintext.toString("utf8"));
74037
+ }
74038
+ recoverClaims() {
74039
+ for (const name of encryptedEntryNames(this.path)) {
74040
+ if (name.endsWith(".quarantine")) continue;
74041
+ const marker = name.indexOf(".processing-");
74042
+ if (marker === -1) continue;
74043
+ const claimedPath = (0, import_node_path14.join)(this.path, name);
74044
+ const eventPath = (0, import_node_path14.join)(this.path, `${name.slice(0, marker)}.event`);
74045
+ if ((0, import_node_fs16.existsSync)(eventPath)) (0, import_node_fs16.rmSync)(claimedPath, { force: true });
74046
+ else (0, import_node_fs16.renameSync)(claimedPath, eventPath);
74047
+ }
74048
+ }
74049
+ enforceBounds() {
74050
+ const entries = encryptedEntryNames(this.path).filter((name) => name.endsWith(".event")).map((name) => ({ name, bytes: (0, import_node_fs16.statSync)((0, import_node_path14.join)(this.path, name)).size }));
74051
+ const totalBytes = entries.reduce((sum, entry) => sum + entry.bytes, 0);
74052
+ if (entries.length > this.maxEntries || totalBytes > this.maxBytes) {
74053
+ throw new Error(
74054
+ `native hook inbox capacity exceeded (${entries.length} entries, ${totalBytes} bytes)`
74055
+ );
74056
+ }
74057
+ }
74058
+ };
74059
+ function nativeHookInboxPath(configPath) {
74060
+ return (0, import_node_path14.join)((0, import_node_path14.dirname)(configPath), "native-hook-inbox");
74061
+ }
74062
+
74063
+ // src/native-transcript-cursor.ts
74064
+ var import_node_crypto9 = require("crypto");
74065
+ var import_node_fs17 = require("fs");
74066
+ var import_node_path15 = require("path");
74067
+ var CURSOR_FILE_VERSION = 1;
74068
+ function nativeTranscriptCursorKey(input2) {
74069
+ return (0, import_node_crypto9.createHash)("sha256").update(`${input2.provider}\0${input2.externalSessionId}\0${input2.transcriptPath}`).digest("base64url");
74070
+ }
74071
+ var NativeTranscriptCursorStore = class {
74072
+ constructor(path2) {
74073
+ this.path = path2;
74074
+ this.entries = this.read();
74075
+ }
74076
+ entries;
74077
+ get(key) {
74078
+ const value2 = this.entries[key];
74079
+ return value2 ? { ...value2 } : void 0;
74080
+ }
74081
+ commit(key, cursor) {
74082
+ if (!Number.isSafeInteger(cursor.offset) || cursor.offset < 0) {
74083
+ throw new Error("native transcript cursor offset must be a non-negative safe integer");
74084
+ }
74085
+ this.entries[key] = { ...cursor };
74086
+ this.persist();
74087
+ }
74088
+ read() {
74089
+ if (!(0, import_node_fs17.existsSync)(this.path)) return {};
74090
+ const parsed = JSON.parse((0, import_node_fs17.readFileSync)(this.path, "utf8"));
74091
+ if (parsed.version !== CURSOR_FILE_VERSION || !parsed.entries || typeof parsed.entries !== "object") {
74092
+ throw new Error("unsupported native transcript cursor file");
74093
+ }
74094
+ return parsed.entries;
74095
+ }
74096
+ persist() {
74097
+ (0, import_node_fs17.mkdirSync)((0, import_node_path15.dirname)(this.path), { recursive: true, mode: 448 });
74098
+ const pendingPath = `${this.path}.pending-${process.pid}-${(0, import_node_crypto9.randomBytes)(6).toString("hex")}`;
74099
+ try {
74100
+ (0, import_node_fs17.writeFileSync)(
74101
+ pendingPath,
74102
+ JSON.stringify({
74103
+ version: CURSOR_FILE_VERSION,
74104
+ entries: this.entries
74105
+ }),
74106
+ { mode: 384 }
74107
+ );
74108
+ (0, import_node_fs17.chmodSync)(pendingPath, 384);
74109
+ (0, import_node_fs17.renameSync)(pendingPath, this.path);
74110
+ } finally {
74111
+ (0, import_node_fs17.rmSync)(pendingPath, { force: true });
74112
+ }
74113
+ }
74114
+ };
74115
+
74116
+ // src/native-transcript-stream.ts
74117
+ var import_node_crypto10 = require("crypto");
74118
+ var import_node_fs18 = require("fs");
74119
+ var DEFAULT_MAX_READ_BYTES = 4 * 1024 * 1024;
74120
+ var NativeTranscriptStreamer = class {
74121
+ constructor(cursorStore, maxReadBytes = DEFAULT_MAX_READ_BYTES) {
74122
+ this.cursorStore = cursorStore;
74123
+ this.maxReadBytes = maxReadBytes;
74124
+ if (!Number.isSafeInteger(maxReadBytes) || maxReadBytes <= 0) {
74125
+ throw new Error("native transcript maxReadBytes must be a positive integer");
74126
+ }
74127
+ }
74128
+ async stream(hook, deliver) {
74129
+ if (!hook.transcriptPath) {
74130
+ return {
74131
+ complete: true,
74132
+ recordsRead: 0,
74133
+ eventsDelivered: 0,
74134
+ assistantOutputDelivered: false
74135
+ };
74136
+ }
74137
+ const key = nativeTranscriptCursorKey({
74138
+ provider: hook.provider,
74139
+ externalSessionId: hook.externalSessionId,
74140
+ transcriptPath: hook.transcriptPath
74141
+ });
74142
+ let previous = this.cursorStore.get(key);
74143
+ if (hook.eventType === "user_prompt" && previous && previous.assistantOutputTurnId !== hook.eventId) {
74144
+ previous = {
74145
+ ...previous,
74146
+ assistantOutputSeen: false,
74147
+ assistantOutputTurnId: hook.eventId
74148
+ };
74149
+ this.cursorStore.commit(key, previous);
74150
+ }
74151
+ const file2 = (0, import_node_fs18.statSync)(hook.transcriptPath);
74152
+ const replaced = Boolean(previous) && (previous.device !== file2.dev || previous.inode !== file2.ino);
74153
+ const truncated = Boolean(previous) && file2.size < previous.offset;
74154
+ const start = !previous || replaced || truncated ? {
74155
+ offset: 0,
74156
+ generation: previous ? previous.generation + 1 : 0,
74157
+ device: file2.dev,
74158
+ inode: file2.ino,
74159
+ assistantOutputSeen: previous?.assistantOutputSeen ?? false,
74160
+ assistantOutputTurnId: hook.eventType === "user_prompt" ? hook.eventId : previous?.assistantOutputTurnId
74161
+ } : previous;
74162
+ let assistantOutputSeen = start.assistantOutputSeen ?? false;
74163
+ if (file2.size <= start.offset) {
74164
+ return {
74165
+ complete: true,
74166
+ recordsRead: 0,
74167
+ eventsDelivered: 0,
74168
+ assistantOutputDelivered: assistantOutputSeen
74169
+ };
74170
+ }
74171
+ const bytesToRead = Math.min(file2.size - start.offset, this.maxReadBytes);
74172
+ const buffer = Buffer.allocUnsafe(bytesToRead);
74173
+ const fd = (0, import_node_fs18.openSync)(hook.transcriptPath, "r");
74174
+ let bytesRead = 0;
74175
+ try {
74176
+ bytesRead = (0, import_node_fs18.readSync)(fd, buffer, 0, bytesToRead, start.offset);
74177
+ } finally {
74178
+ (0, import_node_fs18.closeSync)(fd);
74179
+ }
74180
+ const chunk = buffer.subarray(0, bytesRead);
74181
+ const lastNewline = chunk.lastIndexOf(10);
74182
+ if (lastNewline < 0) {
74183
+ return {
74184
+ complete: false,
74185
+ recordsRead: 0,
74186
+ eventsDelivered: 0,
74187
+ assistantOutputDelivered: assistantOutputSeen
74188
+ };
74189
+ }
74190
+ let recordStart = 0;
74191
+ let recordsRead = 0;
74192
+ let eventsDelivered = 0;
74193
+ let committedOffset = start.offset;
74194
+ while (recordStart <= lastNewline) {
74195
+ const newline = chunk.indexOf(10, recordStart);
74196
+ if (newline < 0 || newline > lastNewline) break;
74197
+ const recordEndOffset = start.offset + newline + 1;
74198
+ const raw = chunk.subarray(recordStart, newline).toString("utf8").trim();
74199
+ recordStart = newline + 1;
74200
+ if (!raw) {
74201
+ committedOffset = recordEndOffset;
74202
+ this.cursorStore.commit(key, {
74203
+ ...start,
74204
+ offset: committedOffset,
74205
+ assistantOutputSeen
74206
+ });
74207
+ continue;
74208
+ }
74209
+ const parsed = parseJsonObject3(raw);
74210
+ const events = parsed ? claudeRecordToAgentEvents(parsed) : [];
74211
+ for (let index = 0; index < events.length; index += 1) {
74212
+ const deliveredEvent = events[index];
74213
+ const sourceCursor = `${start.generation}:${recordEndOffset}:${index}`;
74214
+ const eventId = stableSourceEventId(hook, sourceCursor);
74215
+ const accepted = await deliver({
74216
+ schemaVersion: 1,
74217
+ eventId,
74218
+ provider: hook.provider,
74219
+ externalSessionId: hook.externalSessionId,
74220
+ sourceCursor,
74221
+ observedAt: timestampOf(parsed) ?? hook.observedAt,
74222
+ event: deliveredEvent
74223
+ });
74224
+ if (!accepted) {
74225
+ return {
74226
+ complete: false,
74227
+ recordsRead,
74228
+ eventsDelivered,
74229
+ assistantOutputDelivered: assistantOutputSeen,
74230
+ cursor: `${start.generation}:${committedOffset}`
74231
+ };
74232
+ }
74233
+ eventsDelivered += 1;
74234
+ if (deliveredEvent.type === "text" && deliveredEvent.text.length > 0) {
74235
+ assistantOutputSeen = true;
74236
+ }
74237
+ }
74238
+ committedOffset = recordEndOffset;
74239
+ recordsRead += 1;
74240
+ this.cursorStore.commit(key, {
74241
+ ...start,
74242
+ offset: committedOffset,
74243
+ assistantOutputSeen
74244
+ });
74245
+ }
74246
+ return {
74247
+ complete: committedOffset >= file2.size,
74248
+ recordsRead,
74249
+ eventsDelivered,
74250
+ assistantOutputDelivered: assistantOutputSeen,
74251
+ cursor: `${start.generation}:${committedOffset}`
74252
+ };
74253
+ }
74254
+ };
74255
+ function stableSourceEventId(hook, sourceCursor) {
74256
+ const bytes = (0, import_node_crypto10.createHash)("sha256").update(`${hook.provider}\0${hook.externalSessionId}\0${hook.transcriptPath}\0${sourceCursor}`).digest().subarray(0, 16);
74257
+ bytes[6] = bytes[6] & 15 | 80;
74258
+ bytes[8] = bytes[8] & 63 | 128;
74259
+ const hex3 = bytes.toString("hex");
74260
+ return `${hex3.slice(0, 8)}-${hex3.slice(8, 12)}-${hex3.slice(12, 16)}-${hex3.slice(16, 20)}-${hex3.slice(20)}`;
74261
+ }
74262
+ function parseJsonObject3(value2) {
74263
+ try {
74264
+ const parsed = JSON.parse(value2);
74265
+ return isRecord2(parsed) ? parsed : null;
74266
+ } catch {
74267
+ return null;
74268
+ }
74269
+ }
74270
+ function claudeRecordToAgentEvents(record2) {
74271
+ const timestamp = timestampOf(record2) ? Date.parse(timestampOf(record2)) : Date.now();
74272
+ const ts = Number.isFinite(timestamp) ? timestamp : Date.now();
74273
+ const message = isRecord2(record2.message) ? record2.message : record2;
74274
+ const content = message.content;
74275
+ const role = typeof message.role === "string" ? message.role : typeof record2.type === "string" ? record2.type : null;
74276
+ if (role === "user") {
74277
+ if (!Array.isArray(content)) return [];
74278
+ return content.flatMap((block) => {
74279
+ if (!isRecord2(block) || block.type !== "tool_result") return [];
74280
+ const id = typeof block.tool_use_id === "string" ? block.tool_use_id : typeof block.toolUseId === "string" ? block.toolUseId : null;
74281
+ return id ? [{ type: "tool_result", id, content: extractText2(block.content), ts }] : [];
74282
+ });
74283
+ }
74284
+ if (record2.type === "summary" && typeof record2.summary === "string") {
74285
+ return [{ type: "session_notice", kind: "summary", message: record2.summary, ts }];
74286
+ }
74287
+ if (typeof record2.type === "string" && role !== "assistant") return [];
74288
+ if (Array.isArray(content)) {
74289
+ const events = content.flatMap((block) => {
74290
+ if (!isRecord2(block)) return [];
74291
+ if (block.type === "text" && typeof block.text === "string") {
74292
+ return [{ type: "text", text: block.text, ts }];
74293
+ }
74294
+ if (block.type === "thinking" && typeof block.thinking === "string") {
74295
+ return [{ type: "thinking", text: block.thinking, ts }];
74296
+ }
74297
+ if (block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string") {
74298
+ return [{ type: "tool_use", id: block.id, name: block.name, input: block.input, ts }];
74299
+ }
74300
+ if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
74301
+ return [
74302
+ { type: "tool_result", id: block.tool_use_id, content: extractText2(block.content), ts }
74303
+ ];
74304
+ }
74305
+ return [];
74306
+ });
74307
+ if (events.length > 0) return events;
74308
+ }
74309
+ if (typeof content === "string" && content.length > 0)
74310
+ return [{ type: "text", text: content, ts }];
74311
+ return [];
74312
+ }
74313
+ function extractText2(value2) {
74314
+ if (typeof value2 === "string") return value2;
74315
+ if (Array.isArray(value2)) return value2.map(extractText2).filter(Boolean).join("\n");
74316
+ if (isRecord2(value2)) {
74317
+ if (typeof value2.text === "string") return value2.text;
74318
+ if (typeof value2.content === "string") return value2.content;
74319
+ }
74320
+ return JSON.stringify(value2);
74321
+ }
74322
+ function timestampOf(record2) {
74323
+ if (!record2) return null;
74324
+ for (const key of ["timestamp", "created_at", "createdAt"]) {
74325
+ const value2 = record2[key];
74326
+ if (typeof value2 === "string" && !Number.isNaN(Date.parse(value2))) return value2;
74327
+ }
74328
+ return null;
74329
+ }
74330
+ function isRecord2(value2) {
74331
+ return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
74332
+ }
74333
+
73729
74334
  // src/network-diagnostics.ts
73730
74335
  function collectEvidence(error61) {
73731
74336
  const text = [];
@@ -73803,8 +74408,8 @@ var ObservableMap = class extends Map {
73803
74408
  };
73804
74409
 
73805
74410
  // src/run-journal.ts
73806
- var import_node_fs16 = require("fs");
73807
- var import_node_path14 = require("path");
74411
+ var import_node_fs19 = require("fs");
74412
+ var import_node_path16 = require("path");
73808
74413
  var JOURNAL_VERSION = 1;
73809
74414
  var INTERRUPTED_ENTRY_TTL_MS = 24 * 60 * 6e4;
73810
74415
  function isEntry(value2) {
@@ -73823,9 +74428,9 @@ function isEntry(value2) {
73823
74428
  var RuntimeRunJournal = class {
73824
74429
  constructor(path2, nowMs = Date.now()) {
73825
74430
  this.path = path2;
73826
- if (!(0, import_node_fs16.existsSync)(path2)) return;
74431
+ if (!(0, import_node_fs19.existsSync)(path2)) return;
73827
74432
  try {
73828
- const parsed = JSON.parse((0, import_node_fs16.readFileSync)(path2, "utf8"));
74433
+ const parsed = JSON.parse((0, import_node_fs19.readFileSync)(path2, "utf8"));
73829
74434
  if (parsed.version !== JOURNAL_VERSION || !Array.isArray(parsed.entries)) {
73830
74435
  throw new Error("unsupported run journal format");
73831
74436
  }
@@ -73948,18 +74553,18 @@ var RuntimeRunJournal = class {
73948
74553
  }
73949
74554
  persist() {
73950
74555
  const pendingPath = `${this.path}.pending`;
73951
- (0, import_node_fs16.mkdirSync)((0, import_node_path14.dirname)(this.path), { recursive: true });
74556
+ (0, import_node_fs19.mkdirSync)((0, import_node_path16.dirname)(this.path), { recursive: true });
73952
74557
  try {
73953
- (0, import_node_fs16.writeFileSync)(
74558
+ (0, import_node_fs19.writeFileSync)(
73954
74559
  pendingPath,
73955
74560
  `${JSON.stringify({ version: JOURNAL_VERSION, entries: [...this.entries.values()] })}
73956
74561
  `,
73957
74562
  { mode: 384 }
73958
74563
  );
73959
- (0, import_node_fs16.chmodSync)(pendingPath, 384);
73960
- (0, import_node_fs16.renameSync)(pendingPath, this.path);
74564
+ (0, import_node_fs19.chmodSync)(pendingPath, 384);
74565
+ (0, import_node_fs19.renameSync)(pendingPath, this.path);
73961
74566
  } catch (error61) {
73962
- (0, import_node_fs16.rmSync)(pendingPath, { force: true });
74567
+ (0, import_node_fs19.rmSync)(pendingPath, { force: true });
73963
74568
  throw error61;
73964
74569
  }
73965
74570
  }
@@ -73971,8 +74576,8 @@ var RunStartGate = class {
73971
74576
  async acquire() {
73972
74577
  const previous = this.tail;
73973
74578
  let releaseCurrent;
73974
- const current = new Promise((resolve14) => {
73975
- releaseCurrent = resolve14;
74579
+ const current = new Promise((resolve15) => {
74580
+ releaseCurrent = resolve15;
73976
74581
  });
73977
74582
  this.tail = previous.then(() => current);
73978
74583
  await previous;
@@ -73987,15 +74592,15 @@ var RunStartGate = class {
73987
74592
 
73988
74593
  // src/skills/local-skill-source.ts
73989
74594
  var import_node_buffer2 = require("buffer");
73990
- var import_node_crypto9 = require("crypto");
74595
+ var import_node_crypto12 = require("crypto");
73991
74596
  var import_promises2 = require("fs/promises");
73992
- var import_node_path16 = __toESM(require("path"), 1);
74597
+ var import_node_path18 = __toESM(require("path"), 1);
73993
74598
 
73994
74599
  // ../shared/dist/node/skill-package.js
73995
74600
  var import_node_buffer = require("buffer");
73996
- var import_node_crypto8 = require("crypto");
74601
+ var import_node_crypto11 = require("crypto");
73997
74602
  var import_promises = require("fs/promises");
73998
- var import_node_path15 = require("path");
74603
+ var import_node_path17 = require("path");
73999
74604
  var import_yaml = __toESM(require_dist2(), 1);
74000
74605
 
74001
74606
  // ../shared/dist/skills/domain.js
@@ -74419,7 +75024,7 @@ async function inspectSkillPackage(packageRoot, options = {}) {
74419
75024
  }
74420
75025
  const inspectedFiles = await Promise.all(files.map(readPackageFile));
74421
75026
  inspectedFiles.sort(comparePackageFiles);
74422
- const parsedMetadata = parseSkillMetadata(inspectedFiles.find((file2) => file2.manifest.path === "SKILL.md")?.bytes, options.expectedName === void 0 ? (0, import_node_path15.basename)(root) : options.expectedName);
75027
+ const parsedMetadata = parseSkillMetadata(inspectedFiles.find((file2) => file2.manifest.path === "SKILL.md")?.bytes, options.expectedName === void 0 ? (0, import_node_path17.basename)(root) : options.expectedName);
74423
75028
  const manifestFiles = inspectedFiles.map((file2) => file2.manifest);
74424
75029
  const diagnostics = [
74425
75030
  ...parsedMetadata.conformance,
@@ -74443,7 +75048,7 @@ async function readSkillPackageFile(packageRoot, path2, expectedSha256) {
74443
75048
  if (!normalizedPath || normalizedPath !== path2 || normalizedPath.includes("\\") || components.some((component) => !component || component === "." || component === "..")) {
74444
75049
  throw new SkillPackageError("INVALID_PATH", `Invalid Skill package path: ${path2}`, path2);
74445
75050
  }
74446
- const absolutePath = await (0, import_promises.realpath)((0, import_node_path15.resolve)(root, ...components)).catch(() => null);
75051
+ const absolutePath = await (0, import_promises.realpath)((0, import_node_path17.resolve)(root, ...components)).catch(() => null);
74447
75052
  if (!absolutePath || normalizeRelativePath(root, absolutePath) !== normalizedPath) {
74448
75053
  throw new SkillPackageError("INVALID_PATH", `Skill package file was not found: ${path2}`, path2);
74449
75054
  }
@@ -74464,7 +75069,7 @@ async function readSkillPackageFile(packageRoot, path2, expectedSha256) {
74464
75069
  return { file: read.manifest, bytes: read.bytes };
74465
75070
  }
74466
75071
  async function requirePackageRoot(packageRoot) {
74467
- const root = await (0, import_promises.realpath)((0, import_node_path15.resolve)(packageRoot)).catch(() => null);
75072
+ const root = await (0, import_promises.realpath)((0, import_node_path17.resolve)(packageRoot)).catch(() => null);
74468
75073
  if (!root) {
74469
75074
  throw new SkillPackageError("INVALID_PACKAGE_ROOT", "Skill package directory does not exist");
74470
75075
  }
@@ -74490,7 +75095,7 @@ async function walkPackage(root, ignoredPaths) {
74490
75095
  async function walkDirectory(directory) {
74491
75096
  const entries = await (0, import_promises.readdir)(directory, { withFileTypes: true });
74492
75097
  for (const entry of entries) {
74493
- const absolutePath = (0, import_node_path15.resolve)(directory, entry.name);
75098
+ const absolutePath = (0, import_node_path17.resolve)(directory, entry.name);
74494
75099
  const normalizedPath = normalizeRelativePath(root, absolutePath);
74495
75100
  if (isIgnored(normalizedPath, entry.name, entry.isDirectory(), ignored))
74496
75101
  continue;
@@ -74557,12 +75162,12 @@ function globToRegExp(pattern) {
74557
75162
  return new RegExp(`${source}(?:/.*)?$`);
74558
75163
  }
74559
75164
  function normalizeRelativePath(root, absolutePath) {
74560
- const nativeRelativePath = (0, import_node_path15.relative)(root, absolutePath);
74561
- if (!nativeRelativePath || nativeRelativePath.startsWith(`..${import_node_path15.sep}`) || nativeRelativePath === "..") {
75165
+ const nativeRelativePath = (0, import_node_path17.relative)(root, absolutePath);
75166
+ if (!nativeRelativePath || nativeRelativePath.startsWith(`..${import_node_path17.sep}`) || nativeRelativePath === "..") {
74562
75167
  throw new SkillPackageError("INVALID_PATH", "Skill package path escapes its root");
74563
75168
  }
74564
- const normalized = nativeRelativePath.split(import_node_path15.sep).join("/").normalize("NFC");
74565
- if (normalized.includes("\0") || normalized.includes("\uFFFD") || import_node_path15.posix.isAbsolute(normalized)) {
75169
+ const normalized = nativeRelativePath.split(import_node_path17.sep).join("/").normalize("NFC");
75170
+ if (normalized.includes("\0") || normalized.includes("\uFFFD") || import_node_path17.posix.isAbsolute(normalized)) {
74566
75171
  throw new SkillPackageError("INVALID_PATH", `Invalid Skill package path: ${normalized}`);
74567
75172
  }
74568
75173
  return normalized;
@@ -74597,7 +75202,7 @@ function requireSafeFile(path2, size, normalizedPaths) {
74597
75202
  }
74598
75203
  }
74599
75204
  function isSecretPath(path2) {
74600
- const name = import_node_path15.posix.basename(path2).toLowerCase();
75205
+ const name = import_node_path17.posix.basename(path2).toLowerCase();
74601
75206
  if (SECRET_BASENAMES.has(name) || name.startsWith(".env."))
74602
75207
  return true;
74603
75208
  return name.endsWith(".private-key.pem") || name.endsWith(".private-key.key");
@@ -74625,12 +75230,12 @@ function classifyFileKind(path2) {
74625
75230
  return "reference";
74626
75231
  if (path2.startsWith("assets/"))
74627
75232
  return "asset";
74628
- if (/^(?:licen[cs]e|copying|notice)(?:\.|$)/i.test(import_node_path15.posix.basename(path2)))
75233
+ if (/^(?:licen[cs]e|copying|notice)(?:\.|$)/i.test(import_node_path17.posix.basename(path2)))
74629
75234
  return "license";
74630
75235
  return "other";
74631
75236
  }
74632
75237
  function classifyPreview(path2, bytes) {
74633
- const extension = import_node_path15.posix.extname(path2).toLowerCase();
75238
+ const extension = import_node_path17.posix.extname(path2).toLowerCase();
74634
75239
  if (IMAGE_EXTENSIONS.has(extension))
74635
75240
  return "image";
74636
75241
  if (bytes.includes(0))
@@ -74663,7 +75268,7 @@ function parseSkillMetadata(bytes, expectedName) {
74663
75268
  } catch (error61) {
74664
75269
  throw new SkillPackageError("INVALID_FRONTMATTER", `SKILL.md frontmatter is not valid YAML: ${error61 instanceof Error ? error61.message : String(error61)}`, "SKILL.md");
74665
75270
  }
74666
- if (!isRecord2(parsed)) {
75271
+ if (!isRecord3(parsed)) {
74667
75272
  throw new SkillPackageError("INVALID_FRONTMATTER", "SKILL.md frontmatter must be a YAML mapping", "SKILL.md");
74668
75273
  }
74669
75274
  const admitted = skillFrontmatterAdmissionSchema.safeParse(parsed);
@@ -74734,7 +75339,7 @@ function normalizePackageReference(value2) {
74734
75339
  const withoutSuffix = trimmed.split(/[?#]/, 1)[0];
74735
75340
  if (!withoutSuffix)
74736
75341
  return null;
74737
- const normalized = import_node_path15.posix.normalize(withoutSuffix).replace(/^\.\//, "").normalize("NFC");
75342
+ const normalized = import_node_path17.posix.normalize(withoutSuffix).replace(/^\.\//, "").normalize("NFC");
74738
75343
  if (normalized === ".." || normalized.startsWith("../"))
74739
75344
  return null;
74740
75345
  return normalized;
@@ -74742,14 +75347,14 @@ function normalizePackageReference(value2) {
74742
75347
  function looksLikeFileReference(value2) {
74743
75348
  return !/\s/.test(value2) && (value2.includes("/") || value2.includes("."));
74744
75349
  }
74745
- function isRecord2(value2) {
75350
+ function isRecord3(value2) {
74746
75351
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
74747
75352
  }
74748
75353
  function comparePackageFiles(left, right) {
74749
75354
  return import_node_buffer.Buffer.compare(import_node_buffer.Buffer.from(left.manifest.path, "utf8"), import_node_buffer.Buffer.from(right.manifest.path, "utf8"));
74750
75355
  }
74751
75356
  function hashPackageTree(files) {
74752
- const hash2 = (0, import_node_crypto8.createHash)("sha256");
75357
+ const hash2 = (0, import_node_crypto11.createHash)("sha256");
74753
75358
  hash2.update(TREE_HASH_PREFIX);
74754
75359
  for (const file2 of files) {
74755
75360
  const path2 = import_node_buffer.Buffer.from(file2.path, "utf8");
@@ -74762,7 +75367,7 @@ function hashPackageTree(files) {
74762
75367
  return `sha256:${hash2.digest("hex")}`;
74763
75368
  }
74764
75369
  function sha256(bytes) {
74765
- return `sha256:${(0, import_node_crypto8.createHash)("sha256").update(bytes).digest("hex")}`;
75370
+ return `sha256:${(0, import_node_crypto11.createHash)("sha256").update(bytes).digest("hex")}`;
74766
75371
  }
74767
75372
  function uint322(value2) {
74768
75373
  const buffer = import_node_buffer.Buffer.allocUnsafe(4);
@@ -74894,7 +75499,7 @@ var LocalSkillSource = class {
74894
75499
  locations = /* @__PURE__ */ new Map();
74895
75500
  inspectionCache = /* @__PURE__ */ new Map();
74896
75501
  async scan(input2) {
74897
- const attemptId = (0, import_node_crypto9.randomUUID)();
75502
+ const attemptId = (0, import_node_crypto12.randomUUID)();
74898
75503
  const diagnostics = [];
74899
75504
  const candidates = [];
74900
75505
  const visitedDirectories = /* @__PURE__ */ new Set();
@@ -74971,9 +75576,9 @@ var LocalSkillSource = class {
74971
75576
  */
74972
75577
  async scanRootAsPackage(resolvedRoot, ignoredPaths, mode) {
74973
75578
  const directory = resolvedRoot.directory;
74974
- if (!await pathExists((0, import_node_path16.join)(directory, "SKILL.md"))) return null;
75579
+ if (!await pathExists((0, import_node_path18.join)(directory, "SKILL.md"))) return null;
74975
75580
  const canonicalRoot = await (0, import_promises2.realpath)(directory).catch(() => directory);
74976
- const name = import_node_path16.default.basename(canonicalRoot);
75581
+ const name = import_node_path18.default.basename(canonicalRoot);
74977
75582
  try {
74978
75583
  const fingerprint = await fingerprintPackage(canonicalRoot);
74979
75584
  const ignoreSignature = JSON.stringify(ignoredPaths);
@@ -75043,7 +75648,7 @@ var LocalSkillSource = class {
75043
75648
  );
75044
75649
  for (const entry of entries) {
75045
75650
  if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
75046
- const candidatePath = (0, import_node_path16.join)(resolvedRoot.directory, entry.name);
75651
+ const candidatePath = (0, import_node_path18.join)(resolvedRoot.directory, entry.name);
75047
75652
  const canonicalPath = await (0, import_promises2.realpath)(candidatePath).catch(() => null);
75048
75653
  if (!canonicalPath) {
75049
75654
  diagnostics.push({
@@ -75101,7 +75706,7 @@ var LocalSkillSource = class {
75101
75706
  }
75102
75707
  };
75103
75708
  async function fingerprintPackage(root) {
75104
- const hash2 = (0, import_node_crypto9.createHash)("sha256").update("alan-skill-stat-fingerprint-v1\0");
75709
+ const hash2 = (0, import_node_crypto12.createHash)("sha256").update("alan-skill-stat-fingerprint-v1\0");
75105
75710
  await visit2(root, "");
75106
75711
  return `sha256:${hash2.digest("hex")}`;
75107
75712
  async function visit2(directory, relativeDirectory) {
@@ -75109,7 +75714,7 @@ async function fingerprintPackage(root) {
75109
75714
  entries.sort((left, right) => import_node_buffer2.Buffer.compare(import_node_buffer2.Buffer.from(left.name), import_node_buffer2.Buffer.from(right.name)));
75110
75715
  for (const entry of entries) {
75111
75716
  const path2 = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
75112
- const absolutePath = (0, import_node_path16.join)(directory, entry.name);
75717
+ const absolutePath = (0, import_node_path18.join)(directory, entry.name);
75113
75718
  const metadata = await (0, import_promises2.lstat)(absolutePath);
75114
75719
  hash2.update(path2.normalize("NFC")).update("\0").update(
75115
75720
  entry.isDirectory() ? "d" : entry.isFile() ? "f" : entry.isSymbolicLink() ? "l" : "o"
@@ -75134,12 +75739,12 @@ async function canonicalDirectories(directories) {
75134
75739
  return [...new Set(canonical)];
75135
75740
  }
75136
75741
  function isWithinDirectory(parent, candidate) {
75137
- const path2 = (0, import_node_path16.relative)(parent, candidate);
75138
- return path2 !== ".." && !path2.startsWith(`..${import_node_path16.sep}`) && !(0, import_node_path16.isAbsolute)(path2);
75742
+ const path2 = (0, import_node_path18.relative)(parent, candidate);
75743
+ return path2 !== ".." && !path2.startsWith(`..${import_node_path18.sep}`) && !(0, import_node_path18.isAbsolute)(path2);
75139
75744
  }
75140
75745
  async function loadSkillSourceConfig(homeDirectory) {
75141
- const configDirectory = (0, import_node_path16.join)(homeDirectory, ".alan");
75142
- const configPath = (0, import_node_path16.join)(configDirectory, "config.toml");
75746
+ const configDirectory = (0, import_node_path18.join)(homeDirectory, ".alan");
75747
+ const configPath = (0, import_node_path18.join)(configDirectory, "config.toml");
75143
75748
  await (0, import_promises2.mkdir)(configDirectory, { recursive: true, mode: 448 });
75144
75749
  let created = false;
75145
75750
  try {
@@ -75173,7 +75778,7 @@ async function resolveSkillScanInput(homeDirectory, declared) {
75173
75778
  }
75174
75779
  const { config: config2 } = loaded;
75175
75780
  if (!config2.enabled) return { ...declared, roots: [], ignoredPaths: config2.ignoredPaths };
75176
- const draftsPath = (0, import_node_path16.resolve)((0, import_node_path16.join)(homeDirectory, ".alan", "skill-sources"));
75781
+ const draftsPath = (0, import_node_path18.resolve)((0, import_node_path18.join)(homeDirectory, ".alan", "skill-sources"));
75177
75782
  const enabledProviders = new Set(declared.enabledProviderKinds ?? []);
75178
75783
  const configured = [];
75179
75784
  for (const source of config2.sources) {
@@ -75181,7 +75786,7 @@ async function resolveSkillScanInput(homeDirectory, declared) {
75181
75786
  continue;
75182
75787
  }
75183
75788
  configured.push(
75184
- (0, import_node_path16.resolve)(source.path) === draftsPath ? { kind: "drafts", path: source.path, label: "Alan drafts" } : {
75789
+ (0, import_node_path18.resolve)(source.path) === draftsPath ? { kind: "drafts", path: source.path, label: "Alan drafts" } : {
75185
75790
  kind: "configured",
75186
75791
  declarationId: source.declarationId,
75187
75792
  path: source.path,
@@ -75199,7 +75804,7 @@ async function resolveSkillScanInput(homeDirectory, declared) {
75199
75804
  }
75200
75805
  function parseSkillSourceConfig(content, homeDirectory) {
75201
75806
  const parsed = parse5(content);
75202
- if (!isRecord3(parsed) || parsed.version !== 1 || !isRecord3(parsed.skills)) {
75807
+ if (!isRecord4(parsed) || parsed.version !== 1 || !isRecord4(parsed.skills)) {
75203
75808
  throw new Error("~/.alan/config.toml must contain version = 1 and a [skills] table");
75204
75809
  }
75205
75810
  const skills = parsed.skills;
@@ -75215,7 +75820,7 @@ function parseSkillSourceConfig(content, homeDirectory) {
75215
75820
  throw new Error("~/.alan/config.toml must declare at least one [[skills.sources]] table");
75216
75821
  }
75217
75822
  const sources = skills.sources.map((value2, index) => {
75218
- if (!isRecord3(value2) || typeof value2.path !== "string" || typeof value2.label !== "string") {
75823
+ if (!isRecord4(value2) || typeof value2.path !== "string" || typeof value2.label !== "string") {
75219
75824
  throw new Error(
75220
75825
  `skills.sources entry ${index + 1} must contain string path and label values`
75221
75826
  );
@@ -75228,7 +75833,7 @@ function parseSkillSourceConfig(content, homeDirectory) {
75228
75833
  value2.providers,
75229
75834
  `skills.sources[${index}].providers`
75230
75835
  );
75231
- const relativeDirectory = import_node_path16.default.relative(homeDirectory, sourcePath);
75836
+ const relativeDirectory = import_node_path18.default.relative(homeDirectory, sourcePath);
75232
75837
  const inferredProviders = providerKindsForUserDirectory(relativeDirectory);
75233
75838
  const declaredProviders = value2.providers === void 0 ? inferredProviders : [...new Set(explicitProviders.map((provider) => provider.trim()).filter(Boolean))];
75234
75839
  const stale = staleCodexDeclaration(relativeDirectory, label, declaredProviders);
@@ -75249,7 +75854,7 @@ function parseSkillSourceConfig(content, homeDirectory) {
75249
75854
  return { enabled, publishChanges, discoverRepositorySkills, ignoredPaths, sources };
75250
75855
  }
75251
75856
  function stableSourceDeclarationId(sourcePath) {
75252
- const digest = (0, import_node_crypto9.createHash)("sha256").update("alan-skill-source-v1\0").update(sourcePath.normalize("NFC")).digest("hex").slice(0, 24);
75857
+ const digest = (0, import_node_crypto12.createHash)("sha256").update("alan-skill-source-v1\0").update(sourcePath.normalize("NFC")).digest("hex").slice(0, 24);
75253
75858
  return `source-${digest}`;
75254
75859
  }
75255
75860
  function requiredSourceId(value2, field) {
@@ -75258,7 +75863,7 @@ function requiredSourceId(value2, field) {
75258
75863
  }
75259
75864
  return value2;
75260
75865
  }
75261
- function resolveSkillSourcePath(sourcePath, homeDirectory, pathApi = import_node_path16.default) {
75866
+ function resolveSkillSourcePath(sourcePath, homeDirectory, pathApi = import_node_path18.default) {
75262
75867
  const trimmed = sourcePath.trim();
75263
75868
  if (trimmed === "~") return pathApi.resolve(homeDirectory);
75264
75869
  if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
@@ -75293,7 +75898,7 @@ function resolveScanRoots(roots, enabledProviderKinds) {
75293
75898
  ).entries()) {
75294
75899
  resolved.push({
75295
75900
  root,
75296
- directory: (0, import_node_path16.join)(root.path, relativeDirectory),
75901
+ directory: (0, import_node_path18.join)(root.path, relativeDirectory),
75297
75902
  order: `1:${root.repositoryId}:${String(index).padStart(2, "0")}`
75298
75903
  });
75299
75904
  }
@@ -75315,11 +75920,11 @@ function candidateOrigin(root) {
75315
75920
  }
75316
75921
  function createLocalKey(root, skillName) {
75317
75922
  const rootIdentity = root.kind === "configured" ? `configured:${root.declarationId}` : root.kind === "repository" ? `repository:${root.repositoryId}` : "drafts";
75318
- const digest = (0, import_node_crypto9.createHash)("sha256").update("alan-local-skill-v1\0").update(rootIdentity).update("\0").update(skillName.normalize("NFC")).digest("hex");
75923
+ const digest = (0, import_node_crypto12.createHash)("sha256").update("alan-local-skill-v1\0").update(rootIdentity).update("\0").update(skillName.normalize("NFC")).digest("hex");
75319
75924
  return `local:${digest}`;
75320
75925
  }
75321
75926
  function hashInventory(candidates) {
75322
- const hash2 = (0, import_node_crypto9.createHash)("sha256");
75927
+ const hash2 = (0, import_node_crypto12.createHash)("sha256");
75323
75928
  hash2.update("alan-skill-inventory-v1\0");
75324
75929
  for (const candidate of candidates) {
75325
75930
  hash2.update(candidate.localKey);
@@ -75352,7 +75957,7 @@ async function pathExists(candidate) {
75352
75957
  }
75353
75958
  async function hasManagedMarker(directory) {
75354
75959
  try {
75355
- await (0, import_promises2.access)((0, import_node_path16.join)(directory, ".alan-managed.json"));
75960
+ await (0, import_promises2.access)((0, import_node_path18.join)(directory, ".alan-managed.json"));
75356
75961
  return true;
75357
75962
  } catch {
75358
75963
  return false;
@@ -75371,7 +75976,7 @@ function isMissingPath(error61) {
75371
75976
  function isAlreadyPresent(error61) {
75372
75977
  return typeof error61 === "object" && error61 !== null && "code" in error61 && error61.code === "EEXIST";
75373
75978
  }
75374
- function isRecord3(value2) {
75979
+ function isRecord4(value2) {
75375
75980
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
75376
75981
  }
75377
75982
 
@@ -75505,8 +76110,8 @@ var RuntimeSkillCoordinator = class {
75505
76110
  async runExclusively(work) {
75506
76111
  const previous = this.tail;
75507
76112
  let release2;
75508
- this.tail = new Promise((resolve14) => {
75509
- release2 = resolve14;
76113
+ this.tail = new Promise((resolve15) => {
76114
+ release2 = resolve15;
75510
76115
  });
75511
76116
  await previous;
75512
76117
  try {
@@ -75519,14 +76124,14 @@ var RuntimeSkillCoordinator = class {
75519
76124
 
75520
76125
  // src/skills/runtime-skill-manager.ts
75521
76126
  var import_node_buffer5 = require("buffer");
75522
- var import_node_crypto12 = require("crypto");
76127
+ var import_node_crypto15 = require("crypto");
75523
76128
  var import_promises5 = require("fs/promises");
75524
- var import_node_path19 = require("path");
76129
+ var import_node_path21 = require("path");
75525
76130
 
75526
76131
  // src/skills/managed-skill-store.ts
75527
- var import_node_crypto10 = require("crypto");
76132
+ var import_node_crypto13 = require("crypto");
75528
76133
  var import_promises3 = require("fs/promises");
75529
- var import_node_path17 = require("path");
76134
+ var import_node_path19 = require("path");
75530
76135
  var EMPTY_STATE = {
75531
76136
  version: 1,
75532
76137
  sources: {},
@@ -75545,7 +76150,7 @@ var ManagedSkillStore = class _ManagedSkillStore {
75545
76150
  static async open(baseDirectory) {
75546
76151
  await (0, import_promises3.mkdir)(baseDirectory, { recursive: true, mode: 448 });
75547
76152
  try {
75548
- const parsed = JSON.parse(await (0, import_promises3.readFile)((0, import_node_path17.join)(baseDirectory, "state.json"), "utf8"));
76153
+ const parsed = JSON.parse(await (0, import_promises3.readFile)((0, import_node_path19.join)(baseDirectory, "state.json"), "utf8"));
75549
76154
  return new _ManagedSkillStore(baseDirectory, parseState(parsed), "verified");
75550
76155
  } catch (error61) {
75551
76156
  if (isMissingPath2(error61)) {
@@ -75644,10 +76249,10 @@ var ManagedSkillStore = class _ManagedSkillStore {
75644
76249
  this.canRemoveManagedFiles = true;
75645
76250
  }
75646
76251
  async persist() {
75647
- const statePath = (0, import_node_path17.join)(this.baseDirectory, "state.json");
75648
- const pendingPath = (0, import_node_path17.join)(
76252
+ const statePath = (0, import_node_path19.join)(this.baseDirectory, "state.json");
76253
+ const pendingPath = (0, import_node_path19.join)(
75649
76254
  this.baseDirectory,
75650
- `.state.json.pending-${process.pid}-${(0, import_node_crypto10.randomBytes)(6).toString("hex")}`
76255
+ `.state.json.pending-${process.pid}-${(0, import_node_crypto13.randomBytes)(6).toString("hex")}`
75651
76256
  );
75652
76257
  try {
75653
76258
  await (0, import_promises3.writeFile)(pendingPath, `${JSON.stringify(this.state, null, 2)}
@@ -75663,13 +76268,13 @@ var ManagedSkillStore = class _ManagedSkillStore {
75663
76268
  }
75664
76269
  };
75665
76270
  function parseState(value2) {
75666
- if (!isRecord4(value2) || value2.version !== 1) throw new Error("unsupported Skill state version");
75667
- if (!isRecord4(value2.sources) || !isRecord4(value2.projections)) {
76271
+ if (!isRecord5(value2) || value2.version !== 1) throw new Error("unsupported Skill state version");
76272
+ if (!isRecord5(value2.sources) || !isRecord5(value2.projections)) {
75668
76273
  throw new Error("invalid Skill state maps");
75669
76274
  }
75670
76275
  const sources = {};
75671
76276
  for (const [localKey, source] of Object.entries(value2.sources)) {
75672
- if (!localKey.startsWith("local:") || !isRecord4(source) || typeof source.absolutePath !== "string" || !isSha256(source.contentHash)) {
76277
+ if (!localKey.startsWith("local:") || !isRecord5(source) || typeof source.absolutePath !== "string" || !isSha256(source.contentHash)) {
75673
76278
  throw new Error("invalid Skill source state");
75674
76279
  }
75675
76280
  const publication = parsePublication(source.publication, source.contentHash);
@@ -75681,7 +76286,7 @@ function parseState(value2) {
75681
76286
  }
75682
76287
  const projections = {};
75683
76288
  for (const [directory, projection] of Object.entries(value2.projections)) {
75684
- if (!directory || !isRecord4(projection) || typeof projection.skillId !== "string" || typeof projection.versionId !== "string" || !isSha256(projection.contentHash)) {
76289
+ if (!directory || !isRecord5(projection) || typeof projection.skillId !== "string" || typeof projection.versionId !== "string" || !isSha256(projection.contentHash)) {
75685
76290
  throw new Error("invalid Skill projection state");
75686
76291
  }
75687
76292
  projections[directory] = {
@@ -75702,7 +76307,7 @@ function parseState(value2) {
75702
76307
  }
75703
76308
  function parsePublication(value2, sourceHash) {
75704
76309
  if (value2 === void 0) return void 0;
75705
- if (!isRecord4(value2) || typeof value2.skillId !== "string" || typeof value2.versionId !== "string" || !isSha256(value2.contentHash) || value2.contentHash !== sourceHash) {
76310
+ if (!isRecord5(value2) || typeof value2.skillId !== "string" || typeof value2.versionId !== "string" || !isSha256(value2.contentHash) || value2.contentHash !== sourceHash) {
75706
76311
  throw new Error("invalid Skill source publication state");
75707
76312
  }
75708
76313
  return {
@@ -75719,7 +76324,7 @@ function optionalSha256(value2) {
75719
76324
  function isSha256(value2) {
75720
76325
  return typeof value2 === "string" && /^sha256:[a-f0-9]{64}$/.test(value2);
75721
76326
  }
75722
- function isRecord4(value2) {
76327
+ function isRecord5(value2) {
75723
76328
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
75724
76329
  }
75725
76330
  function isMissingPath2(error61) {
@@ -75728,9 +76333,9 @@ function isMissingPath2(error61) {
75728
76333
 
75729
76334
  // ../shared/dist/node/skill-bundle.js
75730
76335
  var import_node_buffer3 = require("buffer");
75731
- var import_node_crypto11 = require("crypto");
76336
+ var import_node_crypto14 = require("crypto");
75732
76337
  var import_promises4 = require("fs/promises");
75733
- var import_node_path18 = require("path");
76338
+ var import_node_path20 = require("path");
75734
76339
  var import_node_stream = require("stream");
75735
76340
  var import_node_zlib = require("zlib");
75736
76341
  var import_tar_stream = __toESM(require_tar_stream(), 1);
@@ -75739,7 +76344,7 @@ async function createSkillBundle(packageRoot) {
75739
76344
  const archive = (0, import_tar_stream.pack)();
75740
76345
  const tarPromise = collectStream(archive);
75741
76346
  for (const file2 of before.files) {
75742
- const bytes2 = await (0, import_promises4.readFile)((0, import_node_path18.join)(packageRoot, ...file2.path.split("/")));
76347
+ const bytes2 = await (0, import_promises4.readFile)((0, import_node_path20.join)(packageRoot, ...file2.path.split("/")));
75743
76348
  await addArchiveEntry(archive, {
75744
76349
  name: file2.path,
75745
76350
  type: "file",
@@ -75790,8 +76395,8 @@ async function collectStream(stream) {
75790
76395
  return import_node_buffer3.Buffer.concat(chunks);
75791
76396
  }
75792
76397
  function addArchiveEntry(archive, header, bytes) {
75793
- return new Promise((resolve14, reject) => {
75794
- archive.entry(header, bytes, (error61) => error61 ? reject(error61) : resolve14());
76398
+ return new Promise((resolve15, reject) => {
76399
+ archive.entry(header, bytes, (error61) => error61 ? reject(error61) : resolve15());
75795
76400
  });
75796
76401
  }
75797
76402
  async function decompressBounded(archiveBytes) {
@@ -75817,7 +76422,7 @@ async function extractTar(tarBytes, destination) {
75817
76422
  const extractor = (0, import_tar_stream.extract)();
75818
76423
  let fileCount = 0;
75819
76424
  let totalBytes = 0;
75820
- const completion = new Promise((resolve14, reject) => {
76425
+ const completion = new Promise((resolve15, reject) => {
75821
76426
  extractor.on("entry", (header, stream, next) => {
75822
76427
  void extractEntry(header, stream, destination, {
75823
76428
  addFile(size) {
@@ -75832,7 +76437,7 @@ async function extractTar(tarBytes, destination) {
75832
76437
  }
75833
76438
  }).then(next, (error61) => extractor.destroy(error61 instanceof Error ? error61 : new Error(String(error61))));
75834
76439
  });
75835
- extractor.once("finish", resolve14);
76440
+ extractor.once("finish", resolve15);
75836
76441
  extractor.once("error", reject);
75837
76442
  });
75838
76443
  import_node_stream.Readable.from(tarBytes).pipe(extractor);
@@ -75854,19 +76459,19 @@ async function extractEntry(header, stream, destination, limits) {
75854
76459
  if (bytes.byteLength !== declaredSize) {
75855
76460
  throw new Error(`Skill archive file size is invalid: ${path2}`);
75856
76461
  }
75857
- const outputPath = (0, import_node_path18.join)(destination, ...path2.split("/"));
75858
- await (0, import_promises4.mkdir)((0, import_node_path18.dirname)(outputPath), { recursive: true, mode: 448 });
76462
+ const outputPath = (0, import_node_path20.join)(destination, ...path2.split("/"));
76463
+ await (0, import_promises4.mkdir)((0, import_node_path20.dirname)(outputPath), { recursive: true, mode: 448 });
75859
76464
  await (0, import_promises4.writeFile)(outputPath, bytes, { mode: (header.mode ?? 420) & 73 ? 493 : 420 });
75860
76465
  }
75861
76466
  function requireSafeArchivePath(value2) {
75862
76467
  const normalized = value2.replaceAll("\\", "/").normalize("NFC");
75863
- if (!normalized || normalized.startsWith("/") || normalized.includes("\0") || import_node_path18.posix.normalize(normalized) !== normalized || normalized === ".." || normalized.startsWith("../")) {
76468
+ if (!normalized || normalized.startsWith("/") || normalized.includes("\0") || import_node_path20.posix.normalize(normalized) !== normalized || normalized === ".." || normalized.startsWith("../")) {
75864
76469
  throw new Error(`Skill archive contains an unsafe path: ${value2}`);
75865
76470
  }
75866
76471
  return normalized;
75867
76472
  }
75868
76473
  function sha2562(bytes) {
75869
- return `sha256:${(0, import_node_crypto11.createHash)("sha256").update(bytes).digest("hex")}`;
76474
+ return `sha256:${(0, import_node_crypto14.createHash)("sha256").update(bytes).digest("hex")}`;
75870
76475
  }
75871
76476
 
75872
76477
  // src/skills/skill-control-client.ts
@@ -75939,14 +76544,14 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
75939
76544
  }
75940
76545
  reconcileTail = Promise.resolve();
75941
76546
  static async open(options) {
75942
- const skillsStateDirectory = (0, import_node_path19.join)(options.homeDirectory, ".alan", "skills");
75943
- const managedSkillsDirectory = (0, import_node_path19.join)(options.homeDirectory, ".agents", "skills");
76547
+ const skillsStateDirectory = (0, import_node_path21.join)(options.homeDirectory, ".alan", "skills");
76548
+ const managedSkillsDirectory = (0, import_node_path21.join)(options.homeDirectory, ".agents", "skills");
75944
76549
  const store = await ManagedSkillStore.open(skillsStateDirectory);
75945
76550
  return new _RuntimeSkillManager(
75946
76551
  skillsStateDirectory,
75947
76552
  managedSkillsDirectory,
75948
- (0, import_node_path19.join)(options.homeDirectory, ".claude", "skills"),
75949
- (0, import_node_path19.join)(options.homeDirectory, ".alan", "sync-manifest.json"),
76553
+ (0, import_node_path21.join)(options.homeDirectory, ".claude", "skills"),
76554
+ (0, import_node_path21.join)(options.homeDirectory, ".alan", "sync-manifest.json"),
75950
76555
  store,
75951
76556
  options.downloadBundle ?? downloadBundleWithFetch,
75952
76557
  options.localSource ?? new LocalSkillSource(),
@@ -76105,7 +76710,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76105
76710
  if (!runtimeDirectory) {
76106
76711
  throw new Error("Selected Skill version is not installed on this runtime");
76107
76712
  }
76108
- const packageDirectory = (0, import_node_path19.join)(this.managedSkillsDirectory, runtimeDirectory);
76713
+ const packageDirectory = (0, import_node_path21.join)(this.managedSkillsDirectory, runtimeDirectory);
76109
76714
  const marker = await readMarker(packageDirectory);
76110
76715
  if (!marker || marker.skillId !== invocation.skillId || marker.versionId !== invocation.versionId) {
76111
76716
  throw new Error("Selected Skill projection is missing or no longer owned by Alan");
@@ -76119,11 +76724,11 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76119
76724
  async reconcileUnlocked(manifest) {
76120
76725
  const desired = desiredSkillManifestSchema.parse(manifest);
76121
76726
  await (0, import_promises5.mkdir)(this.managedSkillsDirectory, { recursive: true, mode: 448 });
76122
- await (0, import_promises5.mkdir)((0, import_node_path19.join)(this.skillsStateDirectory, "cache", "sha256"), {
76727
+ await (0, import_promises5.mkdir)((0, import_node_path21.join)(this.skillsStateDirectory, "cache", "sha256"), {
76123
76728
  recursive: true,
76124
76729
  mode: 448
76125
76730
  });
76126
- await (0, import_promises5.mkdir)((0, import_node_path19.join)(this.skillsStateDirectory, "tmp"), { recursive: true, mode: 448 });
76731
+ await (0, import_promises5.mkdir)((0, import_node_path21.join)(this.skillsStateDirectory, "tmp"), { recursive: true, mode: 448 });
76127
76732
  const results = [];
76128
76733
  const nextProjections = {};
76129
76734
  for (const entry of desired.skills) {
@@ -76168,13 +76773,13 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76168
76773
  await this.retireLegacySyncManifest();
76169
76774
  }
76170
76775
  return {
76171
- attemptId: (0, import_node_crypto12.randomUUID)(),
76776
+ attemptId: (0, import_node_crypto15.randomUUID)(),
76172
76777
  desiredRevision: desired.revision,
76173
76778
  skills: results
76174
76779
  };
76175
76780
  }
76176
76781
  async reuseCurrentProjection(entry) {
76177
- const destination = (0, import_node_path19.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76782
+ const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76178
76783
  const marker = await readMarker(destination);
76179
76784
  if (marker?.skillId !== entry.skillId || marker.versionId !== entry.versionId || marker.contentHash !== entry.contentHash || !await packageMatches(destination, entry.contentHash)) {
76180
76785
  return null;
@@ -76183,19 +76788,19 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76183
76788
  return entry.contentHash;
76184
76789
  }
76185
76790
  async adoptLegacyProjection(entry) {
76186
- const destination = (0, import_node_path19.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76791
+ const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76187
76792
  if (await pathExists2(destination)) return;
76188
76793
  const manifest = await readLegacyManifest(this.legacyManifestPath);
76189
76794
  const legacy = manifest?.skills[entry.slug];
76190
76795
  if (!legacy) return;
76191
- const source = (0, import_node_path19.join)(this.managedSkillsDirectory, entry.slug);
76796
+ const source = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.slug);
76192
76797
  if (!await legacyFileMatches(source, legacy.hash) || !await packageMatches(source, entry.contentHash)) {
76193
76798
  return;
76194
76799
  }
76195
76800
  await (0, import_promises5.rename)(source, destination);
76196
76801
  try {
76197
76802
  await (0, import_promises5.writeFile)(
76198
- (0, import_node_path19.join)(destination, ".alan-managed.json"),
76803
+ (0, import_node_path21.join)(destination, ".alan-managed.json"),
76199
76804
  `${JSON.stringify(markerFor(entry), null, 2)}
76200
76805
  `,
76201
76806
  { encoding: "utf8", flag: "wx", mode: 384 }
@@ -76204,19 +76809,19 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76204
76809
  await (0, import_promises5.rename)(destination, source).catch(() => void 0);
76205
76810
  throw error61;
76206
76811
  }
76207
- await removeExactLegacySkill((0, import_node_path19.join)(this.compatibilitySkillsDirectory, entry.slug), legacy.hash);
76812
+ await removeExactLegacySkill((0, import_node_path21.join)(this.compatibilitySkillsDirectory, entry.slug), legacy.hash);
76208
76813
  }
76209
76814
  async retireLegacySyncManifest() {
76210
76815
  const manifest = await readLegacyManifest(this.legacyManifestPath);
76211
76816
  if (!manifest) return;
76212
76817
  for (const [command, entry] of Object.entries(manifest.skills)) {
76213
- await removeExactLegacySkill((0, import_node_path19.join)(this.managedSkillsDirectory, command), entry.hash);
76214
- await removeExactLegacySkill((0, import_node_path19.join)(this.compatibilitySkillsDirectory, command), entry.hash);
76818
+ await removeExactLegacySkill((0, import_node_path21.join)(this.managedSkillsDirectory, command), entry.hash);
76819
+ await removeExactLegacySkill((0, import_node_path21.join)(this.compatibilitySkillsDirectory, command), entry.hash);
76215
76820
  }
76216
76821
  await (0, import_promises5.rm)(this.legacyManifestPath, { force: true });
76217
76822
  }
76218
76823
  async ensureCached(entry) {
76219
- const cacheDirectory = (0, import_node_path19.join)(
76824
+ const cacheDirectory = (0, import_node_path21.join)(
76220
76825
  this.skillsStateDirectory,
76221
76826
  "cache",
76222
76827
  "sha256",
@@ -76230,10 +76835,10 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76230
76835
  `Downloaded Skill bundle length ${bytes.byteLength} does not match ${entry.compressedBytes}`
76231
76836
  );
76232
76837
  }
76233
- const pendingDirectory = (0, import_node_path19.join)(
76838
+ const pendingDirectory = (0, import_node_path21.join)(
76234
76839
  this.skillsStateDirectory,
76235
76840
  "tmp",
76236
- `cache-${process.pid}-${(0, import_node_crypto12.randomBytes)(8).toString("hex")}`
76841
+ `cache-${process.pid}-${(0, import_node_crypto15.randomBytes)(8).toString("hex")}`
76237
76842
  );
76238
76843
  try {
76239
76844
  await extractSkillBundle(bytes, pendingDirectory, {
@@ -76251,22 +76856,22 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76251
76856
  return cacheDirectory;
76252
76857
  }
76253
76858
  async project(entry, cacheDirectory) {
76254
- const destination = (0, import_node_path19.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76859
+ const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76255
76860
  const existing = await readMarker(destination);
76256
76861
  if (existing?.skillId === entry.skillId && existing.versionId === entry.versionId && existing.contentHash === entry.contentHash && await packageMatches(destination, entry.contentHash)) {
76257
76862
  await this.ensureCompatibilityProjection(entry, destination);
76258
76863
  return entry.contentHash;
76259
76864
  }
76260
- const suffix = `${process.pid}-${(0, import_node_crypto12.randomBytes)(8).toString("hex")}`;
76261
- const pending = (0, import_node_path19.join)(this.managedSkillsDirectory, `.pending-${suffix}`);
76262
- const backup = (0, import_node_path19.join)(this.managedSkillsDirectory, `.backup-${suffix}`);
76865
+ const suffix = `${process.pid}-${(0, import_node_crypto15.randomBytes)(8).toString("hex")}`;
76866
+ const pending = (0, import_node_path21.join)(this.managedSkillsDirectory, `.pending-${suffix}`);
76867
+ const backup = (0, import_node_path21.join)(this.managedSkillsDirectory, `.backup-${suffix}`);
76263
76868
  let movedExisting = false;
76264
76869
  let installedPending = false;
76265
76870
  let completed = false;
76266
76871
  try {
76267
76872
  await (0, import_promises5.cp)(cacheDirectory, pending, { recursive: true, errorOnExist: true, force: false });
76268
76873
  await (0, import_promises5.writeFile)(
76269
- (0, import_node_path19.join)(pending, ".alan-managed.json"),
76874
+ (0, import_node_path21.join)(pending, ".alan-managed.json"),
76270
76875
  `${JSON.stringify(markerFor(entry), null, 2)}
76271
76876
  `,
76272
76877
  { encoding: "utf8", flag: "wx", mode: 384 }
@@ -76307,7 +76912,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76307
76912
  }
76308
76913
  }
76309
76914
  async requireReplaceable(entry) {
76310
- const destination = (0, import_node_path19.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76915
+ const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76311
76916
  if (!await pathExists2(destination)) return;
76312
76917
  const marker = await readMarker(destination);
76313
76918
  if (!marker) throw unmanagedPathConflict(entry.runtimeDirectory);
@@ -76324,8 +76929,8 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76324
76929
  if (!this.store.inspect().canRemoveManagedFiles) return failures;
76325
76930
  for (const [runtimeDirectory, recorded] of Object.entries(this.store.projections())) {
76326
76931
  if (desired[runtimeDirectory]) continue;
76327
- const destination = (0, import_node_path19.join)(this.managedSkillsDirectory, runtimeDirectory);
76328
- const compatibility = (0, import_node_path19.join)(this.compatibilitySkillsDirectory, runtimeDirectory);
76932
+ const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, runtimeDirectory);
76933
+ const compatibility = (0, import_node_path21.join)(this.compatibilitySkillsDirectory, runtimeDirectory);
76329
76934
  const marker = await readMarker(destination);
76330
76935
  if (!await pathExists2(destination)) continue;
76331
76936
  if (marker && markerMatchesProjection(marker, recorded)) {
@@ -76356,7 +76961,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76356
76961
  }
76357
76962
  async ensureCompatibilityProjection(entry, canonicalDirectory) {
76358
76963
  await (0, import_promises5.mkdir)(this.compatibilitySkillsDirectory, { recursive: true, mode: 448 });
76359
- const destination = (0, import_node_path19.join)(this.compatibilitySkillsDirectory, entry.runtimeDirectory);
76964
+ const destination = (0, import_node_path21.join)(this.compatibilitySkillsDirectory, entry.runtimeDirectory);
76360
76965
  if (await pathExists2(destination)) {
76361
76966
  if (await compatibilityProjectionMatches(destination, canonicalDirectory, entry)) return;
76362
76967
  throw unmanagedCompatibilityConflict(entry.runtimeDirectory);
@@ -76367,11 +76972,11 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76367
76972
  } catch (error61) {
76368
76973
  if (!isSymlinkUnsupported(error61)) throw error61;
76369
76974
  }
76370
- const pending = `${destination}.pending-${process.pid}-${(0, import_node_crypto12.randomBytes)(6).toString("hex")}`;
76975
+ const pending = `${destination}.pending-${process.pid}-${(0, import_node_crypto15.randomBytes)(6).toString("hex")}`;
76371
76976
  try {
76372
76977
  await (0, import_promises5.cp)(canonicalDirectory, pending, { recursive: true, errorOnExist: true, force: false });
76373
76978
  await (0, import_promises5.writeFile)(
76374
- (0, import_node_path19.join)(pending, ".alan-compatibility.json"),
76979
+ (0, import_node_path21.join)(pending, ".alan-compatibility.json"),
76375
76980
  `${JSON.stringify({ ...markerFor(entry), canonicalDirectory }, null, 2)}
76376
76981
  `,
76377
76982
  { encoding: "utf8", flag: "wx", mode: 384 }
@@ -76384,8 +76989,8 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76384
76989
  async runExclusively(work) {
76385
76990
  const previous = this.reconcileTail;
76386
76991
  let release2;
76387
- this.reconcileTail = new Promise((resolve14) => {
76388
- release2 = resolve14;
76992
+ this.reconcileTail = new Promise((resolve15) => {
76993
+ release2 = resolve15;
76389
76994
  });
76390
76995
  await previous;
76391
76996
  try {
@@ -76396,14 +77001,14 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76396
77001
  }
76397
77002
  async withMachineLock(work) {
76398
77003
  await (0, import_promises5.mkdir)(this.skillsStateDirectory, { recursive: true, mode: 448 });
76399
- const lockDirectory = (0, import_node_path19.join)(this.skillsStateDirectory, "manager.lock");
77004
+ const lockDirectory = (0, import_node_path21.join)(this.skillsStateDirectory, "manager.lock");
76400
77005
  const deadline = Date.now() + 3e4;
76401
77006
  while (true) {
76402
77007
  try {
76403
77008
  await (0, import_promises5.mkdir)(lockDirectory, { mode: 448 });
76404
77009
  try {
76405
77010
  await (0, import_promises5.writeFile)(
76406
- (0, import_node_path19.join)(lockDirectory, "owner.json"),
77011
+ (0, import_node_path21.join)(lockDirectory, "owner.json"),
76407
77012
  `${JSON.stringify({ pid: process.pid, acquiredAt: (/* @__PURE__ */ new Date()).toISOString() })}
76408
77013
  `,
76409
77014
  { encoding: "utf8", flag: "wx", mode: 384 }
@@ -76453,7 +77058,7 @@ async function packageMatches(directory, expectedHash) {
76453
77058
  async function readMarker(directory) {
76454
77059
  try {
76455
77060
  const value2 = JSON.parse(
76456
- await (0, import_promises5.readFile)((0, import_node_path19.join)(directory, ".alan-managed.json"), "utf8")
77061
+ await (0, import_promises5.readFile)((0, import_node_path21.join)(directory, ".alan-managed.json"), "utf8")
76457
77062
  );
76458
77063
  if (typeof value2 !== "object" || value2 === null || !("protocolVersion" in value2) || value2.protocolVersion !== SKILLS_PROTOCOL_VERSION || !("skillId" in value2) || typeof value2.skillId !== "string" || !("versionId" in value2) || typeof value2.versionId !== "string" || !("contentHash" in value2) || typeof value2.contentHash !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value2.contentHash)) {
76459
77064
  return null;
@@ -76467,7 +77072,7 @@ async function compatibilityProjectionMatches(destination, canonicalDirectory, e
76467
77072
  const metadata = await (0, import_promises5.lstat)(destination);
76468
77073
  if (metadata.isSymbolicLink()) {
76469
77074
  const target = await (0, import_promises5.readlink)(destination);
76470
- return (0, import_node_path19.resolve)((0, import_node_path19.dirname)(destination), target) === (0, import_node_path19.resolve)(canonicalDirectory);
77075
+ return (0, import_node_path21.resolve)((0, import_node_path21.dirname)(destination), target) === (0, import_node_path21.resolve)(canonicalDirectory);
76471
77076
  }
76472
77077
  if (!metadata.isDirectory()) return false;
76473
77078
  const marker = await readCompatibilityMarker(destination);
@@ -76478,7 +77083,7 @@ async function removeCompatibilityProjection(destination, canonicalDirectory, pr
76478
77083
  const metadata = await (0, import_promises5.lstat)(destination);
76479
77084
  if (metadata.isSymbolicLink()) {
76480
77085
  const target = await (0, import_promises5.readlink)(destination);
76481
- if ((0, import_node_path19.resolve)((0, import_node_path19.dirname)(destination), target) !== (0, import_node_path19.resolve)(canonicalDirectory)) return false;
77086
+ if ((0, import_node_path21.resolve)((0, import_node_path21.dirname)(destination), target) !== (0, import_node_path21.resolve)(canonicalDirectory)) return false;
76482
77087
  await (0, import_promises5.rm)(destination, { force: true });
76483
77088
  return true;
76484
77089
  }
@@ -76490,7 +77095,7 @@ async function removeCompatibilityProjection(destination, canonicalDirectory, pr
76490
77095
  async function readCompatibilityMarker(directory) {
76491
77096
  try {
76492
77097
  const value2 = JSON.parse(
76493
- await (0, import_promises5.readFile)((0, import_node_path19.join)(directory, ".alan-compatibility.json"), "utf8")
77098
+ await (0, import_promises5.readFile)((0, import_node_path21.join)(directory, ".alan-compatibility.json"), "utf8")
76494
77099
  );
76495
77100
  if (value2.protocolVersion !== SKILLS_PROTOCOL_VERSION || typeof value2.skillId !== "string" || typeof value2.versionId !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value2.contentHash) || typeof value2.canonicalDirectory !== "string") {
76496
77101
  return null;
@@ -76530,15 +77135,15 @@ async function legacyFileMatches(directory, expectedHash) {
76530
77135
  try {
76531
77136
  const metadata = await (0, import_promises5.lstat)(directory);
76532
77137
  if (!metadata.isDirectory() || metadata.isSymbolicLink()) return false;
76533
- const content = await (0, import_promises5.readFile)((0, import_node_path19.join)(directory, "SKILL.md"));
76534
- return (0, import_node_crypto12.createHash)("sha256").update(content).digest("hex").slice(0, 16) === expectedHash;
77138
+ const content = await (0, import_promises5.readFile)((0, import_node_path21.join)(directory, "SKILL.md"));
77139
+ return (0, import_node_crypto15.createHash)("sha256").update(content).digest("hex").slice(0, 16) === expectedHash;
76535
77140
  } catch {
76536
77141
  return false;
76537
77142
  }
76538
77143
  }
76539
77144
  async function removeExactLegacySkill(directory, expectedHash) {
76540
77145
  if (!await legacyFileMatches(directory, expectedHash)) return;
76541
- await (0, import_promises5.rm)((0, import_node_path19.join)(directory, "SKILL.md"), { force: true });
77146
+ await (0, import_promises5.rm)((0, import_node_path21.join)(directory, "SKILL.md"), { force: true });
76542
77147
  const remaining = await (0, import_promises5.readdir)(directory);
76543
77148
  if (remaining.length === 0) await (0, import_promises5.rm)(directory, { recursive: true, force: true });
76544
77149
  }
@@ -76656,10 +77261,10 @@ var SuspensionDetector = class {
76656
77261
  async function startDaemon(args) {
76657
77262
  bindConfigPath(args);
76658
77263
  const stored = readConfig();
76659
- const installationId = stored.installationId ?? (0, import_node_crypto13.randomUUID)();
77264
+ const installationId = stored.installationId ?? (0, import_node_crypto16.randomUUID)();
76660
77265
  const configPath = getConfigPath();
76661
77266
  const endpointProfile = stored.endpointProfile ?? resolveEndpointProfile(args);
76662
- const daemonSessionId = (0, import_node_crypto13.randomUUID)();
77267
+ const daemonSessionId = (0, import_node_crypto16.randomUUID)();
76663
77268
  let heartbeatSeq = 0;
76664
77269
  const runtimeId = argValue(args, "--runtime-id") ?? process.env.ALAN_RUNTIME_ID ?? stored.runtimeId;
76665
77270
  const runtimeTokenOverride = argValue(args, "--token") ?? process.env.ALAN_RUNTIME_TOKEN;
@@ -76676,11 +77281,11 @@ async function startDaemon(args) {
76676
77281
  wsUrl = endpointMismatch.expectedWsUrl;
76677
77282
  }
76678
77283
  let localApiPort = getLocalApiPort(args, stored);
76679
- const localApiToken = stored.localApiToken ?? (0, import_node_crypto13.randomBytes)(24).toString("hex");
76680
- const localServiceId = stored.localServiceId ?? (0, import_node_crypto13.randomUUID)();
76681
- const localServiceSecret = stored.localServiceSecret ?? (0, import_node_crypto13.randomBytes)(32).toString("base64url");
77284
+ const localApiToken = stored.localApiToken ?? (0, import_node_crypto16.randomBytes)(24).toString("hex");
77285
+ const localServiceId = stored.localServiceId ?? (0, import_node_crypto16.randomUUID)();
77286
+ const localServiceSecret = stored.localServiceSecret ?? (0, import_node_crypto16.randomBytes)(32).toString("base64url");
76682
77287
  const localServiceStartedAtMs = Date.now();
76683
- const eventSpoolKey = stored.eventSpoolKey ?? (0, import_node_crypto13.randomBytes)(32).toString("base64url");
77288
+ const eventSpoolKey = stored.eventSpoolKey ?? (0, import_node_crypto16.randomBytes)(32).toString("base64url");
76684
77289
  if (stored.daemonEpoch !== void 0 && (!Number.isSafeInteger(stored.daemonEpoch) || stored.daemonEpoch < 0 || stored.daemonEpoch >= Number.MAX_SAFE_INTEGER)) {
76685
77290
  throw new Error("daemon config contains an invalid durable epoch; repair the runtime config");
76686
77291
  }
@@ -76778,17 +77383,17 @@ async function startDaemon(args) {
76778
77383
  installationId
76779
77384
  });
76780
77385
  const recentLogs = [];
76781
- const daemonLogPath = (0, import_node_path20.join)((0, import_node_path20.dirname)(configPath), "logs", "daemon.log");
77386
+ const daemonLogPath = (0, import_node_path22.join)((0, import_node_path22.dirname)(configPath), "logs", "daemon.log");
76782
77387
  const persistDaemonLog = (line) => {
76783
77388
  try {
76784
- (0, import_node_fs17.mkdirSync)((0, import_node_path20.dirname)(daemonLogPath), { recursive: true });
77389
+ (0, import_node_fs20.mkdirSync)((0, import_node_path22.dirname)(daemonLogPath), { recursive: true });
76785
77390
  try {
76786
- if ((0, import_node_fs17.statSync)(daemonLogPath).size > 5 * 1024 * 1024) {
76787
- (0, import_node_fs17.renameSync)(daemonLogPath, `${daemonLogPath}.1`);
77391
+ if ((0, import_node_fs20.statSync)(daemonLogPath).size > 5 * 1024 * 1024) {
77392
+ (0, import_node_fs20.renameSync)(daemonLogPath, `${daemonLogPath}.1`);
76788
77393
  }
76789
77394
  } catch {
76790
77395
  }
76791
- (0, import_node_fs17.appendFileSync)(daemonLogPath, `${line}
77396
+ (0, import_node_fs20.appendFileSync)(daemonLogPath, `${line}
76792
77397
  `, "utf8");
76793
77398
  } catch {
76794
77399
  }
@@ -76807,24 +77412,28 @@ async function startDaemon(args) {
76807
77412
  const alanMcpRegisteredRuns = /* @__PURE__ */ new Map();
76808
77413
  const alanMcpRegisteredBackends = /* @__PURE__ */ new Set();
76809
77414
  const finalizeAlanMcpForRun = (runId) => {
76810
- const registration = alanMcpRegisteredRuns.get(runId);
76811
77415
  if (!alanMcpRegisteredRuns.delete(runId)) return;
76812
77416
  if (alanMcpRegisteredRuns.size > 0) return;
76813
- const backends = [...alanMcpRegisteredBackends];
76814
- alanMcpRegisteredBackends.clear();
76815
77417
  void (async () => {
76816
- for (const backend of backends) {
77418
+ const releaseRunStart = await runStartGate.acquire();
77419
+ try {
76817
77420
  if (alanMcpRegisteredRuns.size > 0) return;
76818
- try {
76819
- const { removedPaths } = await unregisterAlanMcp(backend, void 0, {
76820
- onlyIfRegistration: registration
76821
- });
76822
- if (removedPaths.length > 0) {
76823
- pushLog(`mcp-unregistered backend=${backend} run=${runId}`);
77421
+ const backends = [...alanMcpRegisteredBackends];
77422
+ alanMcpRegisteredBackends.clear();
77423
+ for (const backend of backends) {
77424
+ try {
77425
+ const result = await restoreStaticAlanMcp(backend, apiUrl);
77426
+ if (result.ok) {
77427
+ pushLog(`mcp-static-restored backend=${backend} run=${runId}`);
77428
+ } else {
77429
+ pushLog(`mcp-static-restore-failed backend=${backend} ${result.error ?? "unknown"}`);
77430
+ }
77431
+ } catch (err) {
77432
+ pushLog(`mcp-static-restore-failed backend=${backend} ${err.message}`);
76824
77433
  }
76825
- } catch (err) {
76826
- pushLog(`mcp-unregister-failed backend=${backend} ${err.message}`);
76827
77434
  }
77435
+ } finally {
77436
+ releaseRunStart();
76828
77437
  }
76829
77438
  })();
76830
77439
  };
@@ -76836,6 +77445,8 @@ async function startDaemon(args) {
76836
77445
  return workspaceAccessTracker.recoveryPath() ?? void 0;
76837
77446
  };
76838
77447
  let eventOutbox;
77448
+ let nativeHookInbox;
77449
+ let nativeTranscriptStreamer;
76839
77450
  let runJournal;
76840
77451
  try {
76841
77452
  eventOutbox = new EncryptedEventOutbox(
@@ -76843,6 +77454,13 @@ async function startDaemon(args) {
76843
77454
  eventSpoolKey,
76844
77455
  pushLog
76845
77456
  );
77457
+ nativeHookInbox = new EncryptedNativeHookInbox(nativeHookInboxPath(configPath), eventSpoolKey, {
77458
+ recoverClaims: true,
77459
+ onQuarantine: ({ fileName, error: error61 }) => pushLog(`native-hook-quarantined file=${fileName} error=${error61.message}`)
77460
+ });
77461
+ nativeTranscriptStreamer = new NativeTranscriptStreamer(
77462
+ new NativeTranscriptCursorStore(`${configPath}.native-transcript-cursors.json`)
77463
+ );
76846
77464
  runJournal = new RuntimeRunJournal(`${configPath}.run-journal.json`);
76847
77465
  } catch (error61) {
76848
77466
  releaseOwnerLease();
@@ -76850,9 +77468,57 @@ async function startDaemon(args) {
76850
77468
  throw error61;
76851
77469
  }
76852
77470
  const workspaceRelocations = /* @__PURE__ */ new Map();
77471
+ let nativeHookDrainInFlight = false;
77472
+ const flushNativeHookInbox = async () => {
77473
+ if (nativeHookDrainInFlight || !socket.connected) return;
77474
+ nativeHookDrainInFlight = true;
77475
+ try {
77476
+ const result = await drainNativeHookInbox({
77477
+ inbox: nativeHookInbox,
77478
+ transcriptStreamer: nativeTranscriptStreamer,
77479
+ sink: {
77480
+ deliver: (envelope) => new Promise((resolveDelivery, rejectDelivery) => {
77481
+ socket.timeout(5e3).emit(
77482
+ "native.session.event",
77483
+ envelope,
77484
+ (error61, acknowledgement) => {
77485
+ const accepted = !error61 && acknowledgement?.ok === true;
77486
+ if (!accepted) {
77487
+ pushLog(
77488
+ `native-hook-delivery-deferred event=${envelope.eventId} provider=${envelope.provider} error=${error61?.message ?? "none"} ack=${acknowledgement?.ok ?? "missing"} retryable=${acknowledgement?.retryable ?? "missing"}`
77489
+ );
77490
+ }
77491
+ if (!error61 && acknowledgement?.ok === false && acknowledgement.retryable === false) {
77492
+ rejectDelivery(
77493
+ new PermanentNativeHookDeliveryError(
77494
+ `backend permanently rejected native event ${envelope.eventId}`
77495
+ )
77496
+ );
77497
+ return;
77498
+ }
77499
+ resolveDelivery(accepted);
77500
+ }
77501
+ );
77502
+ })
77503
+ },
77504
+ onTranscriptError: ({ envelope, error: error61 }) => pushLog(
77505
+ `native-transcript-unavailable event=${envelope.eventId} provider=${envelope.provider} error=${error61.message}`
77506
+ )
77507
+ });
77508
+ if (result.attempted > 0) {
77509
+ pushLog(
77510
+ `native-hook-drain attempted=${result.attempted} acknowledged=${result.acknowledged} deferred=${result.deferred}`
77511
+ );
77512
+ }
77513
+ } catch (error61) {
77514
+ pushLog(`native-hook-drain-failed ${error61.message}`);
77515
+ } finally {
77516
+ nativeHookDrainInFlight = false;
77517
+ }
77518
+ };
76853
77519
  for (const relocation of stored.workspaceRelocations ?? []) {
76854
77520
  if (relocation?.conversationId && relocation.runId && relocation.previousPath && relocation.canonicalPath) {
76855
- workspaceRelocations.set((0, import_node_path20.resolve)(relocation.previousPath), relocation);
77521
+ workspaceRelocations.set((0, import_node_path22.resolve)(relocation.previousPath), relocation);
76856
77522
  }
76857
77523
  }
76858
77524
  if (stored.localApiPort !== localApiPort || stored.localApiToken !== localApiToken || stored.localServiceId !== localServiceId || stored.localServiceSecret !== localServiceSecret || stored.eventSpoolKey !== eventSpoolKey || stored.daemonEpoch !== daemonEpoch || stored.installationId !== installationId) {
@@ -77039,6 +77705,7 @@ async function startDaemon(args) {
77039
77705
  if (!socket.connected) return;
77040
77706
  emitRunnerPresence();
77041
77707
  flushWorkspaceRelocations();
77708
+ void flushNativeHookInbox();
77042
77709
  }, 15e3);
77043
77710
  let pendingWakeGapMs = null;
77044
77711
  const flushWakeGap = () => {
@@ -77401,7 +78068,7 @@ async function startDaemon(args) {
77401
78068
  `${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit Alan desktop or run setup again.`
77402
78069
  );
77403
78070
  }
77404
- const localApiListening = new Promise((resolve14, reject) => {
78071
+ const localApiListening = new Promise((resolve15, reject) => {
77405
78072
  const onError = (error61) => {
77406
78073
  clearInterval(heartbeat);
77407
78074
  clearInterval(versionRefresh);
@@ -77431,7 +78098,7 @@ async function startDaemon(args) {
77431
78098
  if (address && typeof address === "object") localApiPort = address.port;
77432
78099
  pushLog(`local_api listening port=${localApiPort}`);
77433
78100
  console.info("[alan-agent] Local daemon API listening", { port: localApiPort });
77434
- resolve14();
78101
+ resolve15();
77435
78102
  });
77436
78103
  });
77437
78104
  try {
@@ -77451,7 +78118,7 @@ async function startDaemon(args) {
77451
78118
  clearInterval(workspaceLeaseMonitor);
77452
78119
  suspensionDetector.stop();
77453
78120
  socket.disconnect();
77454
- await new Promise((resolve14) => localServer.close(() => resolve14()));
78121
+ await new Promise((resolve15) => localServer.close(() => resolve15()));
77455
78122
  releaseOwnerLease();
77456
78123
  releaseStartupLease();
77457
78124
  throw error61;
@@ -77461,6 +78128,8 @@ async function startDaemon(args) {
77461
78128
  skillSyncInterval.unref?.();
77462
78129
  const skillAuditInterval = setInterval(() => runSkillSync("refresh"), 24 * 60 * 6e4);
77463
78130
  skillAuditInterval.unref?.();
78131
+ const nativeHookDrainInterval = setInterval(() => void flushNativeHookInbox(), 1e3);
78132
+ nativeHookDrainInterval.unref?.();
77464
78133
  let stopped = false;
77465
78134
  const stop = async () => {
77466
78135
  if (stopped) return;
@@ -77471,14 +78140,15 @@ async function startDaemon(args) {
77471
78140
  clearInterval(workspaceLeaseMonitor);
77472
78141
  clearInterval(skillSyncInterval);
77473
78142
  clearInterval(skillAuditInterval);
78143
+ clearInterval(nativeHookDrainInterval);
77474
78144
  suspensionDetector.stop();
77475
78145
  for (const entry of activeAgents.values()) {
77476
78146
  abortActiveAgent(entry, { userInitiated: true });
77477
78147
  }
77478
78148
  activeAgents.clear();
77479
78149
  socket.disconnect();
77480
- await new Promise((resolve14) => {
77481
- localServer.close(() => resolve14());
78150
+ await new Promise((resolve15) => {
78151
+ localServer.close(() => resolve15());
77482
78152
  });
77483
78153
  } finally {
77484
78154
  sleepInhibitor.dispose();
@@ -77558,7 +78228,7 @@ async function setupDaemon(args) {
77558
78228
  const token = argValue(args, "--token") ?? process.env.ALAN_SETUP_TOKEN;
77559
78229
  const displayName = argValue(args, "--name") ?? (0, import_node_os11.hostname)();
77560
78230
  const scope = argValue(args, "--scope") ?? process.env.ALAN_RUNTIME_SCOPE ?? (setupToken ? "team" : "user");
77561
- const installationId = argValue(args, "--installation-id") ?? previousConfig.installationId ?? (0, import_node_crypto14.randomUUID)();
78231
+ const installationId = argValue(args, "--installation-id") ?? previousConfig.installationId ?? (0, import_node_crypto17.randomUUID)();
77562
78232
  if (scope !== "team" && scope !== "user") {
77563
78233
  throw new Error("setup --scope must be either 'team' or 'user'");
77564
78234
  }
@@ -77567,7 +78237,7 @@ async function setupDaemon(args) {
77567
78237
  "setup requires --setup-token <token> or --token <Alan access token>.\nFor normal setup, copy the setup token from Alan and run: alan-agent setup --setup-token <token>\nFor browser authorization, run: alan-agent login"
77568
78238
  );
77569
78239
  }
77570
- const registrationAttemptId = setupToken ? getOrCreateSetupAttemptId(setupToken) : (0, import_node_crypto14.randomUUID)();
78240
+ const registrationAttemptId = setupToken ? getOrCreateSetupAttemptId(setupToken) : (0, import_node_crypto17.randomUUID)();
77571
78241
  const registrationUrl = `${apiUrl}/public/runtimes${setupToken ? "/register-with-setup-token" : ""}`;
77572
78242
  const registrationInit = {
77573
78243
  method: "POST",
@@ -77602,7 +78272,7 @@ async function setupDaemon(args) {
77602
78272
  }
77603
78273
  const retriableStatus = response && (response.status === 408 || response.status === 429 || response.status >= 500);
77604
78274
  if (response && (!retriableStatus || response.ok) || attempt === 3) break;
77605
- await new Promise((resolve14) => setTimeout(resolve14, attempt * 100));
78275
+ await new Promise((resolve15) => setTimeout(resolve15, attempt * 100));
77606
78276
  }
77607
78277
  if (!response) {
77608
78278
  const detail = transportError instanceof Error ? transportError.message : String(transportError);
@@ -77633,10 +78303,10 @@ async function setupDaemon(args) {
77633
78303
  runtimeToken: body.runtimeToken,
77634
78304
  runtimeRenewalToken: body.runtimeRenewalToken,
77635
78305
  localApiPort: getLocalApiPort(args, {}),
77636
- localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto14.randomBytes)(24).toString("hex"),
77637
- localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto14.randomUUID)(),
77638
- localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto14.randomBytes)(32).toString("base64url"),
77639
- eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto14.randomBytes)(32).toString("base64url"),
78306
+ localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto17.randomBytes)(24).toString("hex"),
78307
+ localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto17.randomUUID)(),
78308
+ localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
78309
+ eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
77640
78310
  daemonEpoch: previousConfig.daemonEpoch,
77641
78311
  displayName: body.runtime.displayName ?? displayName,
77642
78312
  installationId
@@ -77666,7 +78336,7 @@ async function loginWithDeviceCode(args) {
77666
78336
  const displayName = argValue(args, "--name") ?? (0, import_node_os11.hostname)();
77667
78337
  const maxPolls = Number.parseInt(argValue(args, "--max-polls") ?? "300", 10);
77668
78338
  const resumed = readPendingDeviceAuthorization(apiUrl);
77669
- const installationId = resumed?.installationId ?? previousConfig.installationId ?? (0, import_node_crypto14.randomUUID)();
78339
+ const installationId = resumed?.installationId ?? previousConfig.installationId ?? (0, import_node_crypto17.randomUUID)();
77670
78340
  let authorization = resumed ? { ...resumed, operationId: operation.id } : null;
77671
78341
  if (!authorization) {
77672
78342
  clearPendingDeviceAuthorization();
@@ -77742,10 +78412,10 @@ async function loginWithDeviceCode(args) {
77742
78412
  runtimeToken: body.runtimeToken,
77743
78413
  runtimeRenewalToken: body.runtimeRenewalToken,
77744
78414
  localApiPort: getLocalApiPort(args, {}),
77745
- localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto14.randomBytes)(24).toString("hex"),
77746
- localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto14.randomUUID)(),
77747
- localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto14.randomBytes)(32).toString("base64url"),
77748
- eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto14.randomBytes)(32).toString("base64url"),
78415
+ localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto17.randomBytes)(24).toString("hex"),
78416
+ localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto17.randomUUID)(),
78417
+ localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
78418
+ eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
77749
78419
  daemonEpoch: previousConfig.daemonEpoch,
77750
78420
  displayName: body.runtime.displayName ?? displayName,
77751
78421
  installationId
@@ -78096,6 +78766,860 @@ async function printDoctor(args = []) {
78096
78766
  );
78097
78767
  }
78098
78768
 
78769
+ // src/mcp-config-adapters.ts
78770
+ var import_node_crypto18 = require("crypto");
78771
+ var import_node_fs21 = require("fs");
78772
+ var import_node_os12 = require("os");
78773
+ var import_node_path23 = require("path");
78774
+ var log = {
78775
+ info: (...args) => console.info("[mcp-config-adapters]", ...args),
78776
+ warn: (...args) => console.warn("[mcp-config-adapters]", ...args),
78777
+ debug: () => {
78778
+ }
78779
+ };
78780
+ var HOME = (0, import_node_os12.homedir)();
78781
+ var PATHS = {
78782
+ claude: (0, import_node_path23.join)(HOME, ".claude.json"),
78783
+ codex: (0, import_node_path23.join)(HOME, ".codex", "config.toml"),
78784
+ cursor: (0, import_node_path23.join)(HOME, ".cursor", "mcp.json"),
78785
+ antigravity: (0, import_node_path23.join)(HOME, ".gemini", "config", "mcp_config.json"),
78786
+ kimi: (0, import_node_path23.join)(HOME, ".kimi", "mcp.json"),
78787
+ opencode: (0, import_node_path23.join)(HOME, ".config", "opencode", "opencode.json"),
78788
+ factory: (0, import_node_path23.join)(HOME, ".factory", "mcp.json"),
78789
+ amp: (0, import_node_path23.join)(HOME, ".config", "amp", "settings.json")
78790
+ };
78791
+ var MalformedToolConfigError = class extends Error {
78792
+ };
78793
+ function parseJsonSafe2(content) {
78794
+ if (!content) return {};
78795
+ try {
78796
+ const parsed = JSON.parse(content);
78797
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
78798
+ return parsed;
78799
+ }
78800
+ } catch {
78801
+ }
78802
+ throw new MalformedToolConfigError("Existing tool config is malformed");
78803
+ }
78804
+ function readFileOr(path2) {
78805
+ return (0, import_node_fs21.existsSync)(path2) ? (0, import_node_fs21.readFileSync)(path2, "utf-8") : null;
78806
+ }
78807
+ function acquireConfigLock2(path2) {
78808
+ (0, import_node_fs21.mkdirSync)((0, import_node_path23.dirname)(path2), { recursive: true });
78809
+ const lockPath = `${path2}.alan-lock`;
78810
+ const startedAt = Date.now();
78811
+ while (true) {
78812
+ try {
78813
+ (0, import_node_fs21.mkdirSync)(lockPath);
78814
+ return () => (0, import_node_fs21.rmSync)(lockPath, { recursive: true, force: true });
78815
+ } catch (error61) {
78816
+ const code = error61 && typeof error61 === "object" && "code" in error61 ? error61.code : void 0;
78817
+ if (code !== "EEXIST") throw error61;
78818
+ try {
78819
+ if (Date.now() - (0, import_node_fs21.statSync)(lockPath).mtimeMs > 3e4) {
78820
+ (0, import_node_fs21.rmSync)(lockPath, { recursive: true, force: true });
78821
+ continue;
78822
+ }
78823
+ } catch {
78824
+ continue;
78825
+ }
78826
+ if (Date.now() - startedAt >= 2e3) {
78827
+ throw new Error(`Alan could not safely update ${path2}`);
78828
+ }
78829
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);
78830
+ }
78831
+ }
78832
+ }
78833
+ function writeFileSafe2(path2, content) {
78834
+ (0, import_node_fs21.mkdirSync)((0, import_node_path23.dirname)(path2), { recursive: true });
78835
+ const pendingPath = `${path2}.alan-pending-${process.pid}-${(0, import_node_crypto18.randomBytes)(6).toString("hex")}`;
78836
+ try {
78837
+ (0, import_node_fs21.writeFileSync)(pendingPath, content, { encoding: "utf8", mode: 384, flag: "wx" });
78838
+ (0, import_node_fs21.renameSync)(pendingPath, path2);
78839
+ } finally {
78840
+ (0, import_node_fs21.rmSync)(pendingPath, { force: true });
78841
+ }
78842
+ }
78843
+ function saveMalformedBackup2(path2, content) {
78844
+ const hash2 = (0, import_node_crypto18.createHash)("sha256").update(content).digest("hex").slice(0, 16);
78845
+ const backupPath = `${path2}.alan-backup-${hash2}`;
78846
+ if (!(0, import_node_fs21.existsSync)(backupPath)) (0, import_node_fs21.copyFileSync)(path2, backupPath);
78847
+ return backupPath;
78848
+ }
78849
+ var claudeAdapter = {
78850
+ cli: "Claude Code",
78851
+ configPath: PATHS.claude,
78852
+ guardDir: HOME,
78853
+ // ~/.claude.json lives in home — always exists
78854
+ merge(existing, servers) {
78855
+ const config2 = parseJsonSafe2(existing);
78856
+ const mcpServers = config2.mcpServers ?? {};
78857
+ for (const s of servers) {
78858
+ if (s.kind === "http") {
78859
+ mcpServers[s.name] = { type: "http", url: s.url, headers: s.headers };
78860
+ } else {
78861
+ mcpServers[s.name] = {
78862
+ command: s.command,
78863
+ ...s.args?.length ? { args: s.args } : {},
78864
+ ...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
78865
+ };
78866
+ }
78867
+ }
78868
+ config2.mcpServers = mcpServers;
78869
+ return JSON.stringify(config2, null, 2);
78870
+ },
78871
+ remove(existing, serverNames) {
78872
+ const config2 = parseJsonSafe2(existing);
78873
+ const mcpServers = config2.mcpServers ?? {};
78874
+ for (const name of serverNames) delete mcpServers[name];
78875
+ config2.mcpServers = mcpServers;
78876
+ return JSON.stringify(config2, null, 2);
78877
+ }
78878
+ };
78879
+ var codexAdapter = {
78880
+ cli: "Codex",
78881
+ configPath: PATHS.codex,
78882
+ guardDir: (0, import_node_path23.join)(HOME, ".codex"),
78883
+ merge(existing, servers) {
78884
+ let config2 = {};
78885
+ if (existing) {
78886
+ try {
78887
+ config2 = parse5(existing);
78888
+ } catch {
78889
+ throw new MalformedToolConfigError("Existing tool config is malformed");
78890
+ }
78891
+ }
78892
+ const mcpServers = config2.mcp_servers ?? {};
78893
+ for (const s of servers) {
78894
+ if (s.kind === "http") {
78895
+ mcpServers[s.name] = { url: s.url, http_headers: s.headers };
78896
+ } else {
78897
+ mcpServers[s.name] = {
78898
+ command: s.command,
78899
+ ...s.args?.length ? { args: s.args } : {},
78900
+ ...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
78901
+ };
78902
+ }
78903
+ }
78904
+ config2.mcp_servers = mcpServers;
78905
+ return stringify(config2);
78906
+ },
78907
+ remove(existing, serverNames) {
78908
+ if (!existing) return "";
78909
+ let config2 = {};
78910
+ try {
78911
+ config2 = parse5(existing);
78912
+ } catch {
78913
+ return existing;
78914
+ }
78915
+ const mcpServers = config2.mcp_servers ?? {};
78916
+ for (const name of serverNames) delete mcpServers[name];
78917
+ config2.mcp_servers = mcpServers;
78918
+ return stringify(config2);
78919
+ }
78920
+ };
78921
+ var cursorAdapter = {
78922
+ cli: "Cursor",
78923
+ configPath: PATHS.cursor,
78924
+ guardDir: (0, import_node_path23.join)(HOME, ".cursor"),
78925
+ merge(existing, servers) {
78926
+ const config2 = parseJsonSafe2(existing);
78927
+ const mcpServers = config2.mcpServers ?? {};
78928
+ for (const s of servers) {
78929
+ if (s.kind === "http") {
78930
+ mcpServers[s.name] = { type: "http", url: s.url, headers: s.headers };
78931
+ } else {
78932
+ mcpServers[s.name] = {
78933
+ command: s.command,
78934
+ ...s.args?.length ? { args: s.args } : {},
78935
+ ...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
78936
+ };
78937
+ }
78938
+ }
78939
+ config2.mcpServers = mcpServers;
78940
+ return JSON.stringify(config2, null, 2);
78941
+ },
78942
+ remove(existing, serverNames) {
78943
+ const config2 = parseJsonSafe2(existing);
78944
+ const mcpServers = config2.mcpServers ?? {};
78945
+ for (const name of serverNames) delete mcpServers[name];
78946
+ config2.mcpServers = mcpServers;
78947
+ return JSON.stringify(config2, null, 2);
78948
+ }
78949
+ };
78950
+ function createAntigravityAdapter() {
78951
+ return {
78952
+ cli: "Antigravity CLI",
78953
+ configPath: PATHS.antigravity,
78954
+ guardDir: (0, import_node_path23.join)(HOME, ".gemini", "config"),
78955
+ merge(existing, servers) {
78956
+ const config2 = parseJsonSafe2(existing);
78957
+ const mcpServers = config2.mcpServers ?? {};
78958
+ for (const s of servers) {
78959
+ if (s.kind === "http") {
78960
+ mcpServers[s.name] = { serverUrl: s.url, headers: s.headers };
78961
+ } else {
78962
+ mcpServers[s.name] = {
78963
+ command: s.command,
78964
+ ...s.args?.length ? { args: s.args } : {},
78965
+ ...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
78966
+ };
78967
+ }
78968
+ }
78969
+ config2.mcpServers = mcpServers;
78970
+ return JSON.stringify(config2, null, 2);
78971
+ },
78972
+ remove(existing, serverNames) {
78973
+ const config2 = parseJsonSafe2(existing);
78974
+ const mcpServers = config2.mcpServers ?? {};
78975
+ for (const name of serverNames) delete mcpServers[name];
78976
+ config2.mcpServers = mcpServers;
78977
+ return JSON.stringify(config2, null, 2);
78978
+ }
78979
+ };
78980
+ }
78981
+ var antigravityAdapter = createAntigravityAdapter();
78982
+ var kimiAdapter = {
78983
+ cli: "Kimi CLI",
78984
+ configPath: PATHS.kimi,
78985
+ guardDir: (0, import_node_path23.join)(HOME, ".kimi"),
78986
+ merge(existing, servers) {
78987
+ const config2 = parseJsonSafe2(existing);
78988
+ const mcpServers = config2.mcpServers ?? {};
78989
+ for (const s of servers) {
78990
+ if (s.kind === "http") {
78991
+ mcpServers[s.name] = { url: s.url, headers: s.headers };
78992
+ } else {
78993
+ mcpServers[s.name] = {
78994
+ command: s.command,
78995
+ ...s.args?.length ? { args: s.args } : {},
78996
+ ...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
78997
+ };
78998
+ }
78999
+ }
79000
+ config2.mcpServers = mcpServers;
79001
+ return JSON.stringify(config2, null, 2);
79002
+ },
79003
+ remove(existing, serverNames) {
79004
+ const config2 = parseJsonSafe2(existing);
79005
+ const mcpServers = config2.mcpServers ?? {};
79006
+ for (const name of serverNames) delete mcpServers[name];
79007
+ config2.mcpServers = mcpServers;
79008
+ return JSON.stringify(config2, null, 2);
79009
+ }
79010
+ };
79011
+ var opencodeAdapter = {
79012
+ cli: "OpenCode",
79013
+ configPath: PATHS.opencode,
79014
+ guardDir: (0, import_node_path23.join)(HOME, ".config", "opencode"),
79015
+ merge(existing, servers) {
79016
+ const config2 = parseJsonSafe2(existing);
79017
+ const mcp = config2.mcp ?? {};
79018
+ for (const s of servers) {
79019
+ if (s.kind === "http") {
79020
+ mcp[s.name] = { type: "remote", url: s.url, headers: s.headers };
79021
+ } else {
79022
+ mcp[s.name] = {
79023
+ type: "local",
79024
+ command: [s.command, ...s.args ?? []],
79025
+ ...s.env && Object.keys(s.env).length > 0 ? { environment: s.env } : {}
79026
+ };
79027
+ }
79028
+ }
79029
+ config2.mcp = mcp;
79030
+ return JSON.stringify(config2, null, 2);
79031
+ },
79032
+ remove(existing, serverNames) {
79033
+ const config2 = parseJsonSafe2(existing);
79034
+ const mcp = config2.mcp ?? {};
79035
+ for (const name of serverNames) delete mcp[name];
79036
+ config2.mcp = mcp;
79037
+ return JSON.stringify(config2, null, 2);
79038
+ }
79039
+ };
79040
+ var factoryAdapter = {
79041
+ cli: "Factory Droid",
79042
+ configPath: PATHS.factory,
79043
+ guardDir: (0, import_node_path23.join)(HOME, ".factory"),
79044
+ merge(existing, servers) {
79045
+ const config2 = parseJsonSafe2(existing);
79046
+ const mcpServers = config2.mcpServers ?? {};
79047
+ for (const s of servers) {
79048
+ if (s.kind === "http") {
79049
+ mcpServers[s.name] = {
79050
+ type: "http",
79051
+ url: s.url,
79052
+ disabled: false,
79053
+ ...s.headers && Object.keys(s.headers).length > 0 ? { headers: s.headers } : {}
79054
+ };
79055
+ } else {
79056
+ mcpServers[s.name] = {
79057
+ type: "stdio",
79058
+ command: s.command,
79059
+ disabled: false,
79060
+ ...s.args?.length ? { args: s.args } : {},
79061
+ ...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
79062
+ };
79063
+ }
79064
+ }
79065
+ config2.mcpServers = mcpServers;
79066
+ return JSON.stringify(config2, null, 2);
79067
+ },
79068
+ remove(existing, serverNames) {
79069
+ const config2 = parseJsonSafe2(existing);
79070
+ const mcpServers = config2.mcpServers ?? {};
79071
+ for (const name of serverNames) delete mcpServers[name];
79072
+ config2.mcpServers = mcpServers;
79073
+ return JSON.stringify(config2, null, 2);
79074
+ }
79075
+ };
79076
+ var ampAdapter = {
79077
+ cli: "Amp",
79078
+ configPath: PATHS.amp,
79079
+ guardDir: (0, import_node_path23.join)(HOME, ".config", "amp"),
79080
+ merge(existing, servers) {
79081
+ const config2 = parseJsonSafe2(existing);
79082
+ const mcpServers = config2["amp.mcpServers"] ?? {};
79083
+ for (const s of servers) {
79084
+ if (s.kind === "http") {
79085
+ mcpServers[s.name] = { url: s.url, headers: s.headers };
79086
+ } else {
79087
+ mcpServers[s.name] = {
79088
+ command: s.command,
79089
+ ...s.args?.length ? { args: s.args } : {}
79090
+ };
79091
+ }
79092
+ }
79093
+ config2["amp.mcpServers"] = mcpServers;
79094
+ return JSON.stringify(config2, null, 2);
79095
+ },
79096
+ remove(existing, serverNames) {
79097
+ const config2 = parseJsonSafe2(existing);
79098
+ const mcpServers = config2["amp.mcpServers"] ?? {};
79099
+ for (const name of serverNames) delete mcpServers[name];
79100
+ config2["amp.mcpServers"] = mcpServers;
79101
+ return JSON.stringify(config2, null, 2);
79102
+ }
79103
+ };
79104
+ var ALL_ADAPTERS = [
79105
+ claudeAdapter,
79106
+ codexAdapter,
79107
+ cursorAdapter,
79108
+ antigravityAdapter,
79109
+ kimiAdapter,
79110
+ opencodeAdapter,
79111
+ factoryAdapter,
79112
+ ampAdapter
79113
+ ];
79114
+ function serverEntry(adapter, content, name) {
79115
+ if (adapter === codexAdapter) {
79116
+ const parsed2 = parse5(content);
79117
+ const servers2 = parsed2.mcp_servers;
79118
+ return servers2 && typeof servers2 === "object" ? servers2[name] : void 0;
79119
+ }
79120
+ const parsed = parseJsonSafe2(content);
79121
+ const key = adapter === opencodeAdapter ? "mcp" : adapter === ampAdapter ? "amp.mcpServers" : "mcpServers";
79122
+ const servers = parsed[key];
79123
+ return servers && typeof servers === "object" ? servers[name] : void 0;
79124
+ }
79125
+ function canonicalize(value2) {
79126
+ if (Array.isArray(value2)) return value2.map(canonicalize);
79127
+ if (value2 && typeof value2 === "object") {
79128
+ return Object.fromEntries(
79129
+ Object.entries(value2).sort(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => [key, canonicalize(entry)])
79130
+ );
79131
+ }
79132
+ return value2;
79133
+ }
79134
+ function entriesMatch(actual, expected) {
79135
+ return JSON.stringify(canonicalize(actual)) === JSON.stringify(canonicalize(expected));
79136
+ }
79137
+ var ALAN_SESSION_HEADER_NAMES = /* @__PURE__ */ new Set([
79138
+ "x-conversation-id",
79139
+ "x-team-id",
79140
+ "x-allowed-tools",
79141
+ "x-alan-run-reference"
79142
+ ]);
79143
+ function normalizeAlanSessionHeaders(actual, expected) {
79144
+ if (!actual || typeof actual !== "object" || Array.isArray(actual)) return actual;
79145
+ if (!expected || typeof expected !== "object" || Array.isArray(expected)) return actual;
79146
+ const normalized = { ...actual };
79147
+ const expectedHeaders = expected;
79148
+ const hadAlanSessionMarker = Object.keys(normalized).some(
79149
+ (key) => ALAN_SESSION_HEADER_NAMES.has(key.toLowerCase())
79150
+ );
79151
+ for (const key of Object.keys(normalized)) {
79152
+ if (ALAN_SESSION_HEADER_NAMES.has(key.toLowerCase())) delete normalized[key];
79153
+ }
79154
+ if (hadAlanSessionMarker) {
79155
+ const expectedAuthorization = Object.entries(expectedHeaders).find(
79156
+ ([key]) => key.toLowerCase() === "authorization"
79157
+ );
79158
+ for (const key of Object.keys(normalized)) {
79159
+ if (key.toLowerCase() === "authorization") delete normalized[key];
79160
+ }
79161
+ if (expectedAuthorization) {
79162
+ normalized[expectedAuthorization[0]] = expectedAuthorization[1];
79163
+ }
79164
+ }
79165
+ return normalized;
79166
+ }
79167
+ function normalizeAlanRuntimeEntry(actual, expected) {
79168
+ if (Array.isArray(actual)) {
79169
+ if (!Array.isArray(expected)) return actual;
79170
+ return actual.map((entry, index) => normalizeAlanRuntimeEntry(entry, expected[index]));
79171
+ }
79172
+ if (!actual || typeof actual !== "object" || Array.isArray(actual)) return actual;
79173
+ if (!expected || typeof expected !== "object" || Array.isArray(expected)) return actual;
79174
+ const expectedRecord = expected;
79175
+ return Object.fromEntries(
79176
+ Object.entries(actual).map(([key, value2]) => [
79177
+ key,
79178
+ key.toLowerCase() === "headers" || key.toLowerCase() === "http_headers" ? normalizeAlanSessionHeaders(value2, expectedRecord[key]) : normalizeAlanRuntimeEntry(value2, expectedRecord[key])
79179
+ ])
79180
+ );
79181
+ }
79182
+ function matchesAlanManagedEntry(actual, expected) {
79183
+ return entriesMatch(actual, expected) || entriesMatch(normalizeAlanRuntimeEntry(actual, expected), expected);
79184
+ }
79185
+ function hasAlanManagedSignature(entry) {
79186
+ if (!entry || typeof entry !== "object") return false;
79187
+ for (const [key, value2] of Object.entries(entry)) {
79188
+ const lower = key.toLowerCase();
79189
+ if ((lower === "headers" || lower === "http_headers") && value2 && typeof value2 === "object") {
79190
+ const headerNames = Object.keys(value2);
79191
+ if (headerNames.some((name) => ALAN_SESSION_HEADER_NAMES.has(name.toLowerCase()))) {
79192
+ return true;
79193
+ }
79194
+ }
79195
+ if (value2 && typeof value2 === "object" && hasAlanManagedSignature(value2)) return true;
79196
+ }
79197
+ return false;
79198
+ }
79199
+ function expectedServerEntry(adapter, server) {
79200
+ return serverEntry(adapter, adapter.merge(null, [server]), server.name);
79201
+ }
79202
+ function isManagedServerList(entries) {
79203
+ return entries.length === 0 || typeof entries[0] !== "string";
79204
+ }
79205
+ function syncMcpToAllClis(servers, oldManagedServers = []) {
79206
+ const written = [];
79207
+ const failures = [];
79208
+ for (const adapter of ALL_ADAPTERS) {
79209
+ if (!(0, import_node_fs21.existsSync)(adapter.guardDir)) {
79210
+ log.debug("MCP Adapters", `Skipping ${adapter.cli} \u2014 guard dir not found`, {
79211
+ guardDir: adapter.guardDir
79212
+ });
79213
+ continue;
79214
+ }
79215
+ const releaseLock = acquireConfigLock2(adapter.configPath);
79216
+ try {
79217
+ const existing = readFileOr(adapter.configPath);
79218
+ const previousServers = isManagedServerList(oldManagedServers) ? oldManagedServers : [];
79219
+ const previousByName = new Map(previousServers.map((server) => [server.name, server]));
79220
+ if (existing) {
79221
+ for (const server of servers) {
79222
+ const currentEntry = serverEntry(adapter, existing, server.name);
79223
+ if (currentEntry === void 0) continue;
79224
+ const desiredEntry = expectedServerEntry(adapter, server);
79225
+ const previous = previousByName.get(server.name);
79226
+ const previousEntry = previous ? expectedServerEntry(adapter, previous) : void 0;
79227
+ if (!hasAlanManagedSignature(currentEntry) && !matchesAlanManagedEntry(currentEntry, desiredEntry) && (!previousEntry || !matchesAlanManagedEntry(currentEntry, previousEntry))) {
79228
+ throw new Error(`an unowned entry already uses the name ${server.name}`);
79229
+ }
79230
+ }
79231
+ }
79232
+ const currentNames = new Set(servers.map((s) => s.name));
79233
+ const toRemove = previousServers.filter((server) => !currentNames.has(server.name));
79234
+ let content;
79235
+ if (toRemove.length > 0) {
79236
+ const ownedNames = existing ? toRemove.filter(
79237
+ (server) => matchesAlanManagedEntry(
79238
+ serverEntry(adapter, existing, server.name),
79239
+ expectedServerEntry(adapter, server)
79240
+ )
79241
+ ).map((server) => server.name) : [];
79242
+ const cleaned = adapter.remove(existing, ownedNames);
79243
+ content = adapter.merge(cleaned, servers);
79244
+ } else {
79245
+ content = adapter.merge(existing, servers);
79246
+ }
79247
+ writeFileSafe2(adapter.configPath, content);
79248
+ written.push(adapter.cli);
79249
+ log.info("MCP Adapters", `Synced to ${adapter.cli}`, {
79250
+ configPath: adapter.configPath,
79251
+ serverCount: servers.length
79252
+ });
79253
+ } catch (err) {
79254
+ const existing = readFileOr(adapter.configPath);
79255
+ const backupPath = err instanceof MalformedToolConfigError && existing !== null ? saveMalformedBackup2(adapter.configPath, existing) : void 0;
79256
+ const message = `${adapter.cli}: ${err instanceof Error ? err.message : String(err)}${backupPath ? `; review ${backupPath}` : ""}`;
79257
+ failures.push(message);
79258
+ log.warn("MCP Adapters", `Failed to sync to ${adapter.cli}`, {
79259
+ error: message
79260
+ });
79261
+ } finally {
79262
+ releaseLock();
79263
+ }
79264
+ }
79265
+ if (failures.length > 0) throw new Error(failures.join(" | "));
79266
+ return written;
79267
+ }
79268
+ function removeMcpFromAllClis(managedServers) {
79269
+ if (managedServers.length === 0) return;
79270
+ const ownershipKnown = isManagedServerList(managedServers);
79271
+ for (const adapter of ALL_ADAPTERS) {
79272
+ if (!(0, import_node_fs21.existsSync)(adapter.guardDir)) continue;
79273
+ const releaseLock = acquireConfigLock2(adapter.configPath);
79274
+ try {
79275
+ const existing = readFileOr(adapter.configPath);
79276
+ if (!existing) continue;
79277
+ const ownedNames = ownershipKnown ? managedServers.filter(
79278
+ (server) => matchesAlanManagedEntry(
79279
+ serverEntry(adapter, existing, server.name),
79280
+ expectedServerEntry(adapter, server)
79281
+ )
79282
+ ).map((server) => server.name) : [];
79283
+ if (ownedNames.length === 0) continue;
79284
+ const content = adapter.remove(existing, ownedNames);
79285
+ writeFileSafe2(adapter.configPath, content);
79286
+ log.info("MCP Adapters", `Cleaned up ${adapter.cli}`, {
79287
+ removed: ownedNames
79288
+ });
79289
+ } catch (err) {
79290
+ const existing = readFileOr(adapter.configPath);
79291
+ const backupPath = err instanceof MalformedToolConfigError && existing !== null ? saveMalformedBackup2(adapter.configPath, existing) : void 0;
79292
+ log.warn("MCP Adapters", `Failed to clean up ${adapter.cli}`, {
79293
+ error: `${err instanceof Error ? err.message : String(err)}${backupPath ? `; review ${backupPath}` : ""}`
79294
+ });
79295
+ } finally {
79296
+ releaseLock();
79297
+ }
79298
+ }
79299
+ }
79300
+ var DEFAULT_ALAN_MCP_NAMES = ["alan"];
79301
+ function inspectAlanMcpPresence(expectedServers = [], candidateNames = DEFAULT_ALAN_MCP_NAMES) {
79302
+ const configuredClis = [];
79303
+ const inspectedClis = [];
79304
+ const serverNamesFound = /* @__PURE__ */ new Set();
79305
+ const names = /* @__PURE__ */ new Set([
79306
+ ...candidateNames,
79307
+ ...expectedServers.map((server) => server.name)
79308
+ ]);
79309
+ const expectedByName = new Map(expectedServers.map((server) => [server.name, server]));
79310
+ for (const adapter of ALL_ADAPTERS) {
79311
+ if (!(0, import_node_fs21.existsSync)(adapter.guardDir)) continue;
79312
+ inspectedClis.push(adapter.cli);
79313
+ const existing = readFileOr(adapter.configPath);
79314
+ if (!existing) continue;
79315
+ let foundOnCli = false;
79316
+ for (const name of names) {
79317
+ let entry;
79318
+ try {
79319
+ entry = serverEntry(adapter, existing, name);
79320
+ } catch {
79321
+ continue;
79322
+ }
79323
+ if (entry === void 0) continue;
79324
+ const expected = expectedByName.get(name);
79325
+ const matchesExpected = expected ? matchesAlanManagedEntry(entry, expectedServerEntry(adapter, expected)) : false;
79326
+ if (!hasAlanManagedSignature(entry) && !matchesExpected) continue;
79327
+ foundOnCli = true;
79328
+ serverNamesFound.add(name);
79329
+ }
79330
+ if (foundOnCli) configuredClis.push(adapter.cli);
79331
+ }
79332
+ return {
79333
+ configured: configuredClis.length > 0,
79334
+ configuredClis,
79335
+ inspectedClis,
79336
+ serverNamesFound: [...serverNamesFound]
79337
+ };
79338
+ }
79339
+ function toManagedMcpServers(mcpServers) {
79340
+ const result = [];
79341
+ for (const [name, config2] of Object.entries(mcpServers)) {
79342
+ if (!config2 || typeof config2 !== "object") continue;
79343
+ const c = config2;
79344
+ if (c.command && typeof c.command === "string") {
79345
+ result.push({
79346
+ name,
79347
+ kind: "stdio",
79348
+ command: c.command,
79349
+ args: c.args ?? void 0,
79350
+ env: c.env ?? void 0
79351
+ });
79352
+ } else {
79353
+ result.push({
79354
+ name,
79355
+ kind: "http",
79356
+ url: c.url ?? "",
79357
+ headers: c.headers ?? {}
79358
+ });
79359
+ }
79360
+ }
79361
+ return result;
79362
+ }
79363
+
79364
+ // src/mcp-sync.ts
79365
+ var McpSyncService = class {
79366
+ constructor(options) {
79367
+ this.options = options;
79368
+ this.managedServerNames = [...options.managedServerNames ?? ["alan"]];
79369
+ this.legacyServerNames = [...options.legacyServerNames ?? []];
79370
+ }
79371
+ managedServerNames;
79372
+ legacyServerNames;
79373
+ getStatus() {
79374
+ const presence = inspectAlanMcpPresence([], this.managedServerNames);
79375
+ return {
79376
+ configured: presence.configured,
79377
+ lastSyncAt: null,
79378
+ configuredClis: presence.configuredClis,
79379
+ inspectedClis: presence.inspectedClis,
79380
+ managedServerNames: [...this.managedServerNames],
79381
+ mcpAutoInjectEnabled: this.options.enabled
79382
+ };
79383
+ }
79384
+ async sync(apiUrl, token, onProgress) {
79385
+ if (!this.options.enabled) return { errors: [], mcpServersUpdated: false };
79386
+ onProgress?.({ phase: "fetching" });
79387
+ let config2;
79388
+ try {
79389
+ const response = await fetch(`${apiUrl}/public/ade/config`, {
79390
+ headers: { Authorization: `Bearer ${token}` },
79391
+ signal: AbortSignal.timeout(15e3)
79392
+ });
79393
+ if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
79394
+ config2 = await response.json();
79395
+ } catch (error61) {
79396
+ return {
79397
+ errors: [`Fetch failed: ${error61 instanceof Error ? error61.message : String(error61)}`],
79398
+ mcpServersUpdated: false
79399
+ };
79400
+ }
79401
+ return this.syncServers(config2.mcpServers, apiUrl, onProgress);
79402
+ }
79403
+ syncServers(mcpServers, apiUrl, onProgress) {
79404
+ if (!this.options.enabled) return { errors: [], mcpServersUpdated: false };
79405
+ onProgress?.({ phase: "mcp" });
79406
+ try {
79407
+ const rawServers = this.options.transformMcpServers ? this.options.transformMcpServers(mcpServers, apiUrl) : mcpServers;
79408
+ const managed = toManagedMcpServers(rawServers);
79409
+ const previousNames = [...this.managedServerNames, ...this.legacyServerNames];
79410
+ const written = syncMcpToAllClis(managed, previousNames);
79411
+ return { errors: [], mcpServersUpdated: written.length > 0 };
79412
+ } catch (error61) {
79413
+ return {
79414
+ errors: [`MCP sync: ${error61 instanceof Error ? error61.message : String(error61)}`],
79415
+ mcpServersUpdated: false
79416
+ };
79417
+ }
79418
+ }
79419
+ cleanup(previousServers = []) {
79420
+ removeMcpFromAllClis(previousServers);
79421
+ }
79422
+ };
79423
+
79424
+ // src/native-hook-config.ts
79425
+ var import_node_fs22 = require("fs");
79426
+ var import_node_os13 = require("os");
79427
+ var import_node_path24 = require("path");
79428
+ var CLAUDE_HOOK_EVENTS = [
79429
+ "SessionStart",
79430
+ "UserPromptSubmit",
79431
+ "PostToolUse",
79432
+ "Stop",
79433
+ "SubagentStart",
79434
+ "SubagentStop",
79435
+ "SessionEnd"
79436
+ ];
79437
+ var ALAN_HOOK_MARKER = "# alan-native-session-hook";
79438
+ function shellQuote(value2) {
79439
+ return `'${value2.replaceAll("'", `'\\''`)}'`;
79440
+ }
79441
+ function powershellQuote(value2) {
79442
+ return `'${value2.replaceAll("'", "''")}'`;
79443
+ }
79444
+ function nativeHookCommand(input2) {
79445
+ const args = [
79446
+ input2.cliEntry,
79447
+ "hook",
79448
+ "--config-path",
79449
+ input2.configPath,
79450
+ "--provider",
79451
+ input2.provider,
79452
+ "--event",
79453
+ input2.event,
79454
+ ALAN_HOOK_MARKER
79455
+ ];
79456
+ const environment = Object.entries(input2.environment ?? {});
79457
+ for (const [key] of environment) {
79458
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
79459
+ throw new Error(`Invalid native hook environment variable: ${key}`);
79460
+ }
79461
+ }
79462
+ if (environment.length === 0) {
79463
+ return {
79464
+ type: "command",
79465
+ command: input2.nodeExecutable,
79466
+ args,
79467
+ timeout: 5
79468
+ };
79469
+ }
79470
+ if ((input2.platform ?? (0, import_node_os13.platform)()) === "win32") {
79471
+ const environmentPrefix2 = environment.map(([key, value2]) => `$env:${key}=${powershellQuote(value2)}`).join("; ");
79472
+ const command2 = [input2.nodeExecutable, ...args].map(powershellQuote).join(" ");
79473
+ return {
79474
+ type: "command",
79475
+ shell: "powershell",
79476
+ command: `${environmentPrefix2}; & ${command2}`,
79477
+ timeout: 5
79478
+ };
79479
+ }
79480
+ const environmentPrefix = environment.map(([key, value2]) => `${key}=${shellQuote(value2)}`).join(" ");
79481
+ const command = [input2.nodeExecutable, ...args].map(shellQuote).join(" ");
79482
+ return {
79483
+ type: "command",
79484
+ shell: "bash",
79485
+ command: `${environmentPrefix} ${command}`,
79486
+ timeout: 5
79487
+ };
79488
+ }
79489
+ function readSettings(path2) {
79490
+ if (!(0, import_node_fs22.existsSync)(path2)) return {};
79491
+ try {
79492
+ const value2 = JSON.parse((0, import_node_fs22.readFileSync)(path2, "utf8"));
79493
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) {
79494
+ throw new Error("settings root must be an object");
79495
+ }
79496
+ return value2;
79497
+ } catch (error61) {
79498
+ const backup = `${path2}.malformed-${Date.now()}.bak`;
79499
+ (0, import_node_fs22.copyFileSync)(path2, backup);
79500
+ throw new Error(
79501
+ `Claude settings were malformed and moved to ${backup}: ${error61 instanceof Error ? error61.message : String(error61)}`
79502
+ );
79503
+ }
79504
+ }
79505
+ function withoutAlanHookGroups(value2) {
79506
+ if (!Array.isArray(value2)) return [];
79507
+ return value2.filter((group) => !JSON.stringify(group).includes(ALAN_HOOK_MARKER));
79508
+ }
79509
+ function writeSettings(path2, settings) {
79510
+ (0, import_node_fs22.mkdirSync)((0, import_node_path24.dirname)(path2), { recursive: true, mode: 448 });
79511
+ const temporary = `${path2}.${process.pid}.tmp`;
79512
+ (0, import_node_fs22.writeFileSync)(temporary, `${JSON.stringify(settings, null, 2)}
79513
+ `, { mode: 384 });
79514
+ (0, import_node_fs22.renameSync)(temporary, path2);
79515
+ }
79516
+ function reconcileClaudeNativeHooks(input2) {
79517
+ const homeDir = input2?.homeDir ?? (0, import_node_os13.homedir)();
79518
+ const settingsPath = (0, import_node_path24.join)(homeDir, ".claude", "settings.json");
79519
+ const nodeExecutable = input2?.nodeExecutable ?? process.execPath;
79520
+ const cliEntry = input2?.cliEntry ?? process.argv[1];
79521
+ const configPath = input2?.configPath ?? process.env.ALAN_AGENT_CONFIG_PATH;
79522
+ if (!cliEntry) throw new Error("Cannot resolve the alan-agent CLI entrypoint");
79523
+ if (!configPath?.trim()) {
79524
+ throw new Error("Cannot install Claude hooks without an exact Alan agent config path");
79525
+ }
79526
+ const settings = readSettings(settingsPath);
79527
+ const currentHooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
79528
+ const hooks = { ...currentHooks };
79529
+ for (const event of CLAUDE_HOOK_EVENTS) {
79530
+ hooks[event] = [
79531
+ ...withoutAlanHookGroups(currentHooks[event]),
79532
+ {
79533
+ hooks: [
79534
+ nativeHookCommand({
79535
+ configPath,
79536
+ nodeExecutable,
79537
+ cliEntry,
79538
+ environment: input2?.environment,
79539
+ platform: input2?.platform,
79540
+ provider: "claude_cli",
79541
+ event
79542
+ })
79543
+ ]
79544
+ }
79545
+ ];
79546
+ }
79547
+ const next = { ...settings, hooks };
79548
+ const previous = (0, import_node_fs22.existsSync)(settingsPath) ? (0, import_node_fs22.readFileSync)(settingsPath, "utf8") : null;
79549
+ const serialized = `${JSON.stringify(next, null, 2)}
79550
+ `;
79551
+ if (previous === serialized) return false;
79552
+ writeSettings(settingsPath, next);
79553
+ return true;
79554
+ }
79555
+ function removeClaudeNativeHooks(homeDir = (0, import_node_os13.homedir)()) {
79556
+ const settingsPath = (0, import_node_path24.join)(homeDir, ".claude", "settings.json");
79557
+ if (!(0, import_node_fs22.existsSync)(settingsPath)) return false;
79558
+ const settings = readSettings(settingsPath);
79559
+ if (!settings.hooks || typeof settings.hooks !== "object" || Array.isArray(settings.hooks)) {
79560
+ return false;
79561
+ }
79562
+ const hooks = {};
79563
+ let removed = false;
79564
+ for (const [event, groups] of Object.entries(settings.hooks)) {
79565
+ const retained = withoutAlanHookGroups(groups);
79566
+ if (Array.isArray(groups) && retained.length !== groups.length) removed = true;
79567
+ if (retained.length > 0) hooks[event] = retained;
79568
+ }
79569
+ if (!removed) return false;
79570
+ writeSettings(settingsPath, { ...settings, hooks });
79571
+ return true;
79572
+ }
79573
+
79574
+ // src/integration-cli.ts
79575
+ function alanMcpServers(apiUrl) {
79576
+ return {
79577
+ alan: {
79578
+ type: "http",
79579
+ url: new URL("/mcp", apiUrl).toString()
79580
+ }
79581
+ };
79582
+ }
79583
+ function runMcpIntegrationCommand(args, options = {}) {
79584
+ bindConfigPath(args);
79585
+ const action = args[0] ?? "status";
79586
+ const config2 = readConfig();
79587
+ const endpoints = resolveEndpoints(args.slice(1), config2);
79588
+ const service = new McpSyncService({ enabled: true });
79589
+ if (action === "status") {
79590
+ process.stdout.write(`${JSON.stringify(service.getStatus(), null, 2)}
79591
+ `);
79592
+ return;
79593
+ }
79594
+ if (action === "reconcile") {
79595
+ if (!config2.runtimeId || !config2.runtimeToken) {
79596
+ throw new Error(
79597
+ "Alan is not configured. Run `alan-agent login` or `alan-agent setup` first."
79598
+ );
79599
+ }
79600
+ const result = service.syncServers(alanMcpServers(endpoints.apiUrl), endpoints.apiUrl);
79601
+ if (result.errors.length > 0) throw new Error(result.errors.join("; "));
79602
+ const hooksUpdated = resolveCliExecutable("claude", getDaemonCliEnvironment()) ? reconcileClaudeNativeHooks({
79603
+ configPath: getConfigPath(),
79604
+ nodeExecutable: options.nativeHookRuntime?.executable,
79605
+ cliEntry: options.nativeHookRuntime?.cliEntry,
79606
+ environment: options.nativeHookRuntime?.environment
79607
+ }) : false;
79608
+ process.stdout.write(
79609
+ `${result.mcpServersUpdated || hooksUpdated ? "Alan CLI integrations updated" : "Alan CLI integrations already current"}.
79610
+ `
79611
+ );
79612
+ return;
79613
+ }
79614
+ if (action === "uninstall") {
79615
+ service.cleanup(toManagedMcpServers(alanMcpServers(endpoints.apiUrl)));
79616
+ removeClaudeNativeHooks();
79617
+ process.stdout.write("Removed Alan-owned MCP configuration.\n");
79618
+ return;
79619
+ }
79620
+ throw new Error(`Unknown integrations action: ${action}`);
79621
+ }
79622
+
78099
79623
  // src/lifecycle-logger.ts
78100
79624
  var AgentLifecycleLogger = class _AgentLifecycleLogger {
78101
79625
  context;
@@ -78168,6 +79692,293 @@ function omitUndefined(record2) {
78168
79692
  return Object.fromEntries(Object.entries(record2).filter(([, value2]) => value2 !== void 0));
78169
79693
  }
78170
79694
 
79695
+ // src/native-hook-command.ts
79696
+ var import_node_crypto19 = require("crypto");
79697
+ var import_node_fs23 = require("fs");
79698
+ var import_node_path25 = require("path");
79699
+
79700
+ // src/native-session.ts
79701
+ var NATIVE_SESSION_PROVIDER_CAPABILITIES = [
79702
+ {
79703
+ providerKind: "claude_cli",
79704
+ provider: "claude",
79705
+ command: "claude",
79706
+ capabilities: {
79707
+ mcp: true,
79708
+ lifecycleHooks: true,
79709
+ transcriptDelta: true,
79710
+ subagents: true,
79711
+ sessionEnd: true,
79712
+ state: "supported"
79713
+ },
79714
+ supportedHookEvents: [
79715
+ "SessionStart",
79716
+ "UserPromptSubmit",
79717
+ "PostToolUse",
79718
+ "Stop",
79719
+ "SubagentStart",
79720
+ "SubagentStop",
79721
+ "SessionEnd"
79722
+ ]
79723
+ },
79724
+ {
79725
+ providerKind: "codex_app_server",
79726
+ provider: "codex",
79727
+ command: "codex",
79728
+ capabilities: {
79729
+ mcp: true,
79730
+ lifecycleHooks: false,
79731
+ transcriptDelta: false,
79732
+ subagents: false,
79733
+ sessionEnd: false,
79734
+ state: "degraded",
79735
+ reason: "Codex exposes a turn-complete notification, not full lifecycle hooks."
79736
+ },
79737
+ supportedHookEvents: ["agent-turn-complete"]
79738
+ },
79739
+ {
79740
+ providerKind: "copilot_cli",
79741
+ provider: "copilot",
79742
+ command: "copilot",
79743
+ capabilities: unsupportedMcpOnlyCapability(),
79744
+ supportedHookEvents: []
79745
+ },
79746
+ {
79747
+ providerKind: "cursor_agent_cli",
79748
+ provider: "cursor",
79749
+ command: "cursor-agent",
79750
+ capabilities: unsupportedMcpOnlyCapability(),
79751
+ supportedHookEvents: []
79752
+ },
79753
+ {
79754
+ providerKind: "antigravity_cli",
79755
+ provider: "antigravity",
79756
+ command: "agy",
79757
+ capabilities: unsupportedMcpOnlyCapability(),
79758
+ supportedHookEvents: []
79759
+ },
79760
+ {
79761
+ providerKind: "kimi_cli",
79762
+ provider: "kimi",
79763
+ command: "kimi-cli",
79764
+ capabilities: unsupportedMcpOnlyCapability(),
79765
+ supportedHookEvents: []
79766
+ },
79767
+ {
79768
+ providerKind: "grok_cli",
79769
+ provider: "grok",
79770
+ command: "grok",
79771
+ capabilities: unsupportedMcpOnlyCapability(),
79772
+ supportedHookEvents: []
79773
+ },
79774
+ {
79775
+ providerKind: "opencode_cli",
79776
+ provider: "opencode",
79777
+ command: "opencode",
79778
+ capabilities: unsupportedMcpOnlyCapability(),
79779
+ supportedHookEvents: []
79780
+ },
79781
+ {
79782
+ providerKind: "droid_cli",
79783
+ provider: "droid",
79784
+ command: "droid",
79785
+ capabilities: unsupportedMcpOnlyCapability(),
79786
+ supportedHookEvents: []
79787
+ },
79788
+ {
79789
+ providerKind: "supatest_cli",
79790
+ provider: "supatest",
79791
+ command: "supatest",
79792
+ capabilities: unsupportedMcpOnlyCapability(),
79793
+ supportedHookEvents: []
79794
+ }
79795
+ ];
79796
+ function unsupportedMcpOnlyCapability() {
79797
+ return {
79798
+ mcp: true,
79799
+ lifecycleHooks: false,
79800
+ transcriptDelta: false,
79801
+ subagents: false,
79802
+ sessionEnd: false,
79803
+ state: "unsupported",
79804
+ reason: "No native hook adapter is installed for this provider."
79805
+ };
79806
+ }
79807
+ var CAPABILITIES_BY_PROVIDER_KIND = new Map(
79808
+ NATIVE_SESSION_PROVIDER_CAPABILITIES.map((capability) => [capability.providerKind, capability])
79809
+ );
79810
+ function isNativeSessionProviderKind(value2) {
79811
+ return CAPABILITIES_BY_PROVIDER_KIND.has(value2);
79812
+ }
79813
+
79814
+ // src/native-hook-command.ts
79815
+ var MAX_NATIVE_HOOK_STDIN_BYTES = 1024 * 1024;
79816
+ function argumentValue(args, name) {
79817
+ const index = args.indexOf(name);
79818
+ if (index >= 0) return args[index + 1];
79819
+ const prefix = `${name}=`;
79820
+ return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
79821
+ }
79822
+ function stringField4(payload, ...names) {
79823
+ for (const name of names) {
79824
+ const value2 = payload[name];
79825
+ if (typeof value2 === "string" && value2.trim()) return value2.trim();
79826
+ }
79827
+ return void 0;
79828
+ }
79829
+ function nonNegativeIntegerField(payload, ...names) {
79830
+ for (const name of names) {
79831
+ const value2 = payload[name];
79832
+ if (typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0) return value2;
79833
+ }
79834
+ return void 0;
79835
+ }
79836
+ function parseNativeHookEnvelope(input2) {
79837
+ if (!isNativeSessionProviderKind(input2.provider)) {
79838
+ throw new Error(`unsupported native session provider: ${input2.provider}`);
79839
+ }
79840
+ const rawHookEvent = input2.hookEvent ?? stringField4(input2.payload, "hook_event_name", "hookEvent", "type") ?? "notification";
79841
+ const cwd = stringField4(input2.payload, "cwd", "working_directory", "workingDirectory");
79842
+ const transcriptPath = stringField4(input2.payload, "transcript_path", "transcriptPath");
79843
+ const capability = NATIVE_SESSION_PROVIDER_CAPABILITIES.find(
79844
+ (candidate) => candidate.providerKind === input2.provider
79845
+ );
79846
+ if (!capability) throw new Error(`unsupported native session provider: ${input2.provider}`);
79847
+ const externalSessionId = stringField4(
79848
+ input2.payload,
79849
+ "session_id",
79850
+ "sessionId",
79851
+ "conversation_id",
79852
+ "conversationId",
79853
+ "thread_id",
79854
+ "threadId"
79855
+ );
79856
+ if (!externalSessionId) throw new Error("hook payload is missing a provider session id");
79857
+ const cursor = stringField4(input2.payload, "source_cursor", "sourceCursor") ?? nonNegativeIntegerField(input2.payload, "byte_offset", "byteOffset")?.toString() ?? nonNegativeIntegerField(input2.payload, "sequence", "seq")?.toString();
79858
+ return {
79859
+ schemaVersion: 1,
79860
+ eventId: (0, import_node_crypto19.randomUUID)(),
79861
+ provider: capability.provider,
79862
+ eventType: normalizeHookEvent(rawHookEvent),
79863
+ observedAt: (input2.now ?? /* @__PURE__ */ new Date()).toISOString(),
79864
+ externalSessionId,
79865
+ parentExternalSessionId: stringField4(input2.payload, "parent_session_id", "parentSessionId"),
79866
+ ...cwd ? { cwd: (0, import_node_path25.resolve)(cwd) } : {},
79867
+ ...transcriptPath ? { transcriptPath: (0, import_node_path25.resolve)(transcriptPath) } : {},
79868
+ ...cursor ? { sourceCursor: cursor } : {},
79869
+ providerPayload: input2.payload
79870
+ };
79871
+ }
79872
+ function normalizeHookEvent(raw) {
79873
+ switch (raw.toLowerCase().replaceAll("-", "_").replaceAll(" ", "_")) {
79874
+ case "sessionstart":
79875
+ case "session_start":
79876
+ return "session_start";
79877
+ case "userpromptsubmit":
79878
+ case "user_prompt":
79879
+ return "user_prompt";
79880
+ case "subagentstart":
79881
+ case "subagent_start":
79882
+ return "subagent_start";
79883
+ case "subagentstop":
79884
+ case "subagent_end":
79885
+ return "subagent_end";
79886
+ case "sessionend":
79887
+ case "session_end":
79888
+ return "session_end";
79889
+ case "stop":
79890
+ case "agent_turn_complete":
79891
+ return "stop";
79892
+ default:
79893
+ return "transcript_update";
79894
+ }
79895
+ }
79896
+ function readBoundedHookPayload(fd = 0, maxBytes = MAX_NATIVE_HOOK_STDIN_BYTES) {
79897
+ const chunks = [];
79898
+ let totalBytes = 0;
79899
+ while (true) {
79900
+ const chunk = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1 - totalBytes));
79901
+ const bytesRead = (0, import_node_fs23.readSync)(fd, chunk, 0, chunk.byteLength, null);
79902
+ if (bytesRead === 0) break;
79903
+ totalBytes += bytesRead;
79904
+ if (totalBytes > maxBytes) throw new Error(`hook input exceeds ${maxBytes} bytes`);
79905
+ chunks.push(chunk.subarray(0, bytesRead));
79906
+ }
79907
+ const body = Buffer.concat(chunks, totalBytes);
79908
+ if (body.byteLength === 0) return {};
79909
+ const parsed = JSON.parse(body.toString("utf8"));
79910
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
79911
+ throw new Error("hook input must be a JSON object");
79912
+ }
79913
+ return parsed;
79914
+ }
79915
+ function enqueueNativeHookFromConfig(input2) {
79916
+ if (process.env.ALAN_MANAGED_RUN === "1" || process.env.ALAN_RUN_ID?.trim()) return false;
79917
+ bindConfigPath(input2.args);
79918
+ const config2 = input2.config ?? readConfig();
79919
+ const provider = argumentValue(input2.args, "--provider");
79920
+ if (!provider) throw new Error("hook requires --provider");
79921
+ if (!config2.eventSpoolKey) return false;
79922
+ const envelope = parseNativeHookEnvelope({
79923
+ provider,
79924
+ hookEvent: argumentValue(input2.args, "--event"),
79925
+ payload: input2.payload
79926
+ });
79927
+ new EncryptedNativeHookInbox(nativeHookInboxPath(getConfigPath()), config2.eventSpoolKey).enqueue(
79928
+ envelope
79929
+ );
79930
+ return true;
79931
+ }
79932
+ function runNativeHookCommand(args) {
79933
+ try {
79934
+ const payload = readBoundedHookPayload();
79935
+ const enqueued = enqueueNativeHookFromConfig({ args, payload });
79936
+ const hookEvent = argumentValue(args, "--event")?.toLowerCase();
79937
+ if (enqueued && (hookEvent === "userpromptsubmit" || hookEvent === "user_prompt")) {
79938
+ const providerKind = argumentValue(args, "--provider");
79939
+ const externalSessionId = stringField4(payload, "session_id", "sessionId");
79940
+ const capability = providerKind ? NATIVE_SESSION_PROVIDER_CAPABILITIES.find(
79941
+ (candidate) => candidate.providerKind === providerKind
79942
+ ) : void 0;
79943
+ if (capability && externalSessionId) {
79944
+ process.stdout.write(
79945
+ `${JSON.stringify({
79946
+ hookSpecificOutput: {
79947
+ hookEventName: "UserPromptSubmit",
79948
+ additionalContext: `First call get_native_session_link_status with provider=${capability.provider} and externalSessionId=${externalSessionId}. If status is linked, continue without asking or linking again. If status is syncing, wait briefly and retry the status check. Only when status is pending, ask the user one question: work on an Alan task or continue as a direct session. Resolve a named task with find_records, then call link_native_session. Do not guess a task and do not change task status.`
79949
+ }
79950
+ })}
79951
+ `
79952
+ );
79953
+ }
79954
+ }
79955
+ } catch (error61) {
79956
+ if (process.env.ALAN_HOOK_DEBUG === "1") {
79957
+ process.stderr.write(
79958
+ `[alan-agent] native hook skipped: ${error61 instanceof Error ? error61.message : String(error61)}
79959
+ `
79960
+ );
79961
+ }
79962
+ }
79963
+ }
79964
+ function collectNativeIntegrationStatus(args = []) {
79965
+ bindConfigPath(args);
79966
+ const config2 = readConfig();
79967
+ const env = getDaemonCliEnvironment();
79968
+ return NATIVE_SESSION_PROVIDER_CAPABILITIES.map((capability) => ({
79969
+ provider: capability.providerKind,
79970
+ command: capability.command,
79971
+ installed: resolveCliExecutable(capability.command, env) !== null,
79972
+ hookCoverage: capability.capabilities.lifecycleHooks ? "lifecycle" : capability.capabilities.state === "degraded" ? "notification" : "none",
79973
+ transcriptStreaming: capability.capabilities.transcriptDelta,
79974
+ inboxReady: Boolean(config2.eventSpoolKey)
79975
+ }));
79976
+ }
79977
+ function printNativeIntegrationStatus(args) {
79978
+ process.stdout.write(`${JSON.stringify(collectNativeIntegrationStatus(args), null, 2)}
79979
+ `);
79980
+ }
79981
+
78171
79982
  // src/resume-fallback-env.ts
78172
79983
  var RESUME_FALLBACK_CONTEXT_ENV = "ALAN_RESUME_FALLBACK_CONTEXT_B64";
78173
79984
  function decodeResumeFallbackContext(encoded) {
@@ -78181,7 +79992,7 @@ function decodeResumeFallbackContext(encoded) {
78181
79992
  }
78182
79993
 
78183
79994
  // src/sandbox.ts
78184
- var import_node_os12 = require("os");
79995
+ var import_node_os14 = require("os");
78185
79996
 
78186
79997
  // src/execution-activity-tracker.ts
78187
79998
  var ExecutionActivityTracker = class {
@@ -78506,11 +80317,11 @@ async function readErrorBody(response) {
78506
80317
  }
78507
80318
  }
78508
80319
  function delay2(ms) {
78509
- return new Promise((resolve14) => setTimeout(resolve14, ms));
80320
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
78510
80321
  }
78511
80322
 
78512
80323
  // src/web-presenter.ts
78513
- var import_node_crypto15 = require("crypto");
80324
+ var import_node_crypto20 = require("crypto");
78514
80325
  var WebPresenter = class {
78515
80326
  constructor(wsClient, conversationId, cwd) {
78516
80327
  this.conversationId = conversationId;
@@ -78696,7 +80507,7 @@ var WebPresenter = class {
78696
80507
  this.applicationProblem = createAlanProblem({
78697
80508
  domain: "application",
78698
80509
  code: input2.code,
78699
- id: (0, import_node_crypto15.randomUUID)(),
80510
+ id: (0, import_node_crypto20.randomUUID)(),
78700
80511
  title: APPLICATION_PROBLEM_TITLES[input2.code],
78701
80512
  detail: input2.message,
78702
80513
  occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -78763,7 +80574,7 @@ var WebPresenter = class {
78763
80574
  };
78764
80575
 
78765
80576
  // src/ws-client.ts
78766
- var import_node_crypto17 = require("crypto");
80577
+ var import_node_crypto22 = require("crypto");
78767
80578
 
78768
80579
  // ../shared/dist/agent-liveness.js
78769
80580
  var AGENT_LIVENESS_PROTOCOL_VERSION = 1;
@@ -78791,8 +80602,8 @@ var agentProbeAckSchema = external_exports.object({
78791
80602
  });
78792
80603
 
78793
80604
  // src/sandbox-outbox.ts
78794
- var import_node_crypto16 = require("crypto");
78795
- var import_node_fs18 = require("fs");
80605
+ var import_node_crypto21 = require("crypto");
80606
+ var import_node_fs24 = require("fs");
78796
80607
  var SANDBOX_EVENT_OUTBOX_ENV = "ALAN_EVENT_OUTBOX_ENABLED";
78797
80608
  function isSandboxEventOutboxEnabled(env = process.env) {
78798
80609
  return env[SANDBOX_EVENT_OUTBOX_ENV] === "true";
@@ -78801,7 +80612,7 @@ function deriveSandboxOutboxKey(sessionToken) {
78801
80612
  if (!sessionToken) {
78802
80613
  throw new Error("sandbox event outbox requires ALAN_SESSION_TOKEN for key derivation");
78803
80614
  }
78804
- return (0, import_node_crypto16.createHash)("sha256").update(sessionToken, "utf8").digest("base64url");
80615
+ return (0, import_node_crypto21.createHash)("sha256").update(sessionToken, "utf8").digest("base64url");
78805
80616
  }
78806
80617
  function sandboxOutboxSpoolPath(conversationId) {
78807
80618
  return `/tmp/alan-agent-outbox-${conversationId}.enc`;
@@ -78815,7 +80626,7 @@ function createSandboxEventOutbox(input2) {
78815
80626
  input2.pushLog?.(
78816
80627
  `sandbox_outbox_spool_reset ${error61 instanceof Error ? error61.message : String(error61)}`
78817
80628
  );
78818
- (0, import_node_fs18.rmSync)(path2, { force: true });
80629
+ (0, import_node_fs24.rmSync)(path2, { force: true });
78819
80630
  return new EncryptedEventOutbox(path2, key, input2.pushLog, input2.options);
78820
80631
  }
78821
80632
  }
@@ -78953,7 +80764,7 @@ var WSClient = class {
78953
80764
  }
78954
80765
  socket;
78955
80766
  /** Stable id for this agent process; fences stale heartbeats server-side. */
78956
- agentSessionId = (0, import_node_crypto17.randomUUID)();
80767
+ agentSessionId = (0, import_node_crypto22.randomUUID)();
78957
80768
  /** Monotonic heartbeat sequence — advances on every hello + heartbeat. */
78958
80769
  heartbeatSeq = 0;
78959
80770
  heartbeatTimer = null;
@@ -79037,9 +80848,9 @@ var WSClient = class {
79037
80848
  });
79038
80849
  }
79039
80850
  waitForConnection(timeoutMs = 15e3) {
79040
- return new Promise((resolve14, reject) => {
80851
+ return new Promise((resolve15, reject) => {
79041
80852
  if (this.socket.connected) {
79042
- resolve14();
80853
+ resolve15();
79043
80854
  return;
79044
80855
  }
79045
80856
  let lastError;
@@ -79050,7 +80861,7 @@ var WSClient = class {
79050
80861
  };
79051
80862
  const handleConnect = () => {
79052
80863
  cleanup();
79053
- resolve14();
80864
+ resolve15();
79054
80865
  };
79055
80866
  const handleConnectError = (error61) => {
79056
80867
  lastError = error61;
@@ -79383,9 +81194,9 @@ async function runSandbox(config2) {
79383
81194
  onUserMessage: (payload) => {
79384
81195
  if (payload === null) {
79385
81196
  if (pendingMessageResolve) {
79386
- const resolve14 = pendingMessageResolve;
81197
+ const resolve15 = pendingMessageResolve;
79387
81198
  pendingMessageResolve = null;
79388
- resolve14(null);
81199
+ resolve15(null);
79389
81200
  }
79390
81201
  return;
79391
81202
  }
@@ -79407,9 +81218,9 @@ async function runSandbox(config2) {
79407
81218
  fileCount: payload.files?.length ?? 0
79408
81219
  });
79409
81220
  if (pendingMessageResolve) {
79410
- const resolve14 = pendingMessageResolve;
81221
+ const resolve15 = pendingMessageResolve;
79411
81222
  pendingMessageResolve = null;
79412
- resolve14(payload);
81223
+ resolve15(payload);
79413
81224
  } else {
79414
81225
  queuedMessages.push(payload);
79415
81226
  }
@@ -79474,7 +81285,7 @@ async function runSandbox(config2) {
79474
81285
  }
79475
81286
  const presenter = new WebPresenter(wsClient, config2.sessionId, config2.projectPath);
79476
81287
  presenter.setSuppressSessionLifecycle(true);
79477
- const runtimeSkillManager = await RuntimeSkillManager.open({ homeDirectory: (0, import_node_os12.homedir)() });
81288
+ const runtimeSkillManager = await RuntimeSkillManager.open({ homeDirectory: (0, import_node_os14.homedir)() });
79478
81289
  let lastProviderSessionId = config2.providerSessionId;
79479
81290
  let resumeFallbackContext = config2.resumeFallbackContext;
79480
81291
  let activeBackendKind = config2.backendKind || "claude_cli";
@@ -79488,12 +81299,13 @@ async function runSandbox(config2) {
79488
81299
  let currentAlanMcp;
79489
81300
  while (!stopped) {
79490
81301
  if (currentTask?.trim() || currentSkillInvocation) {
81302
+ const activeRunId = currentRunId ?? config2.runId;
79491
81303
  const agent = new CoreAgent(presenter, {
79492
81304
  backendKind: activeBackendKind,
81305
+ runId: activeRunId,
79493
81306
  providerApiKey: activeProviderApiKey
79494
81307
  });
79495
81308
  currentAgent = agent;
79496
- const activeRunId = currentRunId ?? config2.runId;
79497
81309
  const rootExecutionActivity = executionActivityTracker && activeRunId && currentTaskId ? executionActivityTracker.register({
79498
81310
  ownerId: `root:${activeRunId}`,
79499
81311
  leaseRunId: activeRunId,
@@ -79681,8 +81493,8 @@ async function runSandbox(config2) {
79681
81493
  });
79682
81494
  if (stopped) break;
79683
81495
  }
79684
- const nextPayload = queuedMessages.shift() ?? await new Promise((resolve14) => {
79685
- pendingMessageResolve = resolve14;
81496
+ const nextPayload = queuedMessages.shift() ?? await new Promise((resolve15) => {
81497
+ pendingMessageResolve = resolve15;
79686
81498
  });
79687
81499
  if (nextPayload === null || stopped) {
79688
81500
  break;
@@ -79761,9 +81573,9 @@ async function runSandbox(config2) {
79761
81573
 
79762
81574
  // src/service-manager.ts
79763
81575
  var import_node_child_process9 = require("child_process");
79764
- var import_node_fs19 = require("fs");
79765
- var import_node_os13 = require("os");
79766
- var import_node_path21 = require("path");
81576
+ var import_node_fs25 = require("fs");
81577
+ var import_node_os15 = require("os");
81578
+ var import_node_path26 = require("path");
79767
81579
  var SERVICE_LABEL = "ai.tryalan.agent";
79768
81580
  var SYSTEMD_UNIT = "alan-agent.service";
79769
81581
  var WINDOWS_TASK = "Alan Agent";
@@ -79783,9 +81595,16 @@ function buildDaemonServicePlan(input2) {
79783
81595
  if (userId === void 0) throw new Error("Cannot determine the current macOS user ID");
79784
81596
  const domain2 = `gui/${userId}`;
79785
81597
  const serviceTarget = `${domain2}/${SERVICE_LABEL}`;
79786
- const manifestPath = (0, import_node_path21.join)(input2.homeDir, "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
79787
- const logDir = (0, import_node_path21.join)(input2.homeDir, ".alan", "agent", "logs");
81598
+ const manifestPath = (0, import_node_path26.join)(input2.homeDir, "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
81599
+ const logDir = (0, import_node_path26.join)(input2.homeDir, ".alan", "agent", "logs");
79788
81600
  const programArguments = daemonArgs.map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
81601
+ const environmentEntries = Object.entries(input2.environment ?? {}).map(([key, value2]) => ` <key>${xml(key)}</key>
81602
+ <string>${xml(value2)}</string>`).join("\n");
81603
+ const environmentBlock = environmentEntries ? ` <key>EnvironmentVariables</key>
81604
+ <dict>
81605
+ ${environmentEntries}
81606
+ </dict>
81607
+ ` : "";
79789
81608
  const manifest = `<?xml version="1.0" encoding="UTF-8"?>
79790
81609
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
79791
81610
  <plist version="1.0">
@@ -79796,7 +81615,7 @@ function buildDaemonServicePlan(input2) {
79796
81615
  <array>
79797
81616
  ${programArguments}
79798
81617
  </array>
79799
- <key>RunAtLoad</key>
81618
+ ${environmentBlock} <key>RunAtLoad</key>
79800
81619
  <true/>
79801
81620
  <key>KeepAlive</key>
79802
81621
  <dict>
@@ -79806,9 +81625,9 @@ ${programArguments}
79806
81625
  <key>ThrottleInterval</key>
79807
81626
  <integer>5</integer>
79808
81627
  <key>StandardOutPath</key>
79809
- <string>${xml((0, import_node_path21.join)(logDir, "daemon.log"))}</string>
81628
+ <string>${xml((0, import_node_path26.join)(logDir, "daemon.log"))}</string>
79810
81629
  <key>StandardErrorPath</key>
79811
- <string>${xml((0, import_node_path21.join)(logDir, "daemon-error.log"))}</string>
81630
+ <string>${xml((0, import_node_path26.join)(logDir, "daemon-error.log"))}</string>
79812
81631
  </dict>
79813
81632
  </plist>
79814
81633
  `;
@@ -79847,7 +81666,8 @@ ${programArguments}
79847
81666
  };
79848
81667
  }
79849
81668
  if (input2.platform === "linux") {
79850
- const manifestPath = (0, import_node_path21.join)(input2.homeDir, ".config", "systemd", "user", SYSTEMD_UNIT);
81669
+ const manifestPath = (0, import_node_path26.join)(input2.homeDir, ".config", "systemd", "user", SYSTEMD_UNIT);
81670
+ const environmentLines = Object.entries(input2.environment ?? {}).map(([key, value2]) => `Environment=${systemdArg(`${key}=${value2}`)}`).join("\n");
79851
81671
  const manifest = `[Unit]
79852
81672
  Description=Alan local agent daemon
79853
81673
  After=network-online.target
@@ -79857,7 +81677,8 @@ StartLimitBurst=3
79857
81677
 
79858
81678
  [Service]
79859
81679
  Type=simple
79860
- ExecStart=${daemonArgs.map(systemdArg).join(" ")}
81680
+ ${environmentLines ? `${environmentLines}
81681
+ ` : ""}ExecStart=${daemonArgs.map(systemdArg).join(" ")}
79861
81682
  Restart=on-failure
79862
81683
  RestartSec=5
79863
81684
 
@@ -79898,9 +81719,11 @@ WantedBy=default.target
79898
81719
  }
79899
81720
  };
79900
81721
  }
79901
- const executable = powershellLiteral(input2.nodeExecutable);
81722
+ const environmentPrefix = Object.entries(input2.environment ?? {}).map(([key, value2]) => `$env:${key} = ${powershellLiteral(value2)}`).join("; ");
81723
+ const directArgument = [input2.cliEntry, "daemon", "--profile", input2.profile].map((part) => part.includes(" ") ? `"${part}"` : part).join(" ");
81724
+ const executable = powershellLiteral(environmentPrefix ? "powershell.exe" : input2.nodeExecutable);
79902
81725
  const argument = powershellLiteral(
79903
- [input2.cliEntry, "daemon", "--profile", input2.profile].map((part) => part.includes(" ") ? `"${part}"` : part).join(" ")
81726
+ environmentPrefix ? `-NoProfile -NonInteractive -WindowStyle Hidden -Command "${environmentPrefix}; & ${powershellLiteral(input2.nodeExecutable)} ${powershellLiteral(input2.cliEntry)} daemon --profile ${input2.profile}"` : directArgument
79904
81727
  );
79905
81728
  const installScript = [
79906
81729
  `$action = New-ScheduledTaskAction -Execute ${executable} -Argument ${argument}`,
@@ -79943,31 +81766,32 @@ WantedBy=default.target
79943
81766
  }
79944
81767
  };
79945
81768
  }
79946
- function currentServicePlan(args) {
79947
- const currentPlatform = (0, import_node_os13.platform)();
81769
+ function currentServicePlan(args, runtime) {
81770
+ const currentPlatform = (0, import_node_os15.platform)();
79948
81771
  if (currentPlatform !== "darwin" && currentPlatform !== "linux" && currentPlatform !== "win32") {
79949
81772
  throw new Error(`Daemon service installation is not supported on ${currentPlatform}`);
79950
81773
  }
79951
81774
  const profile = resolveEndpointProfileFromArgs(args);
79952
- const cliEntry = process.argv[1];
81775
+ const cliEntry = runtime?.cliEntry ?? process.argv[1];
79953
81776
  if (!cliEntry) throw new Error("Cannot determine the alan-agent executable path");
79954
81777
  return buildDaemonServicePlan({
79955
81778
  platform: currentPlatform,
79956
- homeDir: (0, import_node_os13.homedir)(),
79957
- nodeExecutable: process.execPath,
79958
- cliEntry: (0, import_node_path21.resolve)(cliEntry),
79959
- profile
81779
+ homeDir: (0, import_node_os15.homedir)(),
81780
+ nodeExecutable: runtime?.executable ?? process.execPath,
81781
+ cliEntry: (0, import_node_path26.resolve)(cliEntry),
81782
+ profile,
81783
+ environment: runtime?.environment
79960
81784
  });
79961
81785
  }
79962
81786
  function writeManifest(path2, contents) {
79963
- (0, import_node_fs19.mkdirSync)((0, import_node_path21.dirname)(path2), { recursive: true, mode: 448 });
81787
+ (0, import_node_fs25.mkdirSync)((0, import_node_path26.dirname)(path2), { recursive: true, mode: 448 });
79964
81788
  const pendingPath = `${path2}.pending-${process.pid}`;
79965
81789
  try {
79966
- (0, import_node_fs19.writeFileSync)(pendingPath, contents, { mode: 384 });
79967
- (0, import_node_fs19.chmodSync)(pendingPath, 384);
79968
- (0, import_node_fs19.renameSync)(pendingPath, path2);
81790
+ (0, import_node_fs25.writeFileSync)(pendingPath, contents, { mode: 384 });
81791
+ (0, import_node_fs25.chmodSync)(pendingPath, 384);
81792
+ (0, import_node_fs25.renameSync)(pendingPath, path2);
79969
81793
  } finally {
79970
- (0, import_node_fs19.rmSync)(pendingPath, { force: true });
81794
+ (0, import_node_fs25.rmSync)(pendingPath, { force: true });
79971
81795
  }
79972
81796
  }
79973
81797
  function runServiceCommand(command) {
@@ -79981,16 +81805,16 @@ function runServiceCommand(command) {
79981
81805
  }
79982
81806
  return { status, output: output2 };
79983
81807
  }
79984
- function installDaemonService(args = []) {
81808
+ function installDaemonService(args = [], runtime) {
79985
81809
  const profile = resolveEndpointProfileFromArgs(args);
79986
81810
  const configPath = resolveAgentConfigPath({ profile });
79987
81811
  if (!configExistsAtPath(configPath)) {
79988
81812
  throw new Error(`No ${profile} runtime is configured. Run alan-agent setup or login first.`);
79989
81813
  }
79990
- const plan = currentServicePlan(args);
81814
+ const plan = currentServicePlan(args, runtime);
79991
81815
  if (plan.manifestPath && plan.manifest) {
79992
- if ((0, import_node_os13.platform)() === "darwin") {
79993
- (0, import_node_fs19.mkdirSync)((0, import_node_path21.join)((0, import_node_os13.homedir)(), ".alan", "agent", "logs"), { recursive: true, mode: 448 });
81816
+ if ((0, import_node_os15.platform)() === "darwin") {
81817
+ (0, import_node_fs25.mkdirSync)((0, import_node_path26.join)((0, import_node_os15.homedir)(), ".alan", "agent", "logs"), { recursive: true, mode: 448 });
79994
81818
  }
79995
81819
  writeManifest(plan.manifestPath, plan.manifest);
79996
81820
  }
@@ -80000,7 +81824,7 @@ function installDaemonService(args = []) {
80000
81824
  function uninstallDaemonService(args = []) {
80001
81825
  const plan = currentServicePlan(args);
80002
81826
  for (const command of plan.uninstallCommands) runServiceCommand(command);
80003
- if (plan.manifestPath) (0, import_node_fs19.rmSync)(plan.manifestPath, { force: true });
81827
+ if (plan.manifestPath) (0, import_node_fs25.rmSync)(plan.manifestPath, { force: true });
80004
81828
  console.info("[alan-agent] Per-user daemon service removed");
80005
81829
  }
80006
81830
  function printDaemonServiceStatus(args = []) {
@@ -80012,11 +81836,11 @@ function printDaemonServiceStatus(args = []) {
80012
81836
 
80013
81837
  // src/skills/skill-cli.ts
80014
81838
  var import_promises6 = require("fs/promises");
80015
- var import_node_os14 = require("os");
80016
- var import_node_path22 = require("path");
81839
+ var import_node_os16 = require("os");
81840
+ var import_node_path27 = require("path");
80017
81841
  async function runSkillsCommand(args) {
80018
81842
  const [subcommand, ...options] = args;
80019
- const homeDirectory = option(options, "--home") ?? (0, import_node_os14.homedir)();
81843
+ const homeDirectory = option(options, "--home") ?? (0, import_node_os16.homedir)();
80020
81844
  const manager = await RuntimeSkillManager.open({ homeDirectory });
80021
81845
  if (subcommand === "scan") {
80022
81846
  const inputPath = option(options, "--input");
@@ -80057,7 +81881,7 @@ async function defaultScanInput(homeDirectory) {
80057
81881
  });
80058
81882
  }
80059
81883
  async function readProtectedJson(path2) {
80060
- const absolutePath = (0, import_node_path22.resolve)(path2);
81884
+ const absolutePath = (0, import_node_path27.resolve)(path2);
80061
81885
  const stat2 = await (0, import_promises6.lstat)(absolutePath);
80062
81886
  if (!stat2.isFile()) throw new Error(`Skill input is not a regular file: ${absolutePath}`);
80063
81887
  if (process.platform !== "win32" && (stat2.mode & 63) !== 0) {
@@ -80207,6 +82031,17 @@ async function main() {
80207
82031
  await setupDaemon(commandArgs);
80208
82032
  return;
80209
82033
  }
82034
+ if (command === "install") {
82035
+ bindConfigPath(commandArgs);
82036
+ const configured = readConfig();
82037
+ if (!configured.runtimeId || !configured.runtimeToken) {
82038
+ if (commandArgs.includes("--setup-token")) await setupDaemon(commandArgs);
82039
+ else await loginWithDeviceCode(commandArgs);
82040
+ }
82041
+ installDaemonService(commandArgs);
82042
+ runMcpIntegrationCommand(["reconcile", ...commandArgs]);
82043
+ return;
82044
+ }
80210
82045
  if (command === "login") {
80211
82046
  await loginWithDeviceCode(commandArgs);
80212
82047
  return;
@@ -80217,6 +82052,7 @@ async function main() {
80217
82052
  }
80218
82053
  if (command === "service" && commandArgs[0] === "install") {
80219
82054
  installDaemonService(commandArgs.slice(1));
82055
+ runMcpIntegrationCommand(["reconcile", ...commandArgs.slice(1)]);
80220
82056
  return;
80221
82057
  }
80222
82058
  if (command === "service" && commandArgs[0] === "uninstall") {
@@ -80227,6 +82063,18 @@ async function main() {
80227
82063
  printDaemonServiceStatus(commandArgs.slice(1));
80228
82064
  return;
80229
82065
  }
82066
+ if (command === "integrations" && commandArgs[0] === "capabilities") {
82067
+ printNativeIntegrationStatus(commandArgs.slice(1));
82068
+ return;
82069
+ }
82070
+ if (command === "integrations") {
82071
+ runMcpIntegrationCommand(commandArgs);
82072
+ return;
82073
+ }
82074
+ if (command === "hook") {
82075
+ runNativeHookCommand(commandArgs);
82076
+ return;
82077
+ }
80230
82078
  if (command === "stop") {
80231
82079
  await stopDaemon(commandArgs);
80232
82080
  return;