@alan-ai-hq/agent-manager 0.1.91 → 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 +289 -53
  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.91";
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) {
@@ -42796,31 +42949,59 @@ function buildCodexEffortArgs(selectedEffortLevel, options) {
42796
42949
  }
42797
42950
  var CODEX_STARTUP_TIMEOUT_MS = 3e5;
42798
42951
  var ALAN_MCP_STARTUP_TIMEOUT_SEC = 30;
42799
- function buildCodexManagedMcpArgs(registration) {
42800
- const serverName = managedMcpServerName(registration);
42952
+ function buildCodexManagedMcpArgs(registration, options = {}) {
42801
42953
  return [
42802
- "--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"] : [],
42803
42957
  "-c",
42804
- `mcp_servers.${serverName}.url=${JSON.stringify(registration.url)}`,
42958
+ `mcp_servers.alan.url=${JSON.stringify(registration.url)}`,
42805
42959
  "-c",
42806
- `mcp_servers.${serverName}.env_http_headers.x-alan-run-reference=${JSON.stringify(ALAN_RUN_REFERENCE_ENV)}`,
42960
+ `mcp_servers.alan.env_http_headers.x-alan-run-reference=${JSON.stringify(ALAN_RUN_REFERENCE_ENV)}`,
42807
42961
  "-c",
42808
- `mcp_servers.${serverName}.required=true`,
42962
+ "mcp_servers.alan.required=true",
42809
42963
  "-c",
42810
- `mcp_servers.${serverName}.startup_timeout_sec=${ALAN_MCP_STARTUP_TIMEOUT_SEC}`
42964
+ `mcp_servers.alan.startup_timeout_sec=${ALAN_MCP_STARTUP_TIMEOUT_SEC}`
42811
42965
  ];
42812
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
+ }
42813
42987
  function createCodexRuntimeBackend(command = "codex", defaultArgs = []) {
42814
42988
  return {
42815
42989
  kind: "codex_app_server",
42816
42990
  supportTier: "structured",
42817
42991
  async run(context) {
42818
42992
  const managedRegistration = context.config.alanMcp;
42819
- 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
+ ] : [];
42820
43000
  const runContext = managedRegistration ? {
42821
43001
  ...context,
42822
43002
  env: {
42823
43003
  ...context.env,
43004
+ ...externalMcp?.env,
42824
43005
  [ALAN_RUN_REFERENCE_ENV]: requireManagedRunReference(managedRegistration)
42825
43006
  }
42826
43007
  } : context;
@@ -43781,6 +43962,13 @@ function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = [])
43781
43962
  context.cwd
43782
43963
  );
43783
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;
43784
43972
  try {
43785
43973
  return await createGenericCliBackend({
43786
43974
  kind: "cursor_agent_cli",
@@ -43858,7 +44046,7 @@ function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = [])
43858
44046
  return appendImagePathReferences(prompt, imageFiles);
43859
44047
  },
43860
44048
  parseStructuredLine: parseCursorStructuredLine
43861
- }).run(context);
44049
+ }).run(runContext);
43862
44050
  } finally {
43863
44051
  cleanup();
43864
44052
  }
@@ -44914,6 +45102,28 @@ function resolveKimiCliModelArg(model) {
44914
45102
  if (trimmed.startsWith("claude-")) return void 0;
44915
45103
  return trimmed;
44916
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
+ }
44917
45127
  function createKimiCliBackend(command = "kimi-cli", defaultArgs = []) {
44918
45128
  return {
44919
45129
  kind: "kimi_cli",
@@ -44925,14 +45135,9 @@ function createKimiCliBackend(command = "kimi-cli", defaultArgs = []) {
44925
45135
  if (registration && mcpConfigPath) {
44926
45136
  (0, import_fs9.writeFileSync)(
44927
45137
  mcpConfigPath,
44928
- JSON.stringify({
44929
- mcpServers: {
44930
- [managedMcpServerName(registration)]: {
44931
- url: registration.url,
44932
- headers: { "x-alan-run-reference": requireManagedRunReference(registration) }
44933
- }
44934
- }
44935
- }),
45138
+ JSON.stringify(
45139
+ buildKimiMcpConfig(registration, selectExternalMcpServers(context.config))
45140
+ ),
44936
45141
  { mode: 384 }
44937
45142
  );
44938
45143
  }
@@ -45459,7 +45664,7 @@ function leaseKeyFor(context) {
45459
45664
  if (!conversationId) return null;
45460
45665
  const runReference = context.config.alanMcp?.headers?.["x-alan-run-reference"];
45461
45666
  if (!runReference) return `conv:${conversationId}`;
45462
- 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);
45463
45668
  return `conv:${conversationId}|mcp:${fingerprint}`;
45464
45669
  }
45465
45670
  function isLeaseUsable(lease) {
@@ -46484,8 +46689,10 @@ var BaseMachineAgent = class _BaseMachineAgent {
46484
46689
  */
46485
46690
  async beginMcpConfigCriticalSection(backend, context) {
46486
46691
  const runHeaders = context.config.alanMcp?.headers;
46487
- if (!runHeaders || usesProcessLocalManagedMcp(backend.kind)) return () => {
46488
- };
46692
+ if (!runHeaders || usesProcessLocalManagedMcp(backend.kind) || deliversRunReferenceViaEnv(backend.kind)) {
46693
+ return () => {
46694
+ };
46695
+ }
46489
46696
  const releaseGate = await acquireMcpConfigWriteGate();
46490
46697
  try {
46491
46698
  const writtenClis = await syncAlanMcpSessionHeaders({
@@ -46498,7 +46705,11 @@ var BaseMachineAgent = class _BaseMachineAgent {
46498
46705
  void this.presenter.onLog(`Synced Alan MCP session headers to ${writtenClis.join(", ")}`);
46499
46706
  }
46500
46707
  } catch (error61) {
46501
- 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
+ );
46502
46713
  }
46503
46714
  return releaseGate;
46504
46715
  }
@@ -46993,7 +47204,7 @@ function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName, op
46993
47204
  const { width, height } = readImageDimensions2(buffer, mimeType);
46994
47205
  images.push({
46995
47206
  type: "generated_image",
46996
- id: (0, import_crypto6.randomUUID)(),
47207
+ id: (0, import_crypto7.randomUUID)(),
46997
47208
  filename: (0, import_path11.basename)(path2),
46998
47209
  mimeType,
46999
47210
  size: buffer.length,
@@ -47001,7 +47212,7 @@ function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName, op
47001
47212
  height,
47002
47213
  data: buffer.toString("base64"),
47003
47214
  sourcePath: path2,
47004
- sourceHash: (0, import_crypto6.createHash)("sha256").update(buffer).digest("hex"),
47215
+ sourceHash: (0, import_crypto7.createHash)("sha256").update(buffer).digest("hex"),
47005
47216
  toolId,
47006
47217
  ts: Date.now()
47007
47218
  });
@@ -47033,7 +47244,7 @@ function extractSessionMediaFromToolResult(toolId, content, cwd, toolName, optio
47033
47244
  media.push({
47034
47245
  type: "session_media",
47035
47246
  kind,
47036
- id: (0, import_crypto6.randomUUID)(),
47247
+ id: (0, import_crypto7.randomUUID)(),
47037
47248
  filename: (0, import_path11.basename)(path2),
47038
47249
  mimeType,
47039
47250
  size: buffer.length,
@@ -47041,7 +47252,7 @@ function extractSessionMediaFromToolResult(toolId, content, cwd, toolName, optio
47041
47252
  height: dimensions?.height,
47042
47253
  data: buffer.toString("base64"),
47043
47254
  sourcePath: path2,
47044
- sourceHash: (0, import_crypto6.createHash)("sha256").update(buffer).digest("hex"),
47255
+ sourceHash: (0, import_crypto7.createHash)("sha256").update(buffer).digest("hex"),
47045
47256
  toolId,
47046
47257
  ts: Date.now()
47047
47258
  });
@@ -56805,7 +57016,12 @@ async function ensureAlanMcpRegistered(backendKind, registration, home = (0, imp
56805
57016
  releaseLock();
56806
57017
  }
56807
57018
  }
56808
- 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
+ );
56809
57025
  if (!fileOk) {
56810
57026
  const issue2 = {
56811
57027
  code: "mcp_config_repair_required",
@@ -56979,7 +57195,8 @@ function hasUsableRegistrationCredential(headers) {
56979
57195
  const normalized = new Map(
56980
57196
  Object.entries(headers).map(([name, value2]) => [name.toLowerCase(), value2.trim()])
56981
57197
  );
56982
- 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"));
56983
57200
  }
56984
57201
  function readAlanMcpRegistration(path2) {
56985
57202
  return parseAlanMcpRegistration(path2, (0, import_node_fs16.readFileSync)(path2, "utf8"));
@@ -75271,6 +75488,7 @@ async function handleAgentExecute(ctx, payload) {
75271
75488
  prMeta: payload.prMeta,
75272
75489
  conversationId: payload.conversationId,
75273
75490
  alanMcp: payload.alanMcp,
75491
+ externalMcpServers: payload.externalMcpServers,
75274
75492
  taskId: payload.taskId ?? payload.taskMeta?.id,
75275
75493
  teamId: payload.teamId,
75276
75494
  currentUser: payload.currentUser,
@@ -96809,6 +97027,7 @@ var WSClient = class {
96809
97027
  providerSessionId: data.providerSessionId,
96810
97028
  resumeFallbackContext: data.resumeFallbackContext,
96811
97029
  backendKind: data.backendKind,
97030
+ providerApiKey: data.providerApiKey,
96812
97031
  agentId: data.agentId,
96813
97032
  agentPrompt: data.agentPrompt,
96814
97033
  selectedModel: data.selectedModel,
@@ -96826,7 +97045,10 @@ var WSClient = class {
96826
97045
  cloudEnvironmentId: data.cloudEnvironmentId,
96827
97046
  conversationId: data.conversationId,
96828
97047
  teamId: data.teamId,
96829
- currentUser: data.currentUser
97048
+ currentUser: data.currentUser,
97049
+ terminalContextRefs: data.terminalContextRefs,
97050
+ terminalSnapshots: data.terminalSnapshots,
97051
+ externalMcpServers: data.externalMcpServers
96830
97052
  };
96831
97053
  const acceptDelivery = () => {
96832
97054
  if (data.messageId) this.rememberAcceptedMessageId(data.messageId);
@@ -96850,6 +97072,9 @@ var WSClient = class {
96850
97072
  this.socket.on("stop", () => {
96851
97073
  callbacks.onStop();
96852
97074
  });
97075
+ this.socket.on("agent.abort", (data) => {
97076
+ callbacks.onAbort?.({ runId: data?.runId, reason: data?.reason });
97077
+ });
96853
97078
  this.socket.on("tool_response", (data) => {
96854
97079
  if (data?.toolId && typeof data.response === "string") {
96855
97080
  callbacks.onToolResponse?.(data.toolId, data.response);
@@ -96907,6 +97132,7 @@ async function runSandbox(config2) {
96907
97132
  let currentTaskMeta;
96908
97133
  let currentPrMeta;
96909
97134
  let currentWorkflowTools;
97135
+ let currentExternalMcpServers;
96910
97136
  let currentMaxIterations = config2.maxIterations ?? 50;
96911
97137
  let currentTaskId = config2.taskId;
96912
97138
  let currentConversationId = config2.sessionId;
@@ -97001,6 +97227,14 @@ async function runSandbox(config2) {
97001
97227
  lifecycle.info("sandbox_agent_stop_received");
97002
97228
  stopActiveAgent();
97003
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
+ },
97004
97238
  onToolResponse: (toolId, response) => {
97005
97239
  if (currentAgent) {
97006
97240
  const sent = currentAgent.sendToolResponse(toolId, response);
@@ -97197,6 +97431,7 @@ async function runSandbox(config2) {
97197
97431
  resumeFallbackContext,
97198
97432
  taskMeta: currentTaskMeta,
97199
97433
  prMeta: currentPrMeta,
97434
+ externalMcpServers: currentExternalMcpServers,
97200
97435
  taskId: currentTaskId,
97201
97436
  conversationId: currentConversationId,
97202
97437
  alanMcp: currentAlanMcp,
@@ -97314,6 +97549,7 @@ async function runSandbox(config2) {
97314
97549
  activeMode = nextPayload.mode;
97315
97550
  }
97316
97551
  currentAlanMcp = nextPayload.alanMcp;
97552
+ currentExternalMcpServers = nextPayload.externalMcpServers;
97317
97553
  if (nextPayload.taskMeta !== void 0) {
97318
97554
  currentTaskMeta = nextPayload.taskMeta;
97319
97555
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alan-ai-hq/agent-manager",
3
- "version": "0.1.91",
3
+ "version": "0.1.92",
4
4
  "type": "module",
5
5
  "description": "Alan agent runtime — cloud sandbox and local daemon (alan-agent CLI)",
6
6
  "bin": {
@@ -27,8 +27,8 @@
27
27
  "tsx": "^4.19.0",
28
28
  "typescript": "~5.9.3",
29
29
  "@alan-ai/agent-core": "0.1.0",
30
- "@alan-ai/sandbox-runtime-spec": "0.1.0",
31
- "@alan-ai/shared": "0.1.0"
30
+ "@alan-ai/shared": "0.1.0",
31
+ "@alan-ai/sandbox-runtime-spec": "0.1.0"
32
32
  },
33
33
  "scripts": {
34
34
  "build": "tsup",