@alan-ai-hq/agent-manager 0.1.90 → 0.1.92

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 +1256 -1010
  2. package/package.json +3 -3
package/dist/index.cjs CHANGED
@@ -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: randomBytes13, createHash: createHash17 } = require("crypto");
3407
+ var { randomBytes: randomBytes13, createHash: createHash18 } = require("crypto");
3408
3408
  var { Duplex, Readable: Readable2 } = require("stream");
3409
3409
  var { URL: URL2 } = require("url");
3410
3410
  var PerMessageDeflate = require_permessage_deflate();
@@ -4061,7 +4061,7 @@ var require_websocket = __commonJS({
4061
4061
  abortHandshake(websocket, socket, "Invalid Upgrade header");
4062
4062
  return;
4063
4063
  }
4064
- const digest = createHash17("sha1").update(key + GUID).digest("base64");
4064
+ const digest = createHash18("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: createHash17 } = require("crypto");
4431
+ var { createHash: createHash18 } = 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 = createHash17("sha1").update(key + GUID).digest("base64");
4728
+ const digest = createHash18("sha1").update(key + GUID).digest("base64");
4729
4729
  const headers = [
4730
4730
  "HTTP/1.1 101 Switching Protocols",
4731
4731
  "Upgrade: websocket",
@@ -15451,7 +15451,7 @@ function describeError(error61) {
15451
15451
  }
15452
15452
 
15453
15453
  // src/version.ts
15454
- var AGENT_VERSION = "0.1.90";
15454
+ var AGENT_VERSION = "0.1.92";
15455
15455
 
15456
15456
  // src/daemon-worktree.ts
15457
15457
  var import_node_child_process3 = require("child_process");
@@ -36003,6 +36003,42 @@ var import_fs = require("fs");
36003
36003
  var import_os = require("os");
36004
36004
  var import_path = require("path");
36005
36005
 
36006
+ // ../shared/dist/utils/workflow-tools-filter.js
36007
+ var WORKFLOW_ALWAYS_ALLOWED_BUILTIN_TOOLS = [
36008
+ "search_context",
36009
+ "create_test_report",
36010
+ "create_plan",
36011
+ "create_document_artifact",
36012
+ "upsert_prototype_version",
36013
+ "list_artifacts",
36014
+ "get_artifact",
36015
+ "list_prototypes",
36016
+ "get_prototype"
36017
+ ];
36018
+ function createWorkflowToolsFilter(workflowTools) {
36019
+ const hasFilter = !!workflowTools && workflowTools.length > 0;
36020
+ if (!hasFilter) {
36021
+ return {
36022
+ hasFilter: false,
36023
+ isMcpServerAllowed: () => true,
36024
+ resolveAllowedBuiltinTools: () => null
36025
+ };
36026
+ }
36027
+ const tools = workflowTools;
36028
+ const mcpServerIds = new Set(tools.filter((t) => t.type === "mcp").map((t) => t.id));
36029
+ const builtinToolIds = tools.filter((t) => t.type === "builtin" || t.type === "integration").map((t) => t.id);
36030
+ return {
36031
+ hasFilter: true,
36032
+ isMcpServerAllowed: (serverName) => mcpServerIds.has(serverName),
36033
+ resolveAllowedBuiltinTools: () => {
36034
+ const merged = new Set(builtinToolIds);
36035
+ for (const id of WORKFLOW_ALWAYS_ALLOWED_BUILTIN_TOOLS)
36036
+ merged.add(id);
36037
+ return [...merged];
36038
+ }
36039
+ };
36040
+ }
36041
+
36006
36042
  // ../shared/dist/agent/agent-definitions.js
36007
36043
  var COORDINATOR_PROMPT = `## You are the Orchestrator
36008
36044
 
@@ -36490,6 +36526,34 @@ function buildAlanMcpServers(registration) {
36490
36526
  }
36491
36527
  };
36492
36528
  }
36529
+ var ALAN_RUN_REFERENCE_ENV = "ALAN_RUN_REFERENCE";
36530
+ var MCP_CONFIG_KEY = /^[A-Za-z0-9_-]+$/;
36531
+ function isExternalMcpServerName(name) {
36532
+ return MCP_CONFIG_KEY.test(name) && name !== "alan";
36533
+ }
36534
+ function isMcpConfigKey(name) {
36535
+ return MCP_CONFIG_KEY.test(name);
36536
+ }
36537
+ var CURSOR_RUN_REFERENCE_HEADER_VALUE = `\${env:${ALAN_RUN_REFERENCE_ENV}}`;
36538
+ function toCursorMcpServers(src) {
36539
+ const out = {};
36540
+ for (const [name, raw] of Object.entries(src)) {
36541
+ if (!raw || typeof raw !== "object")
36542
+ continue;
36543
+ const s = raw;
36544
+ const isHttp = typeof s.type === "string" && s.type === "http" || typeof s.url === "string";
36545
+ if (!isHttp) {
36546
+ out[name] = s;
36547
+ continue;
36548
+ }
36549
+ const headers = Object.fromEntries(Object.entries(s.headers ?? {}).filter(([key]) => key.toLowerCase() !== "x-alan-run-reference"));
36550
+ out[name] = {
36551
+ ...s,
36552
+ headers: { ...headers, "x-alan-run-reference": CURSOR_RUN_REFERENCE_HEADER_VALUE }
36553
+ };
36554
+ }
36555
+ return out;
36556
+ }
36493
36557
  function toFactoryMcpServers(src) {
36494
36558
  const out = {};
36495
36559
  for (const [name, raw] of Object.entries(src)) {
@@ -36634,7 +36698,10 @@ function renderAlanMcpConfigFiles(mcpServers, home) {
36634
36698
  mergeJsonKey: "mcpServers"
36635
36699
  },
36636
36700
  { path: `${home}/.codex/config.toml`, content: toCodexMcpToml(mcpServers) },
36637
- { path: `${home}/.cursor/mcp.json`, content: JSON.stringify({ mcpServers }) },
36701
+ {
36702
+ path: `${home}/.cursor/mcp.json`,
36703
+ content: JSON.stringify({ mcpServers: toCursorMcpServers(mcpServers) })
36704
+ },
36638
36705
  {
36639
36706
  path: `${home}/.factory/mcp.json`,
36640
36707
  content: JSON.stringify({ mcpServers: toFactoryMcpServers(mcpServers) })
@@ -38302,6 +38369,7 @@ var import_os4 = require("os");
38302
38369
  var import_path5 = require("path");
38303
38370
  var import_url = require("url");
38304
38371
  var import_crypto4 = require("crypto");
38372
+ var import_crypto5 = require("crypto");
38305
38373
  var import_fs6 = require("fs");
38306
38374
  var import_os5 = require("os");
38307
38375
  var import_path6 = require("path");
@@ -38319,14 +38387,16 @@ var import_path9 = require("path");
38319
38387
  var import_fs10 = require("fs");
38320
38388
  var import_os9 = require("os");
38321
38389
  var import_path10 = require("path");
38322
- var import_crypto5 = require("crypto");
38390
+ var import_crypto6 = require("crypto");
38323
38391
  var import_readline2 = require("readline");
38324
38392
  var import_url2 = require("url");
38325
- var import_crypto6 = require("crypto");
38393
+ var import_crypto7 = require("crypto");
38326
38394
  var import_fs11 = require("fs");
38327
38395
  var import_path11 = require("path");
38328
- var ALAN_RUN_REFERENCE_ENV = "ALAN_RUN_REFERENCE";
38329
38396
  var ALAN_MCP_URL_ENV = "ALAN_MCP_URL";
38397
+ function deliversRunReferenceViaEnv(backendKind) {
38398
+ return backendKind === "cursor_agent_cli";
38399
+ }
38330
38400
  var ALAN_AGENT_ENTRY_ENV = "ALAN_AGENT_ENTRY";
38331
38401
  var ALAN_NODE_EXECUTABLE_ENV = "ALAN_NODE_EXECUTABLE";
38332
38402
  function usesProcessLocalManagedMcp(backendKind) {
@@ -41091,6 +41161,65 @@ function createAntigravityCliBackend(command = "agy", defaultArgs = []) {
41091
41161
  }
41092
41162
  };
41093
41163
  }
41164
+ function isWorkflowRun(config2) {
41165
+ return Boolean(config2.workflowId || config2.workflowExecutionId);
41166
+ }
41167
+ function selectExternalMcpServers(config2) {
41168
+ const selected = {};
41169
+ const filter = isWorkflowRun(config2) ? createWorkflowToolsFilter(config2.workflowTools) : null;
41170
+ if (filter && !filter.hasFilter) return selected;
41171
+ for (const [name, server] of Object.entries(config2.externalMcpServers ?? {})) {
41172
+ if (!isExternalMcpServerName(name)) continue;
41173
+ if (filter && !filter.isMcpServerAllowed(name)) continue;
41174
+ selected[name] = server;
41175
+ }
41176
+ return selected;
41177
+ }
41178
+ function headerEnvName(server, header) {
41179
+ const fingerprint = (0, import_crypto5.createHash)("sha256").update(`${server}\0${header}`).digest("hex").slice(0, 12).toUpperCase();
41180
+ return `ALAN_MCP_HEADER_${fingerprint}`;
41181
+ }
41182
+ function planExternalMcpDelivery(config2, processEnv) {
41183
+ const verbatim = selectExternalMcpServers(config2);
41184
+ const servers = {};
41185
+ const env = {};
41186
+ for (const [name, server] of Object.entries(verbatim)) {
41187
+ if (server.url && server.type !== "stdio") {
41188
+ const headerEnv = {};
41189
+ for (const [header, value2] of Object.entries(server.headers ?? {})) {
41190
+ if (!isMcpConfigKey(header)) continue;
41191
+ const variable = headerEnvName(name, header);
41192
+ env[variable] = value2;
41193
+ headerEnv[header] = variable;
41194
+ }
41195
+ servers[name] = {
41196
+ type: server.type === "sse" ? "sse" : "http",
41197
+ url: server.url,
41198
+ ...Object.keys(headerEnv).length ? { headerEnv } : {}
41199
+ };
41200
+ } else if (server.command) {
41201
+ const envKeys = [];
41202
+ for (const [key, value2] of Object.entries(server.env ?? {})) {
41203
+ if (!isMcpConfigKey(key)) continue;
41204
+ if (key in processEnv || key in env && env[key] !== value2) {
41205
+ console.warn(
41206
+ `[external-mcp] Not forwarding env ${key} for MCP server ${name}: it is already set for this run`
41207
+ );
41208
+ continue;
41209
+ }
41210
+ env[key] = value2;
41211
+ envKeys.push(key);
41212
+ }
41213
+ servers[name] = {
41214
+ type: "stdio",
41215
+ command: server.command,
41216
+ ...server.args?.length ? { args: server.args } : {},
41217
+ ...envKeys.length ? { envKeys } : {}
41218
+ };
41219
+ }
41220
+ }
41221
+ return { servers, env, verbatim };
41222
+ }
41094
41223
  function resolveClaudeHome(env, variant = "claude") {
41095
41224
  if (variant === "supatest") {
41096
41225
  return (0, import_path6.join)((0, import_os5.homedir)(), ".supatest", "claude-internal");
@@ -41525,22 +41654,40 @@ function buildClaudeModelArg(baseModel, selectedContextWindow) {
41525
41654
  if (selectedContextWindow === "1m") return `${baseModel}[1m]`;
41526
41655
  return baseModel;
41527
41656
  }
41528
- function buildClaudeManagedMcpArgs(registration, env) {
41529
- const entry = env[ALAN_AGENT_ENTRY_ENV]?.trim() || process.argv[1];
41530
- const node2 = env[ALAN_NODE_EXECUTABLE_ENV]?.trim() || process.execPath;
41531
- if (!entry) throw new Error("Cannot resolve the Alan MCP proxy entrypoint");
41657
+ function buildClaudeManagedMcpArgs(registration, options = {}) {
41658
+ const mcpServers = {};
41659
+ for (const [name, server] of Object.entries(options.externalServers ?? {})) {
41660
+ if (server.type === "stdio") {
41661
+ mcpServers[name] = {
41662
+ type: "stdio",
41663
+ command: server.command,
41664
+ ...server.args?.length ? { args: server.args } : {},
41665
+ ...server.envKeys?.length ? { env: Object.fromEntries(server.envKeys.map((key) => [key, `\${${key}}`])) } : {}
41666
+ };
41667
+ } else {
41668
+ mcpServers[name] = {
41669
+ type: server.type,
41670
+ url: server.url,
41671
+ ...server.headerEnv ? {
41672
+ headers: Object.fromEntries(
41673
+ Object.entries(server.headerEnv).map(([header, variable]) => [
41674
+ header,
41675
+ `\${${variable}}`
41676
+ ])
41677
+ )
41678
+ } : {}
41679
+ };
41680
+ }
41681
+ }
41682
+ mcpServers.alan = {
41683
+ type: "http",
41684
+ url: registration.url,
41685
+ headers: { "x-alan-run-reference": `\${${ALAN_RUN_REFERENCE_ENV}}` }
41686
+ };
41532
41687
  return [
41533
- "--strict-mcp-config",
41688
+ ...options.strict ? ["--strict-mcp-config"] : [],
41534
41689
  "--mcp-config",
41535
- JSON.stringify({
41536
- mcpServers: {
41537
- [managedMcpServerName(registration)]: {
41538
- type: "stdio",
41539
- command: node2,
41540
- args: [entry, "mcp-proxy"]
41541
- }
41542
- }
41543
- })
41690
+ JSON.stringify({ mcpServers })
41544
41691
  ];
41545
41692
  }
41546
41693
  function createClaudeCliBackend(command = "claude", defaultArgs = []) {
@@ -41549,11 +41696,12 @@ function createClaudeCliBackend(command = "claude", defaultArgs = []) {
41549
41696
  supportTier: "structured",
41550
41697
  async run(context) {
41551
41698
  const managedRegistration = context.config.alanMcp;
41699
+ const externalMcp = managedRegistration ? planExternalMcpDelivery(context.config, context.env) : void 0;
41552
41700
  const runContext = managedRegistration ? {
41553
41701
  ...context,
41554
41702
  env: {
41555
41703
  ...context.env,
41556
- [ALAN_MCP_URL_ENV]: managedRegistration.url,
41704
+ ...externalMcp?.env,
41557
41705
  [ALAN_RUN_REFERENCE_ENV]: requireManagedRunReference(managedRegistration)
41558
41706
  }
41559
41707
  } : context;
@@ -41567,7 +41715,12 @@ function createClaudeCliBackend(command = "claude", defaultArgs = []) {
41567
41715
  getClaudePermissionMode(context.config)
41568
41716
  ];
41569
41717
  if (managedRegistration) {
41570
- args.push(...buildClaudeManagedMcpArgs(managedRegistration, runContext.env));
41718
+ args.push(
41719
+ ...buildClaudeManagedMcpArgs(managedRegistration, {
41720
+ strict: isWorkflowRun(context.config),
41721
+ externalServers: externalMcp?.servers
41722
+ })
41723
+ );
41571
41724
  }
41572
41725
  const disallowedTools = getClaudeDisallowedTools(context.config);
41573
41726
  if (disallowedTools.length > 0) {
@@ -42795,27 +42948,60 @@ function buildCodexEffortArgs(selectedEffortLevel, options) {
42795
42948
  return ["-c", `model_reasoning_effort=${level}`];
42796
42949
  }
42797
42950
  var CODEX_STARTUP_TIMEOUT_MS = 3e5;
42798
- function buildCodexManagedMcpArgs(registration) {
42799
- const serverName = managedMcpServerName(registration);
42951
+ var ALAN_MCP_STARTUP_TIMEOUT_SEC = 30;
42952
+ function buildCodexManagedMcpArgs(registration, options = {}) {
42800
42953
  return [
42801
- "--ignore-user-config",
42954
+ // Workflow runs keep the exclusive config: their tool set is a policy,
42955
+ // not the user's own server list.
42956
+ ...options.strict ? ["--ignore-user-config"] : [],
42957
+ "-c",
42958
+ `mcp_servers.alan.url=${JSON.stringify(registration.url)}`,
42802
42959
  "-c",
42803
- `mcp_servers.${serverName}.url=${JSON.stringify(registration.url)}`,
42960
+ `mcp_servers.alan.env_http_headers.x-alan-run-reference=${JSON.stringify(ALAN_RUN_REFERENCE_ENV)}`,
42804
42961
  "-c",
42805
- `mcp_servers.${serverName}.env_http_headers.x-alan-run-reference=${JSON.stringify(ALAN_RUN_REFERENCE_ENV)}`
42962
+ "mcp_servers.alan.required=true",
42963
+ "-c",
42964
+ `mcp_servers.alan.startup_timeout_sec=${ALAN_MCP_STARTUP_TIMEOUT_SEC}`
42806
42965
  ];
42807
42966
  }
42967
+ function buildCodexExternalMcpArgs(servers) {
42968
+ const args = [];
42969
+ for (const [name, server] of Object.entries(servers ?? {})) {
42970
+ const key = `mcp_servers.${name}`;
42971
+ if (server.type === "stdio") {
42972
+ args.push("-c", `${key}.command=${JSON.stringify(server.command)}`);
42973
+ if (server.args?.length) args.push("-c", `${key}.args=${JSON.stringify(server.args)}`);
42974
+ if (server.envKeys?.length) {
42975
+ args.push("-c", `${key}.env_vars=${JSON.stringify(server.envKeys)}`);
42976
+ }
42977
+ } else {
42978
+ if (server.type === "sse") continue;
42979
+ args.push("-c", `${key}.url=${JSON.stringify(server.url)}`);
42980
+ for (const [header, variable] of Object.entries(server.headerEnv ?? {})) {
42981
+ args.push("-c", `${key}.env_http_headers.${header}=${JSON.stringify(variable)}`);
42982
+ }
42983
+ }
42984
+ }
42985
+ return args;
42986
+ }
42808
42987
  function createCodexRuntimeBackend(command = "codex", defaultArgs = []) {
42809
42988
  return {
42810
42989
  kind: "codex_app_server",
42811
42990
  supportTier: "structured",
42812
42991
  async run(context) {
42813
42992
  const managedRegistration = context.config.alanMcp;
42814
- const managedMcpArgs = managedRegistration ? buildCodexManagedMcpArgs(managedRegistration) : [];
42993
+ const externalMcp = managedRegistration ? planExternalMcpDelivery(context.config, context.env) : void 0;
42994
+ const managedMcpArgs = managedRegistration ? [
42995
+ ...buildCodexManagedMcpArgs(managedRegistration, {
42996
+ strict: isWorkflowRun(context.config)
42997
+ }),
42998
+ ...buildCodexExternalMcpArgs(externalMcp?.servers)
42999
+ ] : [];
42815
43000
  const runContext = managedRegistration ? {
42816
43001
  ...context,
42817
43002
  env: {
42818
43003
  ...context.env,
43004
+ ...externalMcp?.env,
42819
43005
  [ALAN_RUN_REFERENCE_ENV]: requireManagedRunReference(managedRegistration)
42820
43006
  }
42821
43007
  } : context;
@@ -43776,6 +43962,13 @@ function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = [])
43776
43962
  context.cwd
43777
43963
  );
43778
43964
  const approveMcps = context.config.alanMcp ? await cliSupportsFlag(command, "--approve-mcps", context.env) : false;
43965
+ const runContext = context.config.alanMcp ? {
43966
+ ...context,
43967
+ env: {
43968
+ ...context.env,
43969
+ [ALAN_RUN_REFERENCE_ENV]: requireManagedRunReference(context.config.alanMcp)
43970
+ }
43971
+ } : context;
43779
43972
  try {
43780
43973
  return await createGenericCliBackend({
43781
43974
  kind: "cursor_agent_cli",
@@ -43853,7 +44046,7 @@ function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = [])
43853
44046
  return appendImagePathReferences(prompt, imageFiles);
43854
44047
  },
43855
44048
  parseStructuredLine: parseCursorStructuredLine
43856
- }).run(context);
44049
+ }).run(runContext);
43857
44050
  } finally {
43858
44051
  cleanup();
43859
44052
  }
@@ -44909,6 +45102,28 @@ function resolveKimiCliModelArg(model) {
44909
45102
  if (trimmed.startsWith("claude-")) return void 0;
44910
45103
  return trimmed;
44911
45104
  }
45105
+ function buildKimiMcpConfig(registration, externalServers = {}) {
45106
+ const mcpServers = {};
45107
+ for (const [name, server] of Object.entries(externalServers)) {
45108
+ if (server.url && server.type !== "stdio") {
45109
+ mcpServers[name] = {
45110
+ url: server.url,
45111
+ ...server.headers ? { headers: server.headers } : {}
45112
+ };
45113
+ } else if (server.command) {
45114
+ mcpServers[name] = {
45115
+ command: server.command,
45116
+ ...server.args?.length ? { args: server.args } : {},
45117
+ ...server.env ? { env: server.env } : {}
45118
+ };
45119
+ }
45120
+ }
45121
+ mcpServers[managedMcpServerName(registration)] = {
45122
+ url: registration.url,
45123
+ headers: { "x-alan-run-reference": requireManagedRunReference(registration) }
45124
+ };
45125
+ return { mcpServers };
45126
+ }
44912
45127
  function createKimiCliBackend(command = "kimi-cli", defaultArgs = []) {
44913
45128
  return {
44914
45129
  kind: "kimi_cli",
@@ -44920,14 +45135,9 @@ function createKimiCliBackend(command = "kimi-cli", defaultArgs = []) {
44920
45135
  if (registration && mcpConfigPath) {
44921
45136
  (0, import_fs9.writeFileSync)(
44922
45137
  mcpConfigPath,
44923
- JSON.stringify({
44924
- mcpServers: {
44925
- [managedMcpServerName(registration)]: {
44926
- url: registration.url,
44927
- headers: { "x-alan-run-reference": requireManagedRunReference(registration) }
44928
- }
44929
- }
44930
- }),
45138
+ JSON.stringify(
45139
+ buildKimiMcpConfig(registration, selectExternalMcpServers(context.config))
45140
+ ),
44931
45141
  { mode: 384 }
44932
45142
  );
44933
45143
  }
@@ -45454,7 +45664,7 @@ function leaseKeyFor(context) {
45454
45664
  if (!conversationId) return null;
45455
45665
  const runReference = context.config.alanMcp?.headers?.["x-alan-run-reference"];
45456
45666
  if (!runReference) return `conv:${conversationId}`;
45457
- const fingerprint = (0, import_crypto5.createHash)("sha256").update(runReference).digest("hex").slice(0, 16);
45667
+ const fingerprint = (0, import_crypto6.createHash)("sha256").update(runReference).digest("hex").slice(0, 16);
45458
45668
  return `conv:${conversationId}|mcp:${fingerprint}`;
45459
45669
  }
45460
45670
  function isLeaseUsable(lease) {
@@ -46479,8 +46689,10 @@ var BaseMachineAgent = class _BaseMachineAgent {
46479
46689
  */
46480
46690
  async beginMcpConfigCriticalSection(backend, context) {
46481
46691
  const runHeaders = context.config.alanMcp?.headers;
46482
- if (!runHeaders || usesProcessLocalManagedMcp(backend.kind)) return () => {
46483
- };
46692
+ if (!runHeaders || usesProcessLocalManagedMcp(backend.kind) || deliversRunReferenceViaEnv(backend.kind)) {
46693
+ return () => {
46694
+ };
46695
+ }
46484
46696
  const releaseGate = await acquireMcpConfigWriteGate();
46485
46697
  try {
46486
46698
  const writtenClis = await syncAlanMcpSessionHeaders({
@@ -46493,7 +46705,11 @@ var BaseMachineAgent = class _BaseMachineAgent {
46493
46705
  void this.presenter.onLog(`Synced Alan MCP session headers to ${writtenClis.join(", ")}`);
46494
46706
  }
46495
46707
  } catch (error61) {
46496
- console.warn("[base-machine-agent] Failed to sync MCP session headers", error61);
46708
+ releaseGate();
46709
+ throw new Error(
46710
+ `Alan could not write this run's MCP credential for ${backend.kind}, so the provider was not started: ${error61 instanceof Error ? error61.message : String(error61)}`,
46711
+ { cause: error61 }
46712
+ );
46497
46713
  }
46498
46714
  return releaseGate;
46499
46715
  }
@@ -46988,7 +47204,7 @@ function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName, op
46988
47204
  const { width, height } = readImageDimensions2(buffer, mimeType);
46989
47205
  images.push({
46990
47206
  type: "generated_image",
46991
- id: (0, import_crypto6.randomUUID)(),
47207
+ id: (0, import_crypto7.randomUUID)(),
46992
47208
  filename: (0, import_path11.basename)(path2),
46993
47209
  mimeType,
46994
47210
  size: buffer.length,
@@ -46996,7 +47212,7 @@ function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName, op
46996
47212
  height,
46997
47213
  data: buffer.toString("base64"),
46998
47214
  sourcePath: path2,
46999
- sourceHash: (0, import_crypto6.createHash)("sha256").update(buffer).digest("hex"),
47215
+ sourceHash: (0, import_crypto7.createHash)("sha256").update(buffer).digest("hex"),
47000
47216
  toolId,
47001
47217
  ts: Date.now()
47002
47218
  });
@@ -47028,7 +47244,7 @@ function extractSessionMediaFromToolResult(toolId, content, cwd, toolName, optio
47028
47244
  media.push({
47029
47245
  type: "session_media",
47030
47246
  kind,
47031
- id: (0, import_crypto6.randomUUID)(),
47247
+ id: (0, import_crypto7.randomUUID)(),
47032
47248
  filename: (0, import_path11.basename)(path2),
47033
47249
  mimeType,
47034
47250
  size: buffer.length,
@@ -47036,7 +47252,7 @@ function extractSessionMediaFromToolResult(toolId, content, cwd, toolName, optio
47036
47252
  height: dimensions?.height,
47037
47253
  data: buffer.toString("base64"),
47038
47254
  sourcePath: path2,
47039
- sourceHash: (0, import_crypto6.createHash)("sha256").update(buffer).digest("hex"),
47255
+ sourceHash: (0, import_crypto7.createHash)("sha256").update(buffer).digest("hex"),
47040
47256
  toolId,
47041
47257
  ts: Date.now()
47042
47258
  });
@@ -56800,7 +57016,12 @@ async function ensureAlanMcpRegistered(backendKind, registration, home = (0, imp
56800
57016
  releaseLock();
56801
57017
  }
56802
57018
  }
56803
- const fileOk = paths.length > 0 && paths.every((p) => registrationsMatch(readAlanMcpRegistration(p), registration));
57019
+ const fileOk = files.length > 0 && files.every(
57020
+ (file2) => registrationsMatch(
57021
+ readAlanMcpRegistration(file2.path),
57022
+ parseAlanMcpRegistration(file2.path, file2.content) ?? registration
57023
+ )
57024
+ );
56804
57025
  if (!fileOk) {
56805
57026
  const issue2 = {
56806
57027
  code: "mcp_config_repair_required",
@@ -56974,7 +57195,8 @@ function hasUsableRegistrationCredential(headers) {
56974
57195
  const normalized = new Map(
56975
57196
  Object.entries(headers).map(([name, value2]) => [name.toLowerCase(), value2.trim()])
56976
57197
  );
56977
- return Boolean(normalized.get("x-alan-run-reference") || normalized.get("authorization"));
57198
+ const reference = normalized.get("x-alan-run-reference") ?? "";
57199
+ return Boolean(reference && !reference.startsWith("${") || normalized.get("authorization"));
56978
57200
  }
56979
57201
  function readAlanMcpRegistration(path2) {
56980
57202
  return parseAlanMcpRegistration(path2, (0, import_node_fs16.readFileSync)(path2, "utf8"));
@@ -75266,6 +75488,7 @@ async function handleAgentExecute(ctx, payload) {
75266
75488
  prMeta: payload.prMeta,
75267
75489
  conversationId: payload.conversationId,
75268
75490
  alanMcp: payload.alanMcp,
75491
+ externalMcpServers: payload.externalMcpServers,
75269
75492
  taskId: payload.taskId ?? payload.taskMeta?.id,
75270
75493
  teamId: payload.teamId,
75271
75494
  currentUser: payload.currentUser,
@@ -82145,345 +82368,966 @@ function runMcpIntegrationCommand(args, options = {}) {
82145
82368
  throw new Error(`Unknown integrations action: ${action}`);
82146
82369
  }
82147
82370
 
82148
- // src/lifecycle-logger.ts
82149
- var AgentLifecycleLogger = class _AgentLifecycleLogger {
82150
- context;
82151
- constructor(context = {}) {
82152
- this.context = normalizeContext(context);
82153
- }
82154
- setContext(context) {
82155
- this.context = normalizeContext({ ...this.context, ...context });
82156
- }
82157
- child(context) {
82158
- return new _AgentLifecycleLogger({ ...this.context, ...context });
82159
- }
82160
- debug(event, fields = {}) {
82161
- this.log("debug", event, fields);
82162
- }
82163
- info(event, fields = {}) {
82164
- this.log("info", event, fields);
82165
- }
82166
- warn(event, fields = {}) {
82167
- this.log("warn", event, fields);
82168
- }
82169
- error(event, fields = {}) {
82170
- this.log("error", event, fields);
82171
- }
82172
- log(level, event, fields) {
82173
- const record2 = omitUndefined({
82174
- ts: (/* @__PURE__ */ new Date()).toISOString(),
82175
- level,
82176
- component: "agent-manager",
82177
- event,
82178
- ...this.context,
82179
- ...normalizeFields(fields)
82180
- });
82181
- console[level](JSON.stringify(record2));
82182
- }
82183
- };
82184
- function lifecycleContextFromEnv(env) {
82185
- return normalizeContext({
82186
- taskId: env.ALAN_TASK_ID,
82187
- conversationId: env.ALAN_SESSION_ID,
82188
- runId: env.ALAN_RUN_ID,
82189
- sandboxId: env.ALAN_SANDBOX_ID,
82190
- provider: env.ALAN_SANDBOX_PROVIDER,
82191
- backendKind: env.ALAN_BACKEND_KIND,
82192
- agentVersion: AGENT_VERSION
82193
- });
82194
- }
82195
- function normalizeContext(context) {
82196
- return omitUndefined({
82197
- ...context,
82198
- agentVersion: context.agentVersion ?? AGENT_VERSION
82199
- });
82371
+ // src/service-manager.ts
82372
+ var import_node_child_process9 = require("child_process");
82373
+ var import_node_fs25 = require("fs");
82374
+ var import_node_os16 = require("os");
82375
+ var import_node_path27 = require("path");
82376
+ var SERVICE_LABEL = "ai.tryalan.agent";
82377
+ var SYSTEMD_UNIT = "alan-agent.service";
82378
+ var WINDOWS_TASK = "Alan Agent";
82379
+ function xml(value2) {
82380
+ return value2.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
82200
82381
  }
82201
- function normalizeFields(fields) {
82202
- const normalized = {};
82203
- for (const [key, value2] of Object.entries(fields)) {
82204
- if (value2 instanceof Error) {
82205
- normalized[key] = {
82206
- name: value2.name,
82207
- message: value2.message,
82208
- stack: value2.stack
82209
- };
82210
- } else {
82211
- normalized[key] = value2;
82212
- }
82213
- }
82214
- return normalized;
82382
+ function systemdArg(value2) {
82383
+ return `"${value2.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
82215
82384
  }
82216
- function omitUndefined(record2) {
82217
- return Object.fromEntries(Object.entries(record2).filter(([, value2]) => value2 !== void 0));
82385
+ function powershellLiteral(value2) {
82386
+ return `'${value2.replaceAll("'", "''")}'`;
82218
82387
  }
82219
-
82220
- // ../../node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
82221
- var __create3 = Object.create;
82222
- var __defProp3 = Object.defineProperty;
82223
- var __getOwnPropDesc3 = Object.getOwnPropertyDescriptor;
82224
- var __getOwnPropNames3 = Object.getOwnPropertyNames;
82225
- var __getProtoOf3 = Object.getPrototypeOf;
82226
- var __hasOwnProp3 = Object.prototype.hasOwnProperty;
82227
- var __commonJSMin2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
82228
- var __exportAll2 = (all, symbols) => {
82229
- let target = {};
82230
- for (var name in all) {
82231
- __defProp3(target, name, {
82232
- get: all[name],
82233
- enumerable: true
82234
- });
82235
- }
82236
- if (symbols) {
82237
- __defProp3(target, Symbol.toStringTag, { value: "Module" });
82388
+ function serviceNames(profile) {
82389
+ if (profile === "production") {
82390
+ return { launchd: SERVICE_LABEL, systemd: SYSTEMD_UNIT, windows: WINDOWS_TASK };
82238
82391
  }
82239
- return target;
82240
- };
82241
- var __copyProps3 = (to, from, except, desc) => {
82242
- if (from && typeof from === "object" || typeof from === "function") {
82243
- for (var keys = __getOwnPropNames3(from), i = 0, n = keys.length, key; i < n; i++) {
82244
- key = keys[i];
82245
- if (!__hasOwnProp3.call(to, key) && key !== except) {
82246
- __defProp3(to, key, {
82247
- get: ((k) => from[k]).bind(null, key),
82248
- enumerable: !(desc = __getOwnPropDesc3(from, key)) || desc.enumerable
82249
- });
82250
- }
82251
- }
82392
+ const suffix = profile === "custom" ? "prod" : profile;
82393
+ const title = `${suffix[0]?.toUpperCase()}${suffix.slice(1)}`;
82394
+ return {
82395
+ launchd: `${SERVICE_LABEL}.${suffix}`,
82396
+ systemd: `alan-agent-${suffix}.service`,
82397
+ windows: `${WINDOWS_TASK} ${title}`
82398
+ };
82399
+ }
82400
+ function buildDaemonServicePlan(input2) {
82401
+ const daemonArgs = [input2.nodeExecutable, input2.cliEntry, "daemon", "--profile", input2.profile];
82402
+ const names = serviceNames(input2.profile);
82403
+ if (input2.platform === "darwin") {
82404
+ const userId = input2.userId ?? process.getuid?.();
82405
+ if (userId === void 0) throw new Error("Cannot determine the current macOS user ID");
82406
+ const domain2 = `gui/${userId}`;
82407
+ const serviceTarget = `${domain2}/${names.launchd}`;
82408
+ const manifestPath = (0, import_node_path27.join)(input2.homeDir, "Library", "LaunchAgents", `${names.launchd}.plist`);
82409
+ const logDir = (0, import_node_path27.join)(resolveAgentConfigDirectory(input2.profile, input2.homeDir), "logs");
82410
+ const programArguments = daemonArgs.map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
82411
+ const environmentEntries = Object.entries(input2.environment ?? {}).map(([key, value2]) => ` <key>${xml(key)}</key>
82412
+ <string>${xml(value2)}</string>`).join("\n");
82413
+ const environmentBlock = environmentEntries ? ` <key>EnvironmentVariables</key>
82414
+ <dict>
82415
+ ${environmentEntries}
82416
+ </dict>
82417
+ ` : "";
82418
+ const manifest = `<?xml version="1.0" encoding="UTF-8"?>
82419
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
82420
+ <plist version="1.0">
82421
+ <dict>
82422
+ <key>Label</key>
82423
+ <string>${names.launchd}</string>
82424
+ <key>ProgramArguments</key>
82425
+ <array>
82426
+ ${programArguments}
82427
+ </array>
82428
+ ${environmentBlock} <key>RunAtLoad</key>
82429
+ <true/>
82430
+ <key>KeepAlive</key>
82431
+ <dict>
82432
+ <key>SuccessfulExit</key>
82433
+ <false/>
82434
+ </dict>
82435
+ <key>ThrottleInterval</key>
82436
+ <integer>5</integer>
82437
+ <key>StandardOutPath</key>
82438
+ <string>${xml((0, import_node_path27.join)(logDir, "daemon.log"))}</string>
82439
+ <key>StandardErrorPath</key>
82440
+ <string>${xml((0, import_node_path27.join)(logDir, "daemon-error.log"))}</string>
82441
+ </dict>
82442
+ </plist>
82443
+ `;
82444
+ return {
82445
+ platform: "darwin",
82446
+ manifestPath,
82447
+ manifest,
82448
+ logDirectory: logDir,
82449
+ loadDefinitionCommands: [
82450
+ {
82451
+ command: "launchctl",
82452
+ args: ["bootstrap", domain2, manifestPath],
82453
+ tolerateFailure: true
82454
+ }
82455
+ ],
82456
+ reloadDefinitionCommands: [
82457
+ {
82458
+ command: "launchctl",
82459
+ args: ["bootout", domain2, manifestPath],
82460
+ tolerateFailure: true
82461
+ },
82462
+ {
82463
+ command: "launchctl",
82464
+ args: ["bootstrap", domain2, manifestPath],
82465
+ tolerateFailure: false
82466
+ }
82467
+ ],
82468
+ enableCommands: [
82469
+ {
82470
+ command: "launchctl",
82471
+ args: ["enable", serviceTarget],
82472
+ tolerateFailure: true
82473
+ }
82474
+ ],
82475
+ startCommands: [
82476
+ {
82477
+ command: "launchctl",
82478
+ args: ["kickstart", serviceTarget],
82479
+ tolerateFailure: false
82480
+ }
82481
+ ],
82482
+ stopCommands: [
82483
+ {
82484
+ command: "launchctl",
82485
+ args: ["kill", "SIGTERM", serviceTarget],
82486
+ tolerateFailure: true
82487
+ }
82488
+ ],
82489
+ replaceCommands: [
82490
+ {
82491
+ command: "launchctl",
82492
+ args: ["bootout", domain2, manifestPath],
82493
+ tolerateFailure: true
82494
+ },
82495
+ {
82496
+ command: "launchctl",
82497
+ args: ["bootstrap", domain2, manifestPath],
82498
+ tolerateFailure: false
82499
+ },
82500
+ {
82501
+ command: "launchctl",
82502
+ args: ["kickstart", "-k", serviceTarget],
82503
+ tolerateFailure: false
82504
+ }
82505
+ ],
82506
+ uninstallCommands: [
82507
+ {
82508
+ command: "launchctl",
82509
+ args: ["bootout", domain2, manifestPath],
82510
+ tolerateFailure: true
82511
+ }
82512
+ ],
82513
+ statusCommand: {
82514
+ command: "launchctl",
82515
+ args: ["print", serviceTarget],
82516
+ tolerateFailure: true
82517
+ },
82518
+ isActiveCommand: {
82519
+ command: "launchctl",
82520
+ args: ["print", serviceTarget],
82521
+ tolerateFailure: true
82522
+ },
82523
+ lingerStatusCommand: null
82524
+ };
82252
82525
  }
82253
- return to;
82254
- };
82255
- var __toESM3 = (mod, isNodeMode, target) => (target = mod != null ? __create3(__getProtoOf3(mod)) : {}, __copyProps3(isNodeMode || !mod || !mod.__esModule ? __defProp3(target, "default", {
82256
- value: mod,
82257
- enumerable: true
82258
- }) : target, mod));
82526
+ if (input2.platform === "linux") {
82527
+ const manifestPath = (0, import_node_path27.join)(input2.homeDir, ".config", "systemd", "user", names.systemd);
82528
+ const environmentLines = Object.entries(input2.environment ?? {}).map(([key, value2]) => `Environment=${systemdArg(`${key}=${value2}`)}`).join("\n");
82529
+ const manifest = `[Unit]
82530
+ Description=Alan local agent daemon
82531
+ After=network-online.target
82532
+ Wants=network-online.target
82533
+ StartLimitIntervalSec=300
82534
+ StartLimitBurst=3
82259
82535
 
82260
- // ../../node_modules/@modelcontextprotocol/server/dist/dialects-DoSzNhcb.mjs
82261
- var DRAFT_2020_12_URIS2 = /* @__PURE__ */ new Set(["https://json-schema.org/draft/2020-12/schema", "http://json-schema.org/draft/2020-12/schema"]);
82262
- var DRAFT_2019_09_URIS2 = /* @__PURE__ */ new Set(["https://json-schema.org/draft/2019-09/schema", "http://json-schema.org/draft/2019-09/schema"]);
82263
- var DRAFT_07_URIS2 = /* @__PURE__ */ new Set(["https://json-schema.org/draft-07/schema", "http://json-schema.org/draft-07/schema"]);
82264
- var DRAFT_06_URIS2 = /* @__PURE__ */ new Set(["https://json-schema.org/draft-06/schema", "http://json-schema.org/draft-06/schema"]);
82265
- function declares2019Dialect2($schema) {
82266
- return typeof $schema === "string" && DRAFT_2019_09_URIS2.has($schema.replace(/#$/, ""));
82267
- }
82268
- function declaredDialect2(schema, remedy) {
82269
- if (!("$schema" in schema) || typeof schema.$schema !== "string") return "2020-12";
82270
- const declared = schema.$schema.replace(/#$/, "");
82271
- if (DRAFT_2020_12_URIS2.has(declared)) return "2020-12";
82272
- if (DRAFT_2019_09_URIS2.has(declared)) return "2019-09";
82273
- if (DRAFT_07_URIS2.has(declared) || DRAFT_06_URIS2.has(declared)) return "draft-7";
82274
- throw new Error(`JSON Schema declares an unsupported dialect ("$schema": "${schema.$schema.slice(0, 200)}"). The default validator supports JSON Schema 2020-12, 2019-09, draft-07, and draft-06; ${remedy}`);
82275
- }
82536
+ [Service]
82537
+ Type=simple
82538
+ ${environmentLines ? `${environmentLines}
82539
+ ` : ""}ExecStart=${daemonArgs.map(systemdArg).join(" ")}
82540
+ Restart=on-failure
82541
+ RestartSec=5
82276
82542
 
82277
- // ../../node_modules/@modelcontextprotocol/server/dist/src-CX2iR2pK.mjs
82278
- var BRANDS2 = /* @__PURE__ */ Symbol.for("mcp.sdk.errorBrands");
82279
- function stampErrorBrands2(instance, ctor) {
82280
- const brands = /* @__PURE__ */ new Set();
82281
- let current = ctor;
82282
- while (typeof current === "function") {
82283
- const brand = current.mcpBrand;
82284
- if (Object.prototype.hasOwnProperty.call(current, "mcpBrand") && typeof brand === "string") brands.add(brand);
82285
- current = Object.getPrototypeOf(current);
82286
- }
82287
- if (brands.size === 0) return;
82288
- Object.defineProperty(instance, BRANDS2, {
82289
- value: brands,
82290
- enumerable: false,
82291
- configurable: true
82292
- });
82293
- }
82294
- function brandedHasInstance2(cls, value2) {
82295
- try {
82296
- if (typeof value2 === "object" && value2 !== null && Object.prototype.hasOwnProperty.call(cls, "mcpBrand") && typeof cls.mcpBrand === "string" && Object.prototype.hasOwnProperty.call(value2, BRANDS2)) {
82297
- const carried = value2[BRANDS2];
82298
- if (carried && typeof carried.has === "function" && carried.has(cls.mcpBrand)) return true;
82299
- }
82300
- } catch {
82301
- }
82302
- return Function.prototype[Symbol.hasInstance].call(cls, value2);
82303
- }
82304
- var OAuthError3 = class OAuthError4 extends Error {
82305
- static {
82306
- Object.defineProperty(this, "mcpBrand", { value: "mcp.OAuthError" });
82307
- }
82308
- static [Symbol.hasInstance](value2) {
82309
- return brandedHasInstance2(this, value2);
82310
- }
82311
- /**
82312
- * Brand-based type guard: equivalent to `value instanceof this`, as an
82313
- * explicit static predicate (the axios/AWS-SDK `isInstance` style). Reads
82314
- * the caller's own brand via `this`, so every branded subclass gets a
82315
- * correctly-scoped guard by inheritance. Must be invoked on the class —
82316
- * in callback position write `v => SdkError.isInstance(v)`, not
82317
- * `.filter(SdkError.isInstance)` (detached calls throw rather than
82318
- * silently matching nothing).
82319
- */
82320
- static isInstance(value2) {
82321
- if (typeof this !== "function") throw new TypeError("isInstance must be called on the class (e.g. `SdkError.isInstance(value)`); for callbacks use `v => SdkError.isInstance(v)`");
82322
- return brandedHasInstance2(this, value2);
82543
+ [Install]
82544
+ WantedBy=default.target
82545
+ `;
82546
+ return {
82547
+ platform: "linux",
82548
+ manifestPath,
82549
+ manifest,
82550
+ logDirectory: null,
82551
+ loadDefinitionCommands: [
82552
+ {
82553
+ command: "systemctl",
82554
+ args: ["--user", "daemon-reload"],
82555
+ tolerateFailure: false
82556
+ }
82557
+ ],
82558
+ reloadDefinitionCommands: [
82559
+ {
82560
+ command: "systemctl",
82561
+ args: ["--user", "daemon-reload"],
82562
+ tolerateFailure: false
82563
+ }
82564
+ ],
82565
+ enableCommands: [
82566
+ {
82567
+ command: "systemctl",
82568
+ args: ["--user", "enable", names.systemd],
82569
+ tolerateFailure: false
82570
+ }
82571
+ ],
82572
+ startCommands: [
82573
+ {
82574
+ command: "systemctl",
82575
+ args: ["--user", "start", names.systemd],
82576
+ tolerateFailure: false
82577
+ }
82578
+ ],
82579
+ stopCommands: [
82580
+ {
82581
+ command: "systemctl",
82582
+ args: ["--user", "stop", names.systemd],
82583
+ tolerateFailure: true
82584
+ }
82585
+ ],
82586
+ replaceCommands: [
82587
+ {
82588
+ command: "systemctl",
82589
+ args: ["--user", "daemon-reload"],
82590
+ tolerateFailure: false
82591
+ },
82592
+ {
82593
+ command: "systemctl",
82594
+ args: ["--user", "restart", names.systemd],
82595
+ tolerateFailure: false
82596
+ }
82597
+ ],
82598
+ uninstallCommands: [
82599
+ {
82600
+ command: "systemctl",
82601
+ args: ["--user", "disable", "--now", names.systemd],
82602
+ tolerateFailure: true
82603
+ },
82604
+ {
82605
+ command: "systemctl",
82606
+ args: ["--user", "daemon-reload"],
82607
+ tolerateFailure: false
82608
+ }
82609
+ ],
82610
+ statusCommand: {
82611
+ command: "systemctl",
82612
+ args: ["--user", "status", names.systemd, "--no-pager"],
82613
+ tolerateFailure: true
82614
+ },
82615
+ isActiveCommand: {
82616
+ command: "systemctl",
82617
+ args: ["--user", "is-active", names.systemd],
82618
+ tolerateFailure: true
82619
+ },
82620
+ lingerStatusCommand: {
82621
+ command: "loginctl",
82622
+ args: ["show-user", "$(id -un)", "-p", "Linger"],
82623
+ tolerateFailure: true
82624
+ }
82625
+ };
82323
82626
  }
82324
- constructor(code, message, errorUri) {
82325
- super(message);
82326
- this.code = code;
82327
- this.errorUri = errorUri;
82328
- this.name = "OAuthError";
82329
- stampErrorBrands2(this, new.target);
82627
+ const environmentPrefix = Object.entries(input2.environment ?? {}).map(([key, value2]) => `$env:${key} = ${powershellLiteral(value2)}`).join("; ");
82628
+ const directArgument = [input2.cliEntry, "daemon", "--profile", input2.profile].map((part) => part.includes(" ") ? `"${part}"` : part).join(" ");
82629
+ const executable = powershellLiteral(environmentPrefix ? "powershell.exe" : input2.nodeExecutable);
82630
+ const argument = powershellLiteral(
82631
+ environmentPrefix ? `-NoProfile -NonInteractive -WindowStyle Hidden -Command "${environmentPrefix}; & ${powershellLiteral(input2.nodeExecutable)} ${powershellLiteral(input2.cliEntry)} daemon --profile ${input2.profile}"` : directArgument
82632
+ );
82633
+ const registerScript = [
82634
+ "$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name",
82635
+ `$action = New-ScheduledTaskAction -Execute ${executable} -Argument ${argument}`,
82636
+ "$trigger = New-ScheduledTaskTrigger -AtLogOn -User $currentUser",
82637
+ "$principal = New-ScheduledTaskPrincipal -UserId $currentUser -LogonType Interactive -RunLevel Limited",
82638
+ "$settings = New-ScheduledTaskSettingsSet -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit ([TimeSpan]::Zero)",
82639
+ `Register-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null`
82640
+ ].join("; ");
82641
+ return {
82642
+ platform: "win32",
82643
+ manifestPath: null,
82644
+ manifest: null,
82645
+ logDirectory: null,
82646
+ loadDefinitionCommands: [
82647
+ {
82648
+ command: "powershell.exe",
82649
+ args: ["-NoProfile", "-NonInteractive", "-Command", registerScript],
82650
+ tolerateFailure: false
82651
+ }
82652
+ ],
82653
+ reloadDefinitionCommands: [
82654
+ {
82655
+ command: "powershell.exe",
82656
+ args: ["-NoProfile", "-NonInteractive", "-Command", registerScript],
82657
+ tolerateFailure: false
82658
+ }
82659
+ ],
82660
+ enableCommands: [],
82661
+ startCommands: [
82662
+ {
82663
+ command: "powershell.exe",
82664
+ args: [
82665
+ "-NoProfile",
82666
+ "-NonInteractive",
82667
+ "-Command",
82668
+ `Start-ScheduledTask -TaskName ${powershellLiteral(names.windows)}`
82669
+ ],
82670
+ tolerateFailure: false
82671
+ }
82672
+ ],
82673
+ stopCommands: [
82674
+ {
82675
+ command: "powershell.exe",
82676
+ args: [
82677
+ "-NoProfile",
82678
+ "-NonInteractive",
82679
+ "-Command",
82680
+ `Stop-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -ErrorAction SilentlyContinue`
82681
+ ],
82682
+ tolerateFailure: true
82683
+ }
82684
+ ],
82685
+ replaceCommands: [
82686
+ {
82687
+ command: "powershell.exe",
82688
+ args: [
82689
+ "-NoProfile",
82690
+ "-NonInteractive",
82691
+ "-Command",
82692
+ `${registerScript}; Start-ScheduledTask -TaskName ${powershellLiteral(names.windows)}`
82693
+ ],
82694
+ tolerateFailure: false
82695
+ }
82696
+ ],
82697
+ uninstallCommands: [
82698
+ {
82699
+ command: "powershell.exe",
82700
+ args: [
82701
+ "-NoProfile",
82702
+ "-NonInteractive",
82703
+ "-Command",
82704
+ `Unregister-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -Confirm:$false -ErrorAction SilentlyContinue`
82705
+ ],
82706
+ tolerateFailure: true
82707
+ }
82708
+ ],
82709
+ statusCommand: {
82710
+ command: "powershell.exe",
82711
+ args: [
82712
+ "-NoProfile",
82713
+ "-NonInteractive",
82714
+ "-Command",
82715
+ `Get-ScheduledTask -TaskName ${powershellLiteral(names.windows)} | Format-List TaskName,State`
82716
+ ],
82717
+ tolerateFailure: true
82718
+ },
82719
+ isActiveCommand: {
82720
+ command: "powershell.exe",
82721
+ args: [
82722
+ "-NoProfile",
82723
+ "-NonInteractive",
82724
+ "-Command",
82725
+ `(Get-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -ErrorAction SilentlyContinue).State`
82726
+ ],
82727
+ tolerateFailure: true
82728
+ },
82729
+ lingerStatusCommand: null
82730
+ };
82731
+ }
82732
+ function resolveCurrentPlatform() {
82733
+ const currentPlatform = (0, import_node_os16.platform)();
82734
+ if (currentPlatform !== "darwin" && currentPlatform !== "linux" && currentPlatform !== "win32") {
82735
+ throw new Error(`Daemon service installation is not supported on ${currentPlatform}`);
82330
82736
  }
82331
- /**
82332
- * Converts the error to a standard OAuth error response object.
82333
- */
82334
- toResponseObject() {
82335
- const response = {
82336
- error: this.code,
82337
- error_description: this.message
82338
- };
82339
- if (this.errorUri) response.error_uri = this.errorUri;
82340
- return response;
82737
+ return currentPlatform;
82738
+ }
82739
+ function currentServicePlan(args, runtime) {
82740
+ const currentPlatform = resolveCurrentPlatform();
82741
+ const profile = resolveEndpointProfileFromArgs(args);
82742
+ const cliEntry = runtime?.cliEntry ?? process.argv[1];
82743
+ if (!cliEntry) throw new Error("Cannot determine the alan-agent executable path");
82744
+ return buildDaemonServicePlan({
82745
+ platform: currentPlatform,
82746
+ homeDir: (0, import_node_os16.homedir)(),
82747
+ nodeExecutable: runtime?.executable ?? process.execPath,
82748
+ cliEntry: (0, import_node_path27.resolve)(cliEntry),
82749
+ profile,
82750
+ environment: runtime?.environment
82751
+ });
82752
+ }
82753
+ function writeManifestAtomic(path2, contents) {
82754
+ (0, import_node_fs25.mkdirSync)((0, import_node_path27.dirname)(path2), { recursive: true, mode: 448 });
82755
+ const pendingPath = `${path2}.pending-${process.pid}`;
82756
+ try {
82757
+ (0, import_node_fs25.writeFileSync)(pendingPath, contents, { mode: 384 });
82758
+ (0, import_node_fs25.chmodSync)(pendingPath, 384);
82759
+ (0, import_node_fs25.renameSync)(pendingPath, path2);
82760
+ } finally {
82761
+ (0, import_node_fs25.rmSync)(pendingPath, { force: true });
82341
82762
  }
82342
- /**
82343
- * Creates an {@linkcode OAuthError} from an OAuth error response.
82344
- */
82345
- static fromResponse(response) {
82346
- return new OAuthError4(response.error, response.error_description ?? response.error, response.error_uri);
82763
+ }
82764
+ function defaultReadManifest(path2) {
82765
+ if (!(0, import_node_fs25.existsSync)(path2)) return null;
82766
+ return (0, import_node_fs25.readFileSync)(path2, "utf8");
82767
+ }
82768
+ function runServiceCommand(command) {
82769
+ const result = (0, import_node_child_process9.spawnSync)(command.command, command.args, { encoding: "utf8", shell: false });
82770
+ const status = result.status ?? 1;
82771
+ const output2 = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
82772
+ if (status !== 0 && !command.tolerateFailure) {
82773
+ throw new Error(
82774
+ `Service command failed (${command.command} ${command.args.join(" ")}): ${output2 || `exit ${status}`}`
82775
+ );
82347
82776
  }
82348
- };
82349
- var SdkErrorCode2 = /* @__PURE__ */ (function(SdkErrorCode$1) {
82350
- SdkErrorCode$1["NotConnected"] = "NOT_CONNECTED";
82351
- SdkErrorCode$1["AlreadyConnected"] = "ALREADY_CONNECTED";
82352
- SdkErrorCode$1["NotInitialized"] = "NOT_INITIALIZED";
82353
- SdkErrorCode$1["CapabilityNotSupported"] = "CAPABILITY_NOT_SUPPORTED";
82354
- SdkErrorCode$1["RequestTimeout"] = "REQUEST_TIMEOUT";
82355
- SdkErrorCode$1["ConnectionClosed"] = "CONNECTION_CLOSED";
82356
- SdkErrorCode$1["SendFailed"] = "SEND_FAILED";
82357
- SdkErrorCode$1["InvalidResult"] = "INVALID_RESULT";
82358
- SdkErrorCode$1["UnsupportedResultType"] = "UNSUPPORTED_RESULT_TYPE";
82359
- SdkErrorCode$1["InputRequiredRoundsExceeded"] = "INPUT_REQUIRED_ROUNDS_EXCEEDED";
82360
- SdkErrorCode$1["ListPaginationExceeded"] = "LIST_PAGINATION_EXCEEDED";
82361
- SdkErrorCode$1["MethodNotSupportedByProtocolVersion"] = "METHOD_NOT_SUPPORTED_BY_PROTOCOL_VERSION";
82362
- SdkErrorCode$1["EraNegotiationFailed"] = "ERA_NEGOTIATION_FAILED";
82363
- SdkErrorCode$1["ClientHttpNotImplemented"] = "CLIENT_HTTP_NOT_IMPLEMENTED";
82364
- SdkErrorCode$1["ClientHttpAuthentication"] = "CLIENT_HTTP_AUTHENTICATION";
82365
- SdkErrorCode$1["ClientHttpForbidden"] = "CLIENT_HTTP_FORBIDDEN";
82366
- SdkErrorCode$1["ClientHttpUnexpectedContent"] = "CLIENT_HTTP_UNEXPECTED_CONTENT";
82367
- SdkErrorCode$1["ClientHttpFailedToOpenStream"] = "CLIENT_HTTP_FAILED_TO_OPEN_STREAM";
82368
- SdkErrorCode$1["ClientHttpFailedToTerminateSession"] = "CLIENT_HTTP_FAILED_TO_TERMINATE_SESSION";
82369
- return SdkErrorCode$1;
82370
- })({});
82371
- var SdkError2 = class extends Error {
82372
- static {
82373
- Object.defineProperty(this, "mcpBrand", { value: "mcp.SdkError" });
82777
+ return { status, output: output2 };
82778
+ }
82779
+ function defaultSleep(ms) {
82780
+ return new Promise((resolveSleep) => {
82781
+ setTimeout(resolveSleep, ms);
82782
+ });
82783
+ }
82784
+ function parseServiceHostState(servicePlatform, result) {
82785
+ const output2 = result.output;
82786
+ if (servicePlatform === "linux") {
82787
+ const trimmed = output2.trim().toLowerCase();
82788
+ if (trimmed === "inactive" || trimmed === "failed" || trimmed === "dead" || /\binactive\b/i.test(output2) || /\bfailed\b/i.test(output2)) {
82789
+ return "stopped";
82790
+ }
82374
82791
  }
82375
- static [Symbol.hasInstance](value2) {
82376
- return brandedHasInstance2(this, value2);
82792
+ if (result.status !== 0) return "unknown";
82793
+ if (servicePlatform === "darwin") {
82794
+ if (/\bstate\s*=\s*running\b/i.test(output2)) return "running";
82795
+ if (/\bstate\s*=\s*(not running|waiting|stopped)\b/i.test(output2)) return "stopped";
82796
+ if (/\bpid\s*=\s*[1-9]\d*\b/i.test(output2)) return "running";
82797
+ if (/could not find service/i.test(output2)) return "stopped";
82798
+ return "unknown";
82377
82799
  }
82378
- /**
82379
- * Brand-based type guard: equivalent to `value instanceof this`, as an
82380
- * explicit static predicate (the axios/AWS-SDK `isInstance` style). Reads
82381
- * the caller's own brand via `this`, so every branded subclass gets a
82382
- * correctly-scoped guard by inheritance. Must be invoked on the class —
82383
- * in callback position write `v => SdkError.isInstance(v)`, not
82384
- * `.filter(SdkError.isInstance)` (detached calls throw rather than
82385
- * silently matching nothing).
82386
- */
82387
- static isInstance(value2) {
82388
- if (typeof this !== "function") throw new TypeError("isInstance must be called on the class (e.g. `SdkError.isInstance(value)`); for callbacks use `v => SdkError.isInstance(v)`");
82389
- return brandedHasInstance2(this, value2);
82800
+ if (servicePlatform === "linux") {
82801
+ const trimmed = output2.trim().toLowerCase();
82802
+ if (trimmed === "active" || /\bactive\s*\(running\)/i.test(output2)) return "running";
82803
+ if (trimmed === "activating" || trimmed === "reloading") return "starting";
82804
+ return "unknown";
82390
82805
  }
82391
- constructor(code, message, data) {
82392
- super(message);
82393
- this.code = code;
82394
- this.data = data;
82395
- this.name = "SdkError";
82396
- stampErrorBrands2(this, new.target);
82806
+ const stateMatch = output2.match(/\b(?:State|state)\s*[:=]\s*(\w+)/);
82807
+ const state = (stateMatch?.[1] ?? output2.trim()).toLowerCase();
82808
+ if (state === "running") return "running";
82809
+ if (state === "ready" || state === "disabled" || state === "queued") return "stopped";
82810
+ return "unknown";
82811
+ }
82812
+ function resolveEnsureDefinitionOutcome(input2) {
82813
+ if (!input2.definitionChanged) {
82814
+ return { changed: false, updatePending: false };
82397
82815
  }
82398
- };
82399
- var SdkHttpError2 = class extends SdkError2 {
82400
- static {
82401
- Object.defineProperty(this, "mcpBrand", { value: "mcp.SdkHttpError" });
82816
+ if (input2.hostState === "running" || input2.hostState === "starting") {
82817
+ return { changed: true, updatePending: true };
82402
82818
  }
82403
- constructor(code, message, data) {
82404
- super(code, message, data);
82405
- this.name = "SdkHttpError";
82819
+ return { changed: true, updatePending: false };
82820
+ }
82821
+ function shouldStartOnInstall(hostState) {
82822
+ return hostState === "stopped";
82823
+ }
82824
+ function resolveUsername(options) {
82825
+ if (options?.username) return options.username;
82826
+ try {
82827
+ return (0, import_node_os16.userInfo)().username;
82828
+ } catch {
82829
+ return process.env.USER || process.env.USERNAME || "unknown";
82406
82830
  }
82407
- get status() {
82408
- return this.data.status;
82831
+ }
82832
+ function buildLingerStatusCommand(username) {
82833
+ return {
82834
+ command: "loginctl",
82835
+ args: ["show-user", username, "-p", "Linger"],
82836
+ tolerateFailure: true
82837
+ };
82838
+ }
82839
+ function assertSystemdUserLingerEnabled(options = {}) {
82840
+ const username = resolveUsername(options);
82841
+ const run = options.runCommand ?? runServiceCommand;
82842
+ const result = run(buildLingerStatusCommand(username));
82843
+ if (result.status !== 0) {
82844
+ throw new Error(
82845
+ `Cannot verify systemd user linger for ${username} (loginctl failed). For durable headless VM boot run: sudo loginctl enable-linger ${username}`
82846
+ );
82409
82847
  }
82410
- get statusText() {
82411
- return this.data.statusText;
82848
+ if (!/Linger=yes/i.test(result.output)) {
82849
+ throw new Error(
82850
+ `systemd user linger is disabled for ${username}. For durable headless VM boot run: sudo loginctl enable-linger ${username}`
82851
+ );
82412
82852
  }
82413
- };
82414
- function isPlainObject$7(value2) {
82415
- return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
82416
82853
  }
82417
- function isImpliedCapabilityMember(capability, member, declaredValue) {
82418
- return capability === "elicitation" && member === "form" && declaredValue["form"] === void 0 && declaredValue["url"] === void 0;
82854
+ function createHostContext(options = {}) {
82855
+ const args = options.args ?? [];
82856
+ const plan = currentServicePlan(args, options.runtime);
82857
+ const run = options.runCommand ?? runServiceCommand;
82858
+ const sleep4 = options.sleep ?? defaultSleep;
82859
+ const nowMs = options.nowMs ?? Date.now;
82860
+ const readManifest = options.readManifest ?? defaultReadManifest;
82861
+ const writeManifest = options.writeManifest ?? writeManifestAtomic;
82862
+ const removeManifest = options.removeManifest ?? ((path2) => (0, import_node_fs25.rmSync)(path2, { force: true }));
82863
+ const ensureLogDir = options.ensureLogDir ?? (() => {
82864
+ if (plan.logDirectory) (0, import_node_fs25.mkdirSync)(plan.logDirectory, { recursive: true, mode: 448 });
82865
+ });
82866
+ return {
82867
+ args,
82868
+ plan,
82869
+ run,
82870
+ sleep: sleep4,
82871
+ nowMs,
82872
+ readManifest,
82873
+ writeManifest,
82874
+ removeManifest,
82875
+ ensureLogDir,
82876
+ profile: resolveEndpointProfileFromArgs(args)
82877
+ };
82419
82878
  }
82420
- function requiredClientCapabilitiesForInputRequest(entry) {
82421
- switch (entry.method) {
82422
- case "elicitation/create":
82423
- if (entry.params?.["mode"] === "url") return { elicitation: { url: {} } };
82424
- return { elicitation: { form: {} } };
82425
- case "sampling/createMessage": {
82426
- const params = entry.params;
82427
- if (params !== void 0 && (params["tools"] !== void 0 || params["toolChoice"] !== void 0)) return { sampling: { tools: {} } };
82428
- return { sampling: {} };
82879
+ function inspectDaemonService(options = {}) {
82880
+ const { plan, run, nowMs } = createHostContext(options);
82881
+ const result = run(plan.isActiveCommand);
82882
+ const state = parseServiceHostState(plan.platform, result);
82883
+ return {
82884
+ state,
82885
+ platform: plan.platform,
82886
+ detail: result.output || void 0,
82887
+ observedAtMs: nowMs()
82888
+ };
82889
+ }
82890
+ function ensureDaemonServiceDefinition(options = {}) {
82891
+ const ctx = createHostContext(options);
82892
+ const { plan, run, readManifest, writeManifest, ensureLogDir } = ctx;
82893
+ let definitionChanged = false;
82894
+ if (plan.manifestPath && plan.manifest) {
82895
+ ensureLogDir();
82896
+ const existing = readManifest(plan.manifestPath);
82897
+ definitionChanged = existing !== plan.manifest;
82898
+ if (definitionChanged) {
82899
+ writeManifest(plan.manifestPath, plan.manifest);
82429
82900
  }
82430
- case "roots/list":
82431
- return { roots: {} };
82432
- default:
82433
- return;
82901
+ } else if (plan.platform === "win32") {
82902
+ definitionChanged = true;
82903
+ }
82904
+ const inspection = inspectDaemonService(options);
82905
+ const outcome = resolveEnsureDefinitionOutcome({
82906
+ definitionChanged,
82907
+ hostState: inspection.state
82908
+ });
82909
+ if (outcome.updatePending) {
82910
+ return outcome;
82911
+ }
82912
+ if (inspection.state === "running" || inspection.state === "starting") {
82913
+ return outcome;
82434
82914
  }
82915
+ const definitionCommands = inspection.state === "stopped" ? plan.reloadDefinitionCommands : plan.loadDefinitionCommands;
82916
+ for (const command of definitionCommands) run(command);
82917
+ return outcome;
82435
82918
  }
82436
- function missingClientCapabilities(required2, declared) {
82437
- const missing = {};
82438
- for (const [capability, requirement] of Object.entries(required2)) {
82439
- if (requirement === void 0) continue;
82440
- const declaredValue = declared === void 0 ? void 0 : declared[capability];
82441
- if (declaredValue === void 0) {
82442
- missing[capability] = requirement;
82443
- continue;
82444
- }
82445
- if (isPlainObject$7(requirement) && isPlainObject$7(declaredValue)) {
82446
- const missingMembers = {};
82447
- for (const [member, memberRequirement] of Object.entries(requirement)) if (memberRequirement !== void 0 && declaredValue[member] === void 0 && !isImpliedCapabilityMember(capability, member, declaredValue)) missingMembers[member] = memberRequirement;
82448
- if (Object.keys(missingMembers).length > 0) missing[capability] = missingMembers;
82919
+ function enableDaemonService(options = {}) {
82920
+ const { plan, run } = createHostContext(options);
82921
+ for (const command of plan.enableCommands) run(command);
82922
+ }
82923
+ function startDaemonService(options = {}) {
82924
+ const { plan, run } = createHostContext(options);
82925
+ for (const command of plan.startCommands) run(command);
82926
+ }
82927
+ function uninstallDaemonService(args = [], options = {}) {
82928
+ const ctx = createHostContext({ ...options, args: options.args ?? args });
82929
+ for (const command of ctx.plan.uninstallCommands) ctx.run(command);
82930
+ if (ctx.plan.manifestPath) ctx.removeManifest(ctx.plan.manifestPath);
82931
+ console.info("[alan-agent] Per-user daemon service removed");
82932
+ }
82933
+ function installDaemonService(args = [], runtime, options = {}) {
82934
+ const profile = resolveEndpointProfileFromArgs(args);
82935
+ const configPath = resolveAgentConfigPath({ profile });
82936
+ if (!configExistsAtPath(configPath)) {
82937
+ throw new Error(`No ${profile} runtime is configured. Run alan-agent setup or login first.`);
82938
+ }
82939
+ const headless = options.headless ?? args.includes("--headless");
82940
+ const merged = { ...options, args, runtime, headless };
82941
+ const ctx = createHostContext(merged);
82942
+ if (headless) {
82943
+ if (ctx.plan.platform !== "linux") {
82944
+ throw new Error("--headless is only supported on Linux systemd user services");
82449
82945
  }
82946
+ assertSystemdUserLingerEnabled(merged);
82450
82947
  }
82451
- return Object.keys(missing).length > 0 ? missing : void 0;
82452
- }
82453
- var FIRST_MODERN_PROTOCOL_VERSION2 = "2026-07-28";
82454
- function isModernProtocolVersion2(version2) {
82455
- return version2 >= FIRST_MODERN_PROTOCOL_VERSION2;
82948
+ const ensured = ensureDaemonServiceDefinition(merged);
82949
+ enableDaemonService(merged);
82950
+ const inspection = inspectDaemonService(merged);
82951
+ if (ensured.updatePending) {
82952
+ console.info("[alan-agent] Daemon service definition update pending; leaving running service", {
82953
+ profile,
82954
+ state: inspection.state
82955
+ });
82956
+ return;
82957
+ }
82958
+ if (!shouldStartOnInstall(inspection.state)) {
82959
+ console.info("[alan-agent] Per-user daemon service already present", {
82960
+ profile,
82961
+ state: inspection.state
82962
+ });
82963
+ return;
82964
+ }
82965
+ startDaemonService(merged);
82966
+ console.info("[alan-agent] Per-user daemon service installed", { profile });
82456
82967
  }
82457
- function legacyProtocolVersions2(versions) {
82458
- return versions.filter((version2) => !isModernProtocolVersion2(version2));
82968
+ function printDaemonServiceStatus(args = []) {
82969
+ const plan = currentServicePlan(args);
82970
+ const result = runServiceCommand({ ...plan.statusCommand, tolerateFailure: true });
82971
+ if (result.status !== 0) {
82972
+ process.stdout.write(`unknown
82973
+ ${result.output}
82974
+ `);
82975
+ return;
82976
+ }
82977
+ process.stdout.write(`${result.output}
82978
+ `);
82459
82979
  }
82460
- function modernProtocolVersions2(versions) {
82461
- return versions.filter((version2) => isModernProtocolVersion2(version2));
82980
+
82981
+ // src/install-command.ts
82982
+ async function runInstallCommand(commandArgs) {
82983
+ bindConfigPath(commandArgs);
82984
+ const configured = readConfig();
82985
+ const configurationMode = resolveInstallConfigurationMode(commandArgs, configured);
82986
+ if (configurationMode === "setup") await setupDaemon(commandArgs);
82987
+ if (configurationMode === "login") await loginWithDeviceCode(commandArgs);
82988
+ installDaemonService(commandArgs);
82989
+ runMcpIntegrationCommand(["reconcile", ...commandArgs]);
82462
82990
  }
82463
- function appendTextFallbackForNonObject2(result) {
82464
- const sc = result.structuredContent;
82465
- if (sc === void 0) return result;
82466
- if (!(typeof sc !== "object" || sc === null || Array.isArray(sc))) return result;
82467
- if (result.content?.some((c) => c.type === "text") ?? false) return result;
82468
- return {
82469
- ...result,
82470
- content: [...result.content ?? [], {
82471
- type: "text",
82472
- text: JSON.stringify(sc)
82473
- }]
82474
- };
82991
+
82992
+ // src/lifecycle-logger.ts
82993
+ var AgentLifecycleLogger = class _AgentLifecycleLogger {
82994
+ context;
82995
+ constructor(context = {}) {
82996
+ this.context = normalizeContext(context);
82997
+ }
82998
+ setContext(context) {
82999
+ this.context = normalizeContext({ ...this.context, ...context });
83000
+ }
83001
+ child(context) {
83002
+ return new _AgentLifecycleLogger({ ...this.context, ...context });
83003
+ }
83004
+ debug(event, fields = {}) {
83005
+ this.log("debug", event, fields);
83006
+ }
83007
+ info(event, fields = {}) {
83008
+ this.log("info", event, fields);
83009
+ }
83010
+ warn(event, fields = {}) {
83011
+ this.log("warn", event, fields);
83012
+ }
83013
+ error(event, fields = {}) {
83014
+ this.log("error", event, fields);
83015
+ }
83016
+ log(level, event, fields) {
83017
+ const record2 = omitUndefined({
83018
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
83019
+ level,
83020
+ component: "agent-manager",
83021
+ event,
83022
+ ...this.context,
83023
+ ...normalizeFields(fields)
83024
+ });
83025
+ console[level](JSON.stringify(record2));
83026
+ }
83027
+ };
83028
+ function lifecycleContextFromEnv(env) {
83029
+ return normalizeContext({
83030
+ taskId: env.ALAN_TASK_ID,
83031
+ conversationId: env.ALAN_SESSION_ID,
83032
+ runId: env.ALAN_RUN_ID,
83033
+ sandboxId: env.ALAN_SANDBOX_ID,
83034
+ provider: env.ALAN_SANDBOX_PROVIDER,
83035
+ backendKind: env.ALAN_BACKEND_KIND,
83036
+ agentVersion: AGENT_VERSION
83037
+ });
82475
83038
  }
82476
- var TOOL_RESULT_FOREIGN_FAMILY_KEYS2 = [
82477
- "task",
82478
- "inputRequests",
82479
- "requestState"
82480
- ];
82481
- function normalizeContentlessToolResult2(value2) {
82482
- if (value2 === null || typeof value2 !== "object" || Array.isArray(value2) || value2.content !== void 0 || TOOL_RESULT_FOREIGN_FAMILY_KEYS2.some((key) => key in value2)) return value2;
82483
- return {
82484
- ...value2,
82485
- content: []
82486
- };
83039
+ function normalizeContext(context) {
83040
+ return omitUndefined({
83041
+ ...context,
83042
+ agentVersion: context.agentVersion ?? AGENT_VERSION
83043
+ });
83044
+ }
83045
+ function normalizeFields(fields) {
83046
+ const normalized = {};
83047
+ for (const [key, value2] of Object.entries(fields)) {
83048
+ if (value2 instanceof Error) {
83049
+ normalized[key] = {
83050
+ name: value2.name,
83051
+ message: value2.message,
83052
+ stack: value2.stack
83053
+ };
83054
+ } else {
83055
+ normalized[key] = value2;
83056
+ }
83057
+ }
83058
+ return normalized;
83059
+ }
83060
+ function omitUndefined(record2) {
83061
+ return Object.fromEntries(Object.entries(record2).filter(([, value2]) => value2 !== void 0));
83062
+ }
83063
+
83064
+ // ../../node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
83065
+ var __create3 = Object.create;
83066
+ var __defProp3 = Object.defineProperty;
83067
+ var __getOwnPropDesc3 = Object.getOwnPropertyDescriptor;
83068
+ var __getOwnPropNames3 = Object.getOwnPropertyNames;
83069
+ var __getProtoOf3 = Object.getPrototypeOf;
83070
+ var __hasOwnProp3 = Object.prototype.hasOwnProperty;
83071
+ var __commonJSMin2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
83072
+ var __exportAll2 = (all, symbols) => {
83073
+ let target = {};
83074
+ for (var name in all) {
83075
+ __defProp3(target, name, {
83076
+ get: all[name],
83077
+ enumerable: true
83078
+ });
83079
+ }
83080
+ if (symbols) {
83081
+ __defProp3(target, Symbol.toStringTag, { value: "Module" });
83082
+ }
83083
+ return target;
83084
+ };
83085
+ var __copyProps3 = (to, from, except, desc) => {
83086
+ if (from && typeof from === "object" || typeof from === "function") {
83087
+ for (var keys = __getOwnPropNames3(from), i = 0, n = keys.length, key; i < n; i++) {
83088
+ key = keys[i];
83089
+ if (!__hasOwnProp3.call(to, key) && key !== except) {
83090
+ __defProp3(to, key, {
83091
+ get: ((k) => from[k]).bind(null, key),
83092
+ enumerable: !(desc = __getOwnPropDesc3(from, key)) || desc.enumerable
83093
+ });
83094
+ }
83095
+ }
83096
+ }
83097
+ return to;
83098
+ };
83099
+ var __toESM3 = (mod, isNodeMode, target) => (target = mod != null ? __create3(__getProtoOf3(mod)) : {}, __copyProps3(isNodeMode || !mod || !mod.__esModule ? __defProp3(target, "default", {
83100
+ value: mod,
83101
+ enumerable: true
83102
+ }) : target, mod));
83103
+
83104
+ // ../../node_modules/@modelcontextprotocol/server/dist/dialects-DoSzNhcb.mjs
83105
+ var DRAFT_2020_12_URIS2 = /* @__PURE__ */ new Set(["https://json-schema.org/draft/2020-12/schema", "http://json-schema.org/draft/2020-12/schema"]);
83106
+ var DRAFT_2019_09_URIS2 = /* @__PURE__ */ new Set(["https://json-schema.org/draft/2019-09/schema", "http://json-schema.org/draft/2019-09/schema"]);
83107
+ var DRAFT_07_URIS2 = /* @__PURE__ */ new Set(["https://json-schema.org/draft-07/schema", "http://json-schema.org/draft-07/schema"]);
83108
+ var DRAFT_06_URIS2 = /* @__PURE__ */ new Set(["https://json-schema.org/draft-06/schema", "http://json-schema.org/draft-06/schema"]);
83109
+ function declares2019Dialect2($schema) {
83110
+ return typeof $schema === "string" && DRAFT_2019_09_URIS2.has($schema.replace(/#$/, ""));
83111
+ }
83112
+ function declaredDialect2(schema, remedy) {
83113
+ if (!("$schema" in schema) || typeof schema.$schema !== "string") return "2020-12";
83114
+ const declared = schema.$schema.replace(/#$/, "");
83115
+ if (DRAFT_2020_12_URIS2.has(declared)) return "2020-12";
83116
+ if (DRAFT_2019_09_URIS2.has(declared)) return "2019-09";
83117
+ if (DRAFT_07_URIS2.has(declared) || DRAFT_06_URIS2.has(declared)) return "draft-7";
83118
+ throw new Error(`JSON Schema declares an unsupported dialect ("$schema": "${schema.$schema.slice(0, 200)}"). The default validator supports JSON Schema 2020-12, 2019-09, draft-07, and draft-06; ${remedy}`);
83119
+ }
83120
+
83121
+ // ../../node_modules/@modelcontextprotocol/server/dist/src-CX2iR2pK.mjs
83122
+ var BRANDS2 = /* @__PURE__ */ Symbol.for("mcp.sdk.errorBrands");
83123
+ function stampErrorBrands2(instance, ctor) {
83124
+ const brands = /* @__PURE__ */ new Set();
83125
+ let current = ctor;
83126
+ while (typeof current === "function") {
83127
+ const brand = current.mcpBrand;
83128
+ if (Object.prototype.hasOwnProperty.call(current, "mcpBrand") && typeof brand === "string") brands.add(brand);
83129
+ current = Object.getPrototypeOf(current);
83130
+ }
83131
+ if (brands.size === 0) return;
83132
+ Object.defineProperty(instance, BRANDS2, {
83133
+ value: brands,
83134
+ enumerable: false,
83135
+ configurable: true
83136
+ });
83137
+ }
83138
+ function brandedHasInstance2(cls, value2) {
83139
+ try {
83140
+ if (typeof value2 === "object" && value2 !== null && Object.prototype.hasOwnProperty.call(cls, "mcpBrand") && typeof cls.mcpBrand === "string" && Object.prototype.hasOwnProperty.call(value2, BRANDS2)) {
83141
+ const carried = value2[BRANDS2];
83142
+ if (carried && typeof carried.has === "function" && carried.has(cls.mcpBrand)) return true;
83143
+ }
83144
+ } catch {
83145
+ }
83146
+ return Function.prototype[Symbol.hasInstance].call(cls, value2);
83147
+ }
83148
+ var OAuthError3 = class OAuthError4 extends Error {
83149
+ static {
83150
+ Object.defineProperty(this, "mcpBrand", { value: "mcp.OAuthError" });
83151
+ }
83152
+ static [Symbol.hasInstance](value2) {
83153
+ return brandedHasInstance2(this, value2);
83154
+ }
83155
+ /**
83156
+ * Brand-based type guard: equivalent to `value instanceof this`, as an
83157
+ * explicit static predicate (the axios/AWS-SDK `isInstance` style). Reads
83158
+ * the caller's own brand via `this`, so every branded subclass gets a
83159
+ * correctly-scoped guard by inheritance. Must be invoked on the class —
83160
+ * in callback position write `v => SdkError.isInstance(v)`, not
83161
+ * `.filter(SdkError.isInstance)` (detached calls throw rather than
83162
+ * silently matching nothing).
83163
+ */
83164
+ static isInstance(value2) {
83165
+ if (typeof this !== "function") throw new TypeError("isInstance must be called on the class (e.g. `SdkError.isInstance(value)`); for callbacks use `v => SdkError.isInstance(v)`");
83166
+ return brandedHasInstance2(this, value2);
83167
+ }
83168
+ constructor(code, message, errorUri) {
83169
+ super(message);
83170
+ this.code = code;
83171
+ this.errorUri = errorUri;
83172
+ this.name = "OAuthError";
83173
+ stampErrorBrands2(this, new.target);
83174
+ }
83175
+ /**
83176
+ * Converts the error to a standard OAuth error response object.
83177
+ */
83178
+ toResponseObject() {
83179
+ const response = {
83180
+ error: this.code,
83181
+ error_description: this.message
83182
+ };
83183
+ if (this.errorUri) response.error_uri = this.errorUri;
83184
+ return response;
83185
+ }
83186
+ /**
83187
+ * Creates an {@linkcode OAuthError} from an OAuth error response.
83188
+ */
83189
+ static fromResponse(response) {
83190
+ return new OAuthError4(response.error, response.error_description ?? response.error, response.error_uri);
83191
+ }
83192
+ };
83193
+ var SdkErrorCode2 = /* @__PURE__ */ (function(SdkErrorCode$1) {
83194
+ SdkErrorCode$1["NotConnected"] = "NOT_CONNECTED";
83195
+ SdkErrorCode$1["AlreadyConnected"] = "ALREADY_CONNECTED";
83196
+ SdkErrorCode$1["NotInitialized"] = "NOT_INITIALIZED";
83197
+ SdkErrorCode$1["CapabilityNotSupported"] = "CAPABILITY_NOT_SUPPORTED";
83198
+ SdkErrorCode$1["RequestTimeout"] = "REQUEST_TIMEOUT";
83199
+ SdkErrorCode$1["ConnectionClosed"] = "CONNECTION_CLOSED";
83200
+ SdkErrorCode$1["SendFailed"] = "SEND_FAILED";
83201
+ SdkErrorCode$1["InvalidResult"] = "INVALID_RESULT";
83202
+ SdkErrorCode$1["UnsupportedResultType"] = "UNSUPPORTED_RESULT_TYPE";
83203
+ SdkErrorCode$1["InputRequiredRoundsExceeded"] = "INPUT_REQUIRED_ROUNDS_EXCEEDED";
83204
+ SdkErrorCode$1["ListPaginationExceeded"] = "LIST_PAGINATION_EXCEEDED";
83205
+ SdkErrorCode$1["MethodNotSupportedByProtocolVersion"] = "METHOD_NOT_SUPPORTED_BY_PROTOCOL_VERSION";
83206
+ SdkErrorCode$1["EraNegotiationFailed"] = "ERA_NEGOTIATION_FAILED";
83207
+ SdkErrorCode$1["ClientHttpNotImplemented"] = "CLIENT_HTTP_NOT_IMPLEMENTED";
83208
+ SdkErrorCode$1["ClientHttpAuthentication"] = "CLIENT_HTTP_AUTHENTICATION";
83209
+ SdkErrorCode$1["ClientHttpForbidden"] = "CLIENT_HTTP_FORBIDDEN";
83210
+ SdkErrorCode$1["ClientHttpUnexpectedContent"] = "CLIENT_HTTP_UNEXPECTED_CONTENT";
83211
+ SdkErrorCode$1["ClientHttpFailedToOpenStream"] = "CLIENT_HTTP_FAILED_TO_OPEN_STREAM";
83212
+ SdkErrorCode$1["ClientHttpFailedToTerminateSession"] = "CLIENT_HTTP_FAILED_TO_TERMINATE_SESSION";
83213
+ return SdkErrorCode$1;
83214
+ })({});
83215
+ var SdkError2 = class extends Error {
83216
+ static {
83217
+ Object.defineProperty(this, "mcpBrand", { value: "mcp.SdkError" });
83218
+ }
83219
+ static [Symbol.hasInstance](value2) {
83220
+ return brandedHasInstance2(this, value2);
83221
+ }
83222
+ /**
83223
+ * Brand-based type guard: equivalent to `value instanceof this`, as an
83224
+ * explicit static predicate (the axios/AWS-SDK `isInstance` style). Reads
83225
+ * the caller's own brand via `this`, so every branded subclass gets a
83226
+ * correctly-scoped guard by inheritance. Must be invoked on the class —
83227
+ * in callback position write `v => SdkError.isInstance(v)`, not
83228
+ * `.filter(SdkError.isInstance)` (detached calls throw rather than
83229
+ * silently matching nothing).
83230
+ */
83231
+ static isInstance(value2) {
83232
+ if (typeof this !== "function") throw new TypeError("isInstance must be called on the class (e.g. `SdkError.isInstance(value)`); for callbacks use `v => SdkError.isInstance(v)`");
83233
+ return brandedHasInstance2(this, value2);
83234
+ }
83235
+ constructor(code, message, data) {
83236
+ super(message);
83237
+ this.code = code;
83238
+ this.data = data;
83239
+ this.name = "SdkError";
83240
+ stampErrorBrands2(this, new.target);
83241
+ }
83242
+ };
83243
+ var SdkHttpError2 = class extends SdkError2 {
83244
+ static {
83245
+ Object.defineProperty(this, "mcpBrand", { value: "mcp.SdkHttpError" });
83246
+ }
83247
+ constructor(code, message, data) {
83248
+ super(code, message, data);
83249
+ this.name = "SdkHttpError";
83250
+ }
83251
+ get status() {
83252
+ return this.data.status;
83253
+ }
83254
+ get statusText() {
83255
+ return this.data.statusText;
83256
+ }
83257
+ };
83258
+ function isPlainObject$7(value2) {
83259
+ return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
83260
+ }
83261
+ function isImpliedCapabilityMember(capability, member, declaredValue) {
83262
+ return capability === "elicitation" && member === "form" && declaredValue["form"] === void 0 && declaredValue["url"] === void 0;
83263
+ }
83264
+ function requiredClientCapabilitiesForInputRequest(entry) {
83265
+ switch (entry.method) {
83266
+ case "elicitation/create":
83267
+ if (entry.params?.["mode"] === "url") return { elicitation: { url: {} } };
83268
+ return { elicitation: { form: {} } };
83269
+ case "sampling/createMessage": {
83270
+ const params = entry.params;
83271
+ if (params !== void 0 && (params["tools"] !== void 0 || params["toolChoice"] !== void 0)) return { sampling: { tools: {} } };
83272
+ return { sampling: {} };
83273
+ }
83274
+ case "roots/list":
83275
+ return { roots: {} };
83276
+ default:
83277
+ return;
83278
+ }
83279
+ }
83280
+ function missingClientCapabilities(required2, declared) {
83281
+ const missing = {};
83282
+ for (const [capability, requirement] of Object.entries(required2)) {
83283
+ if (requirement === void 0) continue;
83284
+ const declaredValue = declared === void 0 ? void 0 : declared[capability];
83285
+ if (declaredValue === void 0) {
83286
+ missing[capability] = requirement;
83287
+ continue;
83288
+ }
83289
+ if (isPlainObject$7(requirement) && isPlainObject$7(declaredValue)) {
83290
+ const missingMembers = {};
83291
+ for (const [member, memberRequirement] of Object.entries(requirement)) if (memberRequirement !== void 0 && declaredValue[member] === void 0 && !isImpliedCapabilityMember(capability, member, declaredValue)) missingMembers[member] = memberRequirement;
83292
+ if (Object.keys(missingMembers).length > 0) missing[capability] = missingMembers;
83293
+ }
83294
+ }
83295
+ return Object.keys(missing).length > 0 ? missing : void 0;
83296
+ }
83297
+ var FIRST_MODERN_PROTOCOL_VERSION2 = "2026-07-28";
83298
+ function isModernProtocolVersion2(version2) {
83299
+ return version2 >= FIRST_MODERN_PROTOCOL_VERSION2;
83300
+ }
83301
+ function legacyProtocolVersions2(versions) {
83302
+ return versions.filter((version2) => !isModernProtocolVersion2(version2));
83303
+ }
83304
+ function modernProtocolVersions2(versions) {
83305
+ return versions.filter((version2) => isModernProtocolVersion2(version2));
83306
+ }
83307
+ function appendTextFallbackForNonObject2(result) {
83308
+ const sc = result.structuredContent;
83309
+ if (sc === void 0) return result;
83310
+ if (!(typeof sc !== "object" || sc === null || Array.isArray(sc))) return result;
83311
+ if (result.content?.some((c) => c.type === "text") ?? false) return result;
83312
+ return {
83313
+ ...result,
83314
+ content: [...result.content ?? [], {
83315
+ type: "text",
83316
+ text: JSON.stringify(sc)
83317
+ }]
83318
+ };
83319
+ }
83320
+ var TOOL_RESULT_FOREIGN_FAMILY_KEYS2 = [
83321
+ "task",
83322
+ "inputRequests",
83323
+ "requestState"
83324
+ ];
83325
+ function normalizeContentlessToolResult2(value2) {
83326
+ if (value2 === null || typeof value2 !== "object" || Array.isArray(value2) || value2.content !== void 0 || TOOL_RESULT_FOREIGN_FAMILY_KEYS2.some((key) => key in value2)) return value2;
83327
+ return {
83328
+ ...value2,
83329
+ content: []
83330
+ };
82487
83331
  }
82488
83332
  function build$12() {
82489
83333
  const JSONValueSchema$1 = lazy(() => union([
@@ -94584,27 +95428,27 @@ async function runMcpStdioProxy(env = process.env, local = new StdioServerTransp
94584
95428
 
94585
95429
  // src/native-hook-command.ts
94586
95430
  var import_node_crypto21 = require("crypto");
94587
- var import_node_fs26 = require("fs");
94588
- var import_node_os16 = require("os");
94589
- var import_node_path28 = require("path");
95431
+ var import_node_fs27 = require("fs");
95432
+ var import_node_os17 = require("os");
95433
+ var import_node_path29 = require("path");
94590
95434
 
94591
95435
  // src/native-project-config.ts
94592
- var import_node_fs25 = require("fs");
94593
- var import_node_path27 = require("path");
95436
+ var import_node_fs26 = require("fs");
95437
+ var import_node_path28 = require("path");
94594
95438
  var MAX_ALAN_PROJECT_CONFIG_BYTES = 64 * 1024;
94595
95439
  var ORGANIZATION_SLUG_PATTERN = /^[a-z0-9-]+$/;
94596
95440
  var TEAM_IDENTIFIER_PATTERN = /^[A-Z0-9]{2,4}$/i;
94597
95441
  function readAlanProjectPin(cwd) {
94598
95442
  let fd;
94599
95443
  try {
94600
- fd = (0, import_node_fs25.openSync)(
94601
- (0, import_node_path27.join)(cwd, "alan.json"),
94602
- import_node_fs25.constants.O_RDONLY | import_node_fs25.constants.O_NONBLOCK | (import_node_fs25.constants.O_NOFOLLOW ?? 0)
95444
+ fd = (0, import_node_fs26.openSync)(
95445
+ (0, import_node_path28.join)(cwd, "alan.json"),
95446
+ import_node_fs26.constants.O_RDONLY | import_node_fs26.constants.O_NONBLOCK | (import_node_fs26.constants.O_NOFOLLOW ?? 0)
94603
95447
  );
94604
- const stat2 = (0, import_node_fs25.fstatSync)(fd);
95448
+ const stat2 = (0, import_node_fs26.fstatSync)(fd);
94605
95449
  if (!stat2.isFile() || stat2.size > MAX_ALAN_PROJECT_CONFIG_BYTES) return void 0;
94606
95450
  const buffer = Buffer.allocUnsafe(MAX_ALAN_PROJECT_CONFIG_BYTES + 1);
94607
- const bytesRead = (0, import_node_fs25.readSync)(fd, buffer, 0, buffer.byteLength, 0);
95451
+ const bytesRead = (0, import_node_fs26.readSync)(fd, buffer, 0, buffer.byteLength, 0);
94608
95452
  if (bytesRead > MAX_ALAN_PROJECT_CONFIG_BYTES) return void 0;
94609
95453
  const parsed = JSON.parse(buffer.subarray(0, bytesRead).toString("utf8"));
94610
95454
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
@@ -94620,7 +95464,7 @@ function readAlanProjectPin(cwd) {
94620
95464
  } finally {
94621
95465
  if (fd !== void 0) {
94622
95466
  try {
94623
- (0, import_node_fs25.closeSync)(fd);
95467
+ (0, import_node_fs26.closeSync)(fd);
94624
95468
  } catch {
94625
95469
  }
94626
95470
  }
@@ -94859,19 +95703,19 @@ function nonNegativeIntegerField(payload, ...names) {
94859
95703
  return void 0;
94860
95704
  }
94861
95705
  function resolveCodexSessionIdFromTranscriptPath(transcriptPath) {
94862
- const fileName = (0, import_node_path28.basename)(transcriptPath);
95706
+ const fileName = (0, import_node_path29.basename)(transcriptPath);
94863
95707
  const match = /^rollout-.*-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i.exec(
94864
95708
  fileName
94865
95709
  );
94866
95710
  return match?.[1];
94867
95711
  }
94868
- function resolveKimiTranscriptPath(externalSessionId, kimiHomeDir = process.env.KIMI_CODE_HOME?.trim() || (0, import_node_path28.join)((0, import_node_os16.homedir)(), ".kimi-code")) {
94869
- if ((0, import_node_path28.basename)(externalSessionId) !== externalSessionId) return void 0;
94870
- const sessionsDir = (0, import_node_path28.join)(kimiHomeDir, "sessions");
94871
- if (!(0, import_node_fs26.existsSync)(sessionsDir)) return void 0;
94872
- for (const workspace of (0, import_node_fs26.readdirSync)(sessionsDir, { withFileTypes: true })) {
95712
+ function resolveKimiTranscriptPath(externalSessionId, kimiHomeDir = process.env.KIMI_CODE_HOME?.trim() || (0, import_node_path29.join)((0, import_node_os17.homedir)(), ".kimi-code")) {
95713
+ if ((0, import_node_path29.basename)(externalSessionId) !== externalSessionId) return void 0;
95714
+ const sessionsDir = (0, import_node_path29.join)(kimiHomeDir, "sessions");
95715
+ if (!(0, import_node_fs27.existsSync)(sessionsDir)) return void 0;
95716
+ for (const workspace of (0, import_node_fs27.readdirSync)(sessionsDir, { withFileTypes: true })) {
94873
95717
  if (!workspace.isDirectory()) continue;
94874
- const transcriptPath = (0, import_node_path28.join)(
95718
+ const transcriptPath = (0, import_node_path29.join)(
94875
95719
  sessionsDir,
94876
95720
  workspace.name,
94877
95721
  externalSessionId,
@@ -94879,7 +95723,7 @@ function resolveKimiTranscriptPath(externalSessionId, kimiHomeDir = process.env.
94879
95723
  "main",
94880
95724
  "wire.jsonl"
94881
95725
  );
94882
- if ((0, import_node_fs26.existsSync)(transcriptPath)) return transcriptPath;
95726
+ if ((0, import_node_fs27.existsSync)(transcriptPath)) return transcriptPath;
94883
95727
  }
94884
95728
  return void 0;
94885
95729
  }
@@ -94914,7 +95758,7 @@ function parseNativeHookEnvelope(input2) {
94914
95758
  const rawHookEvent = input2.hookEvent ?? stringField4(payload, "hook_event_name", "hookEvent", "hookEventName", "type") ?? "notification";
94915
95759
  const eventType = normalizeHookEvent(rawHookEvent);
94916
95760
  const cwd = stringField4(payload, "cwd", "working_directory", "workingDirectory") ?? firstStringArrayField(payload, "workspace_roots", "workspaceRoots");
94917
- const resolvedCwd = cwd ? (0, import_node_path28.resolve)(cwd) : void 0;
95761
+ const resolvedCwd = cwd ? (0, import_node_path29.resolve)(cwd) : void 0;
94918
95762
  const transcriptPath = eventType === "subagent_start" || eventType === "subagent_end" ? stringField4(
94919
95763
  payload,
94920
95764
  "agent_transcript_path",
@@ -94960,7 +95804,7 @@ function parseNativeHookEnvelope(input2) {
94960
95804
  ...(eventType === "subagent_start" || eventType === "subagent_end") && parentSessionId ? { parentExternalSessionId: parentSessionId } : {},
94961
95805
  ...resolvedCwd ? { cwd: resolvedCwd } : {},
94962
95806
  ...projectPin,
94963
- ...transcriptPath ? { transcriptPath: (0, import_node_path28.resolve)(transcriptPath) } : {},
95807
+ ...transcriptPath ? { transcriptPath: (0, import_node_path29.resolve)(transcriptPath) } : {},
94964
95808
  ...cursor ? { sourceCursor: cursor } : {},
94965
95809
  providerPayload
94966
95810
  };
@@ -95007,7 +95851,7 @@ function readBoundedHookPayload(fd = 0, maxBytes = MAX_NATIVE_HOOK_STDIN_BYTES)
95007
95851
  let totalBytes = 0;
95008
95852
  while (true) {
95009
95853
  const chunk = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1 - totalBytes));
95010
- const bytesRead = (0, import_node_fs26.readSync)(fd, chunk, 0, chunk.byteLength, null);
95854
+ const bytesRead = (0, import_node_fs27.readSync)(fd, chunk, 0, chunk.byteLength, null);
95011
95855
  if (bytesRead === 0) break;
95012
95856
  totalBytes += bytesRead;
95013
95857
  if (totalBytes > maxBytes) throw new Error(`hook input exceeds ${maxBytes} bytes`);
@@ -95090,7 +95934,7 @@ function collectNativeIntegrationStatus(args = []) {
95090
95934
  const config2 = readConfig();
95091
95935
  const configPath = getConfigPath();
95092
95936
  const env = getDaemonCliEnvironment();
95093
- const homeDir = (0, import_node_os16.homedir)();
95937
+ const homeDir = (0, import_node_os17.homedir)();
95094
95938
  return NATIVE_SESSION_PROVIDER_CAPABILITIES.map((capability) => ({
95095
95939
  provider: capability.providerKind,
95096
95940
  command: capability.command,
@@ -95119,7 +95963,7 @@ function decodeResumeFallbackContext(encoded) {
95119
95963
  }
95120
95964
 
95121
95965
  // src/sandbox.ts
95122
- var import_node_os17 = require("os");
95966
+ var import_node_os18 = require("os");
95123
95967
 
95124
95968
  // src/execution-activity-tracker.ts
95125
95969
  var ExecutionActivityTracker = class {
@@ -95733,7 +96577,7 @@ var agentProbeAckSchema = external_exports.object({
95733
96577
 
95734
96578
  // src/sandbox-outbox.ts
95735
96579
  var import_node_crypto23 = require("crypto");
95736
- var import_node_fs27 = require("fs");
96580
+ var import_node_fs28 = require("fs");
95737
96581
  function deriveSandboxOutboxKey(sessionToken) {
95738
96582
  if (!sessionToken) {
95739
96583
  throw new Error("sandbox event outbox requires ALAN_SESSION_TOKEN for key derivation");
@@ -95754,24 +96598,24 @@ function assertValidOutboxKey(key, path2) {
95754
96598
  }
95755
96599
  function loadOrCreateSandboxOutboxKey(input2) {
95756
96600
  const keyPath = sandboxOutboxKeyPath(input2.spoolPath);
95757
- if ((0, import_node_fs27.existsSync)(keyPath)) {
95758
- (0, import_node_fs27.chmodSync)(keyPath, 384);
95759
- return assertValidOutboxKey((0, import_node_fs27.readFileSync)(keyPath, "utf8").trim(), keyPath);
96601
+ if ((0, import_node_fs28.existsSync)(keyPath)) {
96602
+ (0, import_node_fs28.chmodSync)(keyPath, 384);
96603
+ return assertValidOutboxKey((0, import_node_fs28.readFileSync)(keyPath, "utf8").trim(), keyPath);
95760
96604
  }
95761
96605
  const initialKey = deriveSandboxOutboxKey(input2.sessionToken);
95762
96606
  try {
95763
- (0, import_node_fs27.writeFileSync)(keyPath, initialKey, { encoding: "utf8", flag: "wx", mode: 384 });
96607
+ (0, import_node_fs28.writeFileSync)(keyPath, initialKey, { encoding: "utf8", flag: "wx", mode: 384 });
95764
96608
  return initialKey;
95765
96609
  } catch (error61) {
95766
96610
  if (error61.code !== "EEXIST") throw error61;
95767
- (0, import_node_fs27.chmodSync)(keyPath, 384);
95768
- return assertValidOutboxKey((0, import_node_fs27.readFileSync)(keyPath, "utf8").trim(), keyPath);
96611
+ (0, import_node_fs28.chmodSync)(keyPath, 384);
96612
+ return assertValidOutboxKey((0, import_node_fs28.readFileSync)(keyPath, "utf8").trim(), keyPath);
95769
96613
  }
95770
96614
  }
95771
96615
  function quarantineUnusableOutbox(path2) {
95772
96616
  const suffix = `corrupt-${Date.now()}-${process.pid}`;
95773
96617
  for (const candidate of [path2, eventJournalDir(path2)]) {
95774
- if ((0, import_node_fs27.existsSync)(candidate)) (0, import_node_fs27.renameSync)(candidate, `${candidate}.${suffix}`);
96618
+ if ((0, import_node_fs28.existsSync)(candidate)) (0, import_node_fs28.renameSync)(candidate, `${candidate}.${suffix}`);
95775
96619
  }
95776
96620
  }
95777
96621
  function createSandboxEventOutbox(input2) {
@@ -96183,6 +97027,7 @@ var WSClient = class {
96183
97027
  providerSessionId: data.providerSessionId,
96184
97028
  resumeFallbackContext: data.resumeFallbackContext,
96185
97029
  backendKind: data.backendKind,
97030
+ providerApiKey: data.providerApiKey,
96186
97031
  agentId: data.agentId,
96187
97032
  agentPrompt: data.agentPrompt,
96188
97033
  selectedModel: data.selectedModel,
@@ -96200,7 +97045,10 @@ var WSClient = class {
96200
97045
  cloudEnvironmentId: data.cloudEnvironmentId,
96201
97046
  conversationId: data.conversationId,
96202
97047
  teamId: data.teamId,
96203
- currentUser: data.currentUser
97048
+ currentUser: data.currentUser,
97049
+ terminalContextRefs: data.terminalContextRefs,
97050
+ terminalSnapshots: data.terminalSnapshots,
97051
+ externalMcpServers: data.externalMcpServers
96204
97052
  };
96205
97053
  const acceptDelivery = () => {
96206
97054
  if (data.messageId) this.rememberAcceptedMessageId(data.messageId);
@@ -96224,6 +97072,9 @@ var WSClient = class {
96224
97072
  this.socket.on("stop", () => {
96225
97073
  callbacks.onStop();
96226
97074
  });
97075
+ this.socket.on("agent.abort", (data) => {
97076
+ callbacks.onAbort?.({ runId: data?.runId, reason: data?.reason });
97077
+ });
96227
97078
  this.socket.on("tool_response", (data) => {
96228
97079
  if (data?.toolId && typeof data.response === "string") {
96229
97080
  callbacks.onToolResponse?.(data.toolId, data.response);
@@ -96281,6 +97132,7 @@ async function runSandbox(config2) {
96281
97132
  let currentTaskMeta;
96282
97133
  let currentPrMeta;
96283
97134
  let currentWorkflowTools;
97135
+ let currentExternalMcpServers;
96284
97136
  let currentMaxIterations = config2.maxIterations ?? 50;
96285
97137
  let currentTaskId = config2.taskId;
96286
97138
  let currentConversationId = config2.sessionId;
@@ -96375,6 +97227,14 @@ async function runSandbox(config2) {
96375
97227
  lifecycle.info("sandbox_agent_stop_received");
96376
97228
  stopActiveAgent();
96377
97229
  },
97230
+ onAbort: ({ runId, reason }) => {
97231
+ if (runId && currentRunId && runId !== currentRunId) {
97232
+ lifecycle.info("sandbox_agent_abort_ignored_stale_run", { runId, currentRunId });
97233
+ return;
97234
+ }
97235
+ lifecycle.info("sandbox_agent_abort_received", { runId, reason });
97236
+ stopActiveAgent(reason === "watchdog_timeout" ? "watchdog_timeout" : "user");
97237
+ },
96378
97238
  onToolResponse: (toolId, response) => {
96379
97239
  if (currentAgent) {
96380
97240
  const sent = currentAgent.sendToolResponse(toolId, response);
@@ -96431,7 +97291,7 @@ async function runSandbox(config2) {
96431
97291
  }
96432
97292
  const presenter = new WebPresenter(wsClient, config2.sessionId, config2.projectPath);
96433
97293
  presenter.setSuppressSessionLifecycle(true);
96434
- const runtimeSkillManager = await RuntimeSkillManager.open({ homeDirectory: (0, import_node_os17.homedir)() });
97294
+ const runtimeSkillManager = await RuntimeSkillManager.open({ homeDirectory: (0, import_node_os18.homedir)() });
96435
97295
  let lastProviderSessionId = config2.providerSessionId;
96436
97296
  let resumeFallbackContext = config2.resumeFallbackContext;
96437
97297
  let activeBackendKind = config2.backendKind || "claude_cli";
@@ -96571,6 +97431,7 @@ async function runSandbox(config2) {
96571
97431
  resumeFallbackContext,
96572
97432
  taskMeta: currentTaskMeta,
96573
97433
  prMeta: currentPrMeta,
97434
+ externalMcpServers: currentExternalMcpServers,
96574
97435
  taskId: currentTaskId,
96575
97436
  conversationId: currentConversationId,
96576
97437
  alanMcp: currentAlanMcp,
@@ -96688,6 +97549,7 @@ async function runSandbox(config2) {
96688
97549
  activeMode = nextPayload.mode;
96689
97550
  }
96690
97551
  currentAlanMcp = nextPayload.alanMcp;
97552
+ currentExternalMcpServers = nextPayload.externalMcpServers;
96691
97553
  if (nextPayload.taskMeta !== void 0) {
96692
97554
  currentTaskMeta = nextPayload.taskMeta;
96693
97555
  }
@@ -96728,616 +97590,6 @@ async function runSandbox(config2) {
96728
97590
  wsClient.close();
96729
97591
  }
96730
97592
 
96731
- // src/service-manager.ts
96732
- var import_node_child_process9 = require("child_process");
96733
- var import_node_fs28 = require("fs");
96734
- var import_node_os18 = require("os");
96735
- var import_node_path29 = require("path");
96736
- var SERVICE_LABEL = "ai.tryalan.agent";
96737
- var SYSTEMD_UNIT = "alan-agent.service";
96738
- var WINDOWS_TASK = "Alan Agent";
96739
- function xml(value2) {
96740
- return value2.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
96741
- }
96742
- function systemdArg(value2) {
96743
- return `"${value2.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
96744
- }
96745
- function powershellLiteral(value2) {
96746
- return `'${value2.replaceAll("'", "''")}'`;
96747
- }
96748
- function serviceNames(profile) {
96749
- if (profile === "production") {
96750
- return { launchd: SERVICE_LABEL, systemd: SYSTEMD_UNIT, windows: WINDOWS_TASK };
96751
- }
96752
- const suffix = profile === "custom" ? "prod" : profile;
96753
- const title = `${suffix[0]?.toUpperCase()}${suffix.slice(1)}`;
96754
- return {
96755
- launchd: `${SERVICE_LABEL}.${suffix}`,
96756
- systemd: `alan-agent-${suffix}.service`,
96757
- windows: `${WINDOWS_TASK} ${title}`
96758
- };
96759
- }
96760
- function buildDaemonServicePlan(input2) {
96761
- const daemonArgs = [input2.nodeExecutable, input2.cliEntry, "daemon", "--profile", input2.profile];
96762
- const names = serviceNames(input2.profile);
96763
- if (input2.platform === "darwin") {
96764
- const userId = input2.userId ?? process.getuid?.();
96765
- if (userId === void 0) throw new Error("Cannot determine the current macOS user ID");
96766
- const domain2 = `gui/${userId}`;
96767
- const serviceTarget = `${domain2}/${names.launchd}`;
96768
- const manifestPath = (0, import_node_path29.join)(input2.homeDir, "Library", "LaunchAgents", `${names.launchd}.plist`);
96769
- const logDir = (0, import_node_path29.join)(resolveAgentConfigDirectory(input2.profile, input2.homeDir), "logs");
96770
- const programArguments = daemonArgs.map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
96771
- const environmentEntries = Object.entries(input2.environment ?? {}).map(([key, value2]) => ` <key>${xml(key)}</key>
96772
- <string>${xml(value2)}</string>`).join("\n");
96773
- const environmentBlock = environmentEntries ? ` <key>EnvironmentVariables</key>
96774
- <dict>
96775
- ${environmentEntries}
96776
- </dict>
96777
- ` : "";
96778
- const manifest = `<?xml version="1.0" encoding="UTF-8"?>
96779
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
96780
- <plist version="1.0">
96781
- <dict>
96782
- <key>Label</key>
96783
- <string>${names.launchd}</string>
96784
- <key>ProgramArguments</key>
96785
- <array>
96786
- ${programArguments}
96787
- </array>
96788
- ${environmentBlock} <key>RunAtLoad</key>
96789
- <true/>
96790
- <key>KeepAlive</key>
96791
- <dict>
96792
- <key>SuccessfulExit</key>
96793
- <false/>
96794
- </dict>
96795
- <key>ThrottleInterval</key>
96796
- <integer>5</integer>
96797
- <key>StandardOutPath</key>
96798
- <string>${xml((0, import_node_path29.join)(logDir, "daemon.log"))}</string>
96799
- <key>StandardErrorPath</key>
96800
- <string>${xml((0, import_node_path29.join)(logDir, "daemon-error.log"))}</string>
96801
- </dict>
96802
- </plist>
96803
- `;
96804
- return {
96805
- platform: "darwin",
96806
- manifestPath,
96807
- manifest,
96808
- logDirectory: logDir,
96809
- loadDefinitionCommands: [
96810
- {
96811
- command: "launchctl",
96812
- args: ["bootstrap", domain2, manifestPath],
96813
- tolerateFailure: true
96814
- }
96815
- ],
96816
- reloadDefinitionCommands: [
96817
- {
96818
- command: "launchctl",
96819
- args: ["bootout", domain2, manifestPath],
96820
- tolerateFailure: true
96821
- },
96822
- {
96823
- command: "launchctl",
96824
- args: ["bootstrap", domain2, manifestPath],
96825
- tolerateFailure: false
96826
- }
96827
- ],
96828
- enableCommands: [
96829
- {
96830
- command: "launchctl",
96831
- args: ["enable", serviceTarget],
96832
- tolerateFailure: true
96833
- }
96834
- ],
96835
- startCommands: [
96836
- {
96837
- command: "launchctl",
96838
- args: ["kickstart", serviceTarget],
96839
- tolerateFailure: false
96840
- }
96841
- ],
96842
- stopCommands: [
96843
- {
96844
- command: "launchctl",
96845
- args: ["kill", "SIGTERM", serviceTarget],
96846
- tolerateFailure: true
96847
- }
96848
- ],
96849
- replaceCommands: [
96850
- {
96851
- command: "launchctl",
96852
- args: ["bootout", domain2, manifestPath],
96853
- tolerateFailure: true
96854
- },
96855
- {
96856
- command: "launchctl",
96857
- args: ["bootstrap", domain2, manifestPath],
96858
- tolerateFailure: false
96859
- },
96860
- {
96861
- command: "launchctl",
96862
- args: ["kickstart", "-k", serviceTarget],
96863
- tolerateFailure: false
96864
- }
96865
- ],
96866
- uninstallCommands: [
96867
- {
96868
- command: "launchctl",
96869
- args: ["bootout", domain2, manifestPath],
96870
- tolerateFailure: true
96871
- }
96872
- ],
96873
- statusCommand: {
96874
- command: "launchctl",
96875
- args: ["print", serviceTarget],
96876
- tolerateFailure: true
96877
- },
96878
- isActiveCommand: {
96879
- command: "launchctl",
96880
- args: ["print", serviceTarget],
96881
- tolerateFailure: true
96882
- },
96883
- lingerStatusCommand: null
96884
- };
96885
- }
96886
- if (input2.platform === "linux") {
96887
- const manifestPath = (0, import_node_path29.join)(input2.homeDir, ".config", "systemd", "user", names.systemd);
96888
- const environmentLines = Object.entries(input2.environment ?? {}).map(([key, value2]) => `Environment=${systemdArg(`${key}=${value2}`)}`).join("\n");
96889
- const manifest = `[Unit]
96890
- Description=Alan local agent daemon
96891
- After=network-online.target
96892
- Wants=network-online.target
96893
- StartLimitIntervalSec=300
96894
- StartLimitBurst=3
96895
-
96896
- [Service]
96897
- Type=simple
96898
- ${environmentLines ? `${environmentLines}
96899
- ` : ""}ExecStart=${daemonArgs.map(systemdArg).join(" ")}
96900
- Restart=on-failure
96901
- RestartSec=5
96902
-
96903
- [Install]
96904
- WantedBy=default.target
96905
- `;
96906
- return {
96907
- platform: "linux",
96908
- manifestPath,
96909
- manifest,
96910
- logDirectory: null,
96911
- loadDefinitionCommands: [
96912
- {
96913
- command: "systemctl",
96914
- args: ["--user", "daemon-reload"],
96915
- tolerateFailure: false
96916
- }
96917
- ],
96918
- reloadDefinitionCommands: [
96919
- {
96920
- command: "systemctl",
96921
- args: ["--user", "daemon-reload"],
96922
- tolerateFailure: false
96923
- }
96924
- ],
96925
- enableCommands: [
96926
- {
96927
- command: "systemctl",
96928
- args: ["--user", "enable", names.systemd],
96929
- tolerateFailure: false
96930
- }
96931
- ],
96932
- startCommands: [
96933
- {
96934
- command: "systemctl",
96935
- args: ["--user", "start", names.systemd],
96936
- tolerateFailure: false
96937
- }
96938
- ],
96939
- stopCommands: [
96940
- {
96941
- command: "systemctl",
96942
- args: ["--user", "stop", names.systemd],
96943
- tolerateFailure: true
96944
- }
96945
- ],
96946
- replaceCommands: [
96947
- {
96948
- command: "systemctl",
96949
- args: ["--user", "daemon-reload"],
96950
- tolerateFailure: false
96951
- },
96952
- {
96953
- command: "systemctl",
96954
- args: ["--user", "restart", names.systemd],
96955
- tolerateFailure: false
96956
- }
96957
- ],
96958
- uninstallCommands: [
96959
- {
96960
- command: "systemctl",
96961
- args: ["--user", "disable", "--now", names.systemd],
96962
- tolerateFailure: true
96963
- },
96964
- {
96965
- command: "systemctl",
96966
- args: ["--user", "daemon-reload"],
96967
- tolerateFailure: false
96968
- }
96969
- ],
96970
- statusCommand: {
96971
- command: "systemctl",
96972
- args: ["--user", "status", names.systemd, "--no-pager"],
96973
- tolerateFailure: true
96974
- },
96975
- isActiveCommand: {
96976
- command: "systemctl",
96977
- args: ["--user", "is-active", names.systemd],
96978
- tolerateFailure: true
96979
- },
96980
- lingerStatusCommand: {
96981
- command: "loginctl",
96982
- args: ["show-user", "$(id -un)", "-p", "Linger"],
96983
- tolerateFailure: true
96984
- }
96985
- };
96986
- }
96987
- const environmentPrefix = Object.entries(input2.environment ?? {}).map(([key, value2]) => `$env:${key} = ${powershellLiteral(value2)}`).join("; ");
96988
- const directArgument = [input2.cliEntry, "daemon", "--profile", input2.profile].map((part) => part.includes(" ") ? `"${part}"` : part).join(" ");
96989
- const executable = powershellLiteral(environmentPrefix ? "powershell.exe" : input2.nodeExecutable);
96990
- const argument = powershellLiteral(
96991
- environmentPrefix ? `-NoProfile -NonInteractive -WindowStyle Hidden -Command "${environmentPrefix}; & ${powershellLiteral(input2.nodeExecutable)} ${powershellLiteral(input2.cliEntry)} daemon --profile ${input2.profile}"` : directArgument
96992
- );
96993
- const registerScript = [
96994
- "$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name",
96995
- `$action = New-ScheduledTaskAction -Execute ${executable} -Argument ${argument}`,
96996
- "$trigger = New-ScheduledTaskTrigger -AtLogOn -User $currentUser",
96997
- "$principal = New-ScheduledTaskPrincipal -UserId $currentUser -LogonType Interactive -RunLevel Limited",
96998
- "$settings = New-ScheduledTaskSettingsSet -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit ([TimeSpan]::Zero)",
96999
- `Register-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null`
97000
- ].join("; ");
97001
- return {
97002
- platform: "win32",
97003
- manifestPath: null,
97004
- manifest: null,
97005
- logDirectory: null,
97006
- loadDefinitionCommands: [
97007
- {
97008
- command: "powershell.exe",
97009
- args: ["-NoProfile", "-NonInteractive", "-Command", registerScript],
97010
- tolerateFailure: false
97011
- }
97012
- ],
97013
- reloadDefinitionCommands: [
97014
- {
97015
- command: "powershell.exe",
97016
- args: ["-NoProfile", "-NonInteractive", "-Command", registerScript],
97017
- tolerateFailure: false
97018
- }
97019
- ],
97020
- enableCommands: [],
97021
- startCommands: [
97022
- {
97023
- command: "powershell.exe",
97024
- args: [
97025
- "-NoProfile",
97026
- "-NonInteractive",
97027
- "-Command",
97028
- `Start-ScheduledTask -TaskName ${powershellLiteral(names.windows)}`
97029
- ],
97030
- tolerateFailure: false
97031
- }
97032
- ],
97033
- stopCommands: [
97034
- {
97035
- command: "powershell.exe",
97036
- args: [
97037
- "-NoProfile",
97038
- "-NonInteractive",
97039
- "-Command",
97040
- `Stop-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -ErrorAction SilentlyContinue`
97041
- ],
97042
- tolerateFailure: true
97043
- }
97044
- ],
97045
- replaceCommands: [
97046
- {
97047
- command: "powershell.exe",
97048
- args: [
97049
- "-NoProfile",
97050
- "-NonInteractive",
97051
- "-Command",
97052
- `${registerScript}; Start-ScheduledTask -TaskName ${powershellLiteral(names.windows)}`
97053
- ],
97054
- tolerateFailure: false
97055
- }
97056
- ],
97057
- uninstallCommands: [
97058
- {
97059
- command: "powershell.exe",
97060
- args: [
97061
- "-NoProfile",
97062
- "-NonInteractive",
97063
- "-Command",
97064
- `Unregister-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -Confirm:$false -ErrorAction SilentlyContinue`
97065
- ],
97066
- tolerateFailure: true
97067
- }
97068
- ],
97069
- statusCommand: {
97070
- command: "powershell.exe",
97071
- args: [
97072
- "-NoProfile",
97073
- "-NonInteractive",
97074
- "-Command",
97075
- `Get-ScheduledTask -TaskName ${powershellLiteral(names.windows)} | Format-List TaskName,State`
97076
- ],
97077
- tolerateFailure: true
97078
- },
97079
- isActiveCommand: {
97080
- command: "powershell.exe",
97081
- args: [
97082
- "-NoProfile",
97083
- "-NonInteractive",
97084
- "-Command",
97085
- `(Get-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -ErrorAction SilentlyContinue).State`
97086
- ],
97087
- tolerateFailure: true
97088
- },
97089
- lingerStatusCommand: null
97090
- };
97091
- }
97092
- function resolveCurrentPlatform() {
97093
- const currentPlatform = (0, import_node_os18.platform)();
97094
- if (currentPlatform !== "darwin" && currentPlatform !== "linux" && currentPlatform !== "win32") {
97095
- throw new Error(`Daemon service installation is not supported on ${currentPlatform}`);
97096
- }
97097
- return currentPlatform;
97098
- }
97099
- function currentServicePlan(args, runtime) {
97100
- const currentPlatform = resolveCurrentPlatform();
97101
- const profile = resolveEndpointProfileFromArgs(args);
97102
- const cliEntry = runtime?.cliEntry ?? process.argv[1];
97103
- if (!cliEntry) throw new Error("Cannot determine the alan-agent executable path");
97104
- return buildDaemonServicePlan({
97105
- platform: currentPlatform,
97106
- homeDir: (0, import_node_os18.homedir)(),
97107
- nodeExecutable: runtime?.executable ?? process.execPath,
97108
- cliEntry: (0, import_node_path29.resolve)(cliEntry),
97109
- profile,
97110
- environment: runtime?.environment
97111
- });
97112
- }
97113
- function writeManifestAtomic(path2, contents) {
97114
- (0, import_node_fs28.mkdirSync)((0, import_node_path29.dirname)(path2), { recursive: true, mode: 448 });
97115
- const pendingPath = `${path2}.pending-${process.pid}`;
97116
- try {
97117
- (0, import_node_fs28.writeFileSync)(pendingPath, contents, { mode: 384 });
97118
- (0, import_node_fs28.chmodSync)(pendingPath, 384);
97119
- (0, import_node_fs28.renameSync)(pendingPath, path2);
97120
- } finally {
97121
- (0, import_node_fs28.rmSync)(pendingPath, { force: true });
97122
- }
97123
- }
97124
- function defaultReadManifest(path2) {
97125
- if (!(0, import_node_fs28.existsSync)(path2)) return null;
97126
- return (0, import_node_fs28.readFileSync)(path2, "utf8");
97127
- }
97128
- function runServiceCommand(command) {
97129
- const result = (0, import_node_child_process9.spawnSync)(command.command, command.args, { encoding: "utf8", shell: false });
97130
- const status = result.status ?? 1;
97131
- const output2 = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
97132
- if (status !== 0 && !command.tolerateFailure) {
97133
- throw new Error(
97134
- `Service command failed (${command.command} ${command.args.join(" ")}): ${output2 || `exit ${status}`}`
97135
- );
97136
- }
97137
- return { status, output: output2 };
97138
- }
97139
- function defaultSleep(ms) {
97140
- return new Promise((resolveSleep) => {
97141
- setTimeout(resolveSleep, ms);
97142
- });
97143
- }
97144
- function parseServiceHostState(servicePlatform, result) {
97145
- const output2 = result.output;
97146
- if (servicePlatform === "linux") {
97147
- const trimmed = output2.trim().toLowerCase();
97148
- if (trimmed === "inactive" || trimmed === "failed" || trimmed === "dead" || /\binactive\b/i.test(output2) || /\bfailed\b/i.test(output2)) {
97149
- return "stopped";
97150
- }
97151
- }
97152
- if (result.status !== 0) return "unknown";
97153
- if (servicePlatform === "darwin") {
97154
- if (/\bstate\s*=\s*running\b/i.test(output2)) return "running";
97155
- if (/\bstate\s*=\s*(not running|waiting|stopped)\b/i.test(output2)) return "stopped";
97156
- if (/\bpid\s*=\s*[1-9]\d*\b/i.test(output2)) return "running";
97157
- if (/could not find service/i.test(output2)) return "stopped";
97158
- return "unknown";
97159
- }
97160
- if (servicePlatform === "linux") {
97161
- const trimmed = output2.trim().toLowerCase();
97162
- if (trimmed === "active" || /\bactive\s*\(running\)/i.test(output2)) return "running";
97163
- if (trimmed === "activating" || trimmed === "reloading") return "starting";
97164
- return "unknown";
97165
- }
97166
- const stateMatch = output2.match(/\b(?:State|state)\s*[:=]\s*(\w+)/);
97167
- const state = (stateMatch?.[1] ?? output2.trim()).toLowerCase();
97168
- if (state === "running") return "running";
97169
- if (state === "ready" || state === "disabled" || state === "queued") return "stopped";
97170
- return "unknown";
97171
- }
97172
- function resolveEnsureDefinitionOutcome(input2) {
97173
- if (!input2.definitionChanged) {
97174
- return { changed: false, updatePending: false };
97175
- }
97176
- if (input2.hostState === "running" || input2.hostState === "starting") {
97177
- return { changed: true, updatePending: true };
97178
- }
97179
- return { changed: true, updatePending: false };
97180
- }
97181
- function shouldStartOnInstall(hostState) {
97182
- return hostState === "stopped";
97183
- }
97184
- function resolveUsername(options) {
97185
- if (options?.username) return options.username;
97186
- try {
97187
- return (0, import_node_os18.userInfo)().username;
97188
- } catch {
97189
- return process.env.USER || process.env.USERNAME || "unknown";
97190
- }
97191
- }
97192
- function buildLingerStatusCommand(username) {
97193
- return {
97194
- command: "loginctl",
97195
- args: ["show-user", username, "-p", "Linger"],
97196
- tolerateFailure: true
97197
- };
97198
- }
97199
- function assertSystemdUserLingerEnabled(options = {}) {
97200
- const username = resolveUsername(options);
97201
- const run = options.runCommand ?? runServiceCommand;
97202
- const result = run(buildLingerStatusCommand(username));
97203
- if (result.status !== 0) {
97204
- throw new Error(
97205
- `Cannot verify systemd user linger for ${username} (loginctl failed). For durable headless VM boot run: sudo loginctl enable-linger ${username}`
97206
- );
97207
- }
97208
- if (!/Linger=yes/i.test(result.output)) {
97209
- throw new Error(
97210
- `systemd user linger is disabled for ${username}. For durable headless VM boot run: sudo loginctl enable-linger ${username}`
97211
- );
97212
- }
97213
- }
97214
- function createHostContext(options = {}) {
97215
- const args = options.args ?? [];
97216
- const plan = currentServicePlan(args, options.runtime);
97217
- const run = options.runCommand ?? runServiceCommand;
97218
- const sleep4 = options.sleep ?? defaultSleep;
97219
- const nowMs = options.nowMs ?? Date.now;
97220
- const readManifest = options.readManifest ?? defaultReadManifest;
97221
- const writeManifest = options.writeManifest ?? writeManifestAtomic;
97222
- const removeManifest = options.removeManifest ?? ((path2) => (0, import_node_fs28.rmSync)(path2, { force: true }));
97223
- const ensureLogDir = options.ensureLogDir ?? (() => {
97224
- if (plan.logDirectory) (0, import_node_fs28.mkdirSync)(plan.logDirectory, { recursive: true, mode: 448 });
97225
- });
97226
- return {
97227
- args,
97228
- plan,
97229
- run,
97230
- sleep: sleep4,
97231
- nowMs,
97232
- readManifest,
97233
- writeManifest,
97234
- removeManifest,
97235
- ensureLogDir,
97236
- profile: resolveEndpointProfileFromArgs(args)
97237
- };
97238
- }
97239
- function inspectDaemonService(options = {}) {
97240
- const { plan, run, nowMs } = createHostContext(options);
97241
- const result = run(plan.isActiveCommand);
97242
- const state = parseServiceHostState(plan.platform, result);
97243
- return {
97244
- state,
97245
- platform: plan.platform,
97246
- detail: result.output || void 0,
97247
- observedAtMs: nowMs()
97248
- };
97249
- }
97250
- function ensureDaemonServiceDefinition(options = {}) {
97251
- const ctx = createHostContext(options);
97252
- const { plan, run, readManifest, writeManifest, ensureLogDir } = ctx;
97253
- let definitionChanged = false;
97254
- if (plan.manifestPath && plan.manifest) {
97255
- ensureLogDir();
97256
- const existing = readManifest(plan.manifestPath);
97257
- definitionChanged = existing !== plan.manifest;
97258
- if (definitionChanged) {
97259
- writeManifest(plan.manifestPath, plan.manifest);
97260
- }
97261
- } else if (plan.platform === "win32") {
97262
- definitionChanged = true;
97263
- }
97264
- const inspection = inspectDaemonService(options);
97265
- const outcome = resolveEnsureDefinitionOutcome({
97266
- definitionChanged,
97267
- hostState: inspection.state
97268
- });
97269
- if (outcome.updatePending) {
97270
- return outcome;
97271
- }
97272
- if (inspection.state === "running" || inspection.state === "starting") {
97273
- return outcome;
97274
- }
97275
- const definitionCommands = inspection.state === "stopped" ? plan.reloadDefinitionCommands : plan.loadDefinitionCommands;
97276
- for (const command of definitionCommands) run(command);
97277
- return outcome;
97278
- }
97279
- function enableDaemonService(options = {}) {
97280
- const { plan, run } = createHostContext(options);
97281
- for (const command of plan.enableCommands) run(command);
97282
- }
97283
- function startDaemonService(options = {}) {
97284
- const { plan, run } = createHostContext(options);
97285
- for (const command of plan.startCommands) run(command);
97286
- }
97287
- function uninstallDaemonService(args = [], options = {}) {
97288
- const ctx = createHostContext({ ...options, args: options.args ?? args });
97289
- for (const command of ctx.plan.uninstallCommands) ctx.run(command);
97290
- if (ctx.plan.manifestPath) ctx.removeManifest(ctx.plan.manifestPath);
97291
- console.info("[alan-agent] Per-user daemon service removed");
97292
- }
97293
- function installDaemonService(args = [], runtime, options = {}) {
97294
- const profile = resolveEndpointProfileFromArgs(args);
97295
- const configPath = resolveAgentConfigPath({ profile });
97296
- if (!configExistsAtPath(configPath)) {
97297
- throw new Error(`No ${profile} runtime is configured. Run alan-agent setup or login first.`);
97298
- }
97299
- const headless = options.headless ?? args.includes("--headless");
97300
- const merged = { ...options, args, runtime, headless };
97301
- const ctx = createHostContext(merged);
97302
- if (headless) {
97303
- if (ctx.plan.platform !== "linux") {
97304
- throw new Error("--headless is only supported on Linux systemd user services");
97305
- }
97306
- assertSystemdUserLingerEnabled(merged);
97307
- }
97308
- const ensured = ensureDaemonServiceDefinition(merged);
97309
- enableDaemonService(merged);
97310
- const inspection = inspectDaemonService(merged);
97311
- if (ensured.updatePending) {
97312
- console.info("[alan-agent] Daemon service definition update pending; leaving running service", {
97313
- profile,
97314
- state: inspection.state
97315
- });
97316
- return;
97317
- }
97318
- if (!shouldStartOnInstall(inspection.state)) {
97319
- console.info("[alan-agent] Per-user daemon service already present", {
97320
- profile,
97321
- state: inspection.state
97322
- });
97323
- return;
97324
- }
97325
- startDaemonService(merged);
97326
- console.info("[alan-agent] Per-user daemon service installed", { profile });
97327
- }
97328
- function printDaemonServiceStatus(args = []) {
97329
- const plan = currentServicePlan(args);
97330
- const result = runServiceCommand({ ...plan.statusCommand, tolerateFailure: true });
97331
- if (result.status !== 0) {
97332
- process.stdout.write(`unknown
97333
- ${result.output}
97334
- `);
97335
- return;
97336
- }
97337
- process.stdout.write(`${result.output}
97338
- `);
97339
- }
97340
-
97341
97593
  // src/skills/skill-cli.ts
97342
97594
  var import_promises6 = require("fs/promises");
97343
97595
  var import_node_os19 = require("os");
@@ -97536,13 +97788,7 @@ async function main() {
97536
97788
  return;
97537
97789
  }
97538
97790
  if (command === "install") {
97539
- bindConfigPath(commandArgs);
97540
- const configured = readConfig();
97541
- const configurationMode = resolveInstallConfigurationMode(commandArgs, configured);
97542
- const setup = configurationMode === "setup" ? await setupDaemon(commandArgs) : null;
97543
- if (configurationMode === "login") await loginWithDeviceCode(commandArgs);
97544
- if (!setup?.daemonAlreadyRunning) installDaemonService(commandArgs);
97545
- runMcpIntegrationCommand(["reconcile", ...commandArgs]);
97791
+ await runInstallCommand(commandArgs);
97546
97792
  return;
97547
97793
  }
97548
97794
  if (command === "login") {