@alan-ai-hq/agent-manager-linux-arm64 0.1.157 → 0.1.158

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 (3) hide show
  1. package/alan-agent.cjs +353 -177
  2. package/alan-holder.cjs +323 -174
  3. package/package.json +1 -1
package/alan-holder.cjs CHANGED
@@ -23279,6 +23279,7 @@ If a needed tool is not exposed, use the host's tool-discovery mechanism once wh
23279
23279
  Whatever the intent, check your draft before sending: three or more numbers being compared, several items sharing the same attributes, a sequence, or two or more options means it should be visual. Stay in plain text for one-line facts, yes/no answers, clarifying questions, conversation, and short summaries of code you just changed. Lead with one sentence, show one visual (two at most), then stop; the visual carries the detail, so do not repeat it in prose.
23280
23280
  - Examples of plain questions with visual answers: "Which queue should the importer use, BullMQ or SQS?" \u2192 a one-line recommendation, then \`\`\`openui Tabs with a KeyValue of cost, latency and ops effort per option and a Callout with the pick. "Why did last night's deploy fail?" \u2192 a one-sentence cause, then Steps with the failed step and its detail, then Code with the error. "How's the test suite doing?" \u2192 a row of Stat (passed, failed, flaky) and a line \`\`\`chart of failures per day.
23281
23281
  - \`\`\`mermaid renders in chat, documents, plans and artifacts too; \`\`\`openui renders only in your replies in the Alan chat. In Slack or Teams messages, commit messages, pull request bodies and files, use plain Markdown instead.
23282
+ - When you compose a plan, test report, review or document to save as an Alan artifact, use the \`alan-artifact\` skill for its structure and any HTML or interactive block, and \`alan-diagrams\` for its Mermaid diagrams.
23282
23283
  - \`\`\`chart renders in chat, documents, plans and artifacts, and is **strict JSON, nothing else**:
23283
23284
  \`\`\`chart
23284
23285
  { "type": "bar", "title": "Quarterly Revenue", "data": [{ "name": "Q1", "revenue": 4000 }, { "name": "Q2", "revenue": 3000 }] }
@@ -23325,7 +23326,7 @@ Team
23325
23326
  - When a user asks to create a Skill, default to a team-owned Alan platform Skill through \`alan-skill-authoring\`, whether this session runs locally or in a cloud sandbox. Use a repository or filesystem Skill only when the user explicitly requests that scope. Do not infer repository scope from the current checkout or from a procedure that mentions repository files. If repeated work suggests a reusable Skill, offer the platform Skill and its evidence; do not publish it without the approval required by the authoring workflow.
23326
23327
  - Own mistakes. Wrong ID, jumped too fast \u2014 say so and fix it.
23327
23328
  - When writing code, work like a senior engineer: understand the existing design and conventions, make the smallest coherent change that solves the request, and keep related logic together. Reuse an existing abstraction when it fits; remove duplication that the change would otherwise introduce, but do not refactor unrelated code or add speculative abstractions. Keep names, types, error handling, and tests clear enough for the next engineer to maintain. Review the diff for accidental changes before calling the work done.
23328
- - Write for a human reader. Task, subtask and epic descriptions, documents, artifacts and comments are read by people in the app, not parsed by agents. Lead with the outcome in one sentence, then short sections with headings and bullet lists. Spell out internal shorthand, and keep commit hashes, IDs and migration numbers out of sentences \u2014 put them in a trailing "References" list when they matter.
23329
+ - Write for a human reader. Task, subtask and epic descriptions, documents, artifacts and comments are read by people in the app, not parsed by agents. Lead with the outcome in one sentence, then short sections with headings and bullet lists when there is enough to organise. Size it to what is actually known: a task written from someone's request restates that request and never adds repro steps, causes, verification or acceptance criteria nobody stated. Spell out internal shorthand, and keep commit hashes, IDs and migration numbers out of sentences \u2014 put them in a trailing "References" list when they matter.
23329
23330
  ${ALAN_VISUAL_PROMPT}
23330
23331
  - Images you generate or edit appear in the chat automatically, the way ChatGPT shows them. Do not link, embed, or print the generated file's path afterwards, and do not announce "Generated:" or "Edited image:" \u2014 at most one short sentence about the result. Mention a path only when the user asked for the file to be saved into the project.
23331
23332
 
@@ -25201,6 +25202,13 @@ var init_read = __esm({
25201
25202
  }
25202
25203
  });
25203
25204
 
25205
+ // ../shared/dist/agent/acp/session-activity.js
25206
+ var init_session_activity = __esm({
25207
+ "../shared/dist/agent/acp/session-activity.js"() {
25208
+ "use strict";
25209
+ }
25210
+ });
25211
+
25204
25212
  // ../shared/dist/agent/acp/transcript-byte-decoder.js
25205
25213
  var init_transcript_byte_decoder = __esm({
25206
25214
  "../shared/dist/agent/acp/transcript-byte-decoder.js"() {
@@ -25232,6 +25240,7 @@ var init_acp = __esm({
25232
25240
  init_types3();
25233
25241
  init_read();
25234
25242
  init_reduce();
25243
+ init_session_activity();
25235
25244
  init_transcript_byte_decoder();
25236
25245
  init_transcript_fold();
25237
25246
  init_transcript_http();
@@ -26104,7 +26113,7 @@ var init_chunk_ZCTKXKYY = __esm({
26104
26113
  }
26105
26114
  });
26106
26115
 
26107
- // ../agent-core/dist/chunk-ILZBXGVI.js
26116
+ // ../agent-core/dist/chunk-FYZKVT3E.js
26108
26117
  function detectImageMimeType(buffer) {
26109
26118
  if (buffer.length >= 8 && buffer.subarray(0, 8).equals(PNG_SIGNATURE)) return "image/png";
26110
26119
  if (buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) {
@@ -26201,21 +26210,38 @@ function imageDimensions(buffer, mimeType) {
26201
26210
  if (mimeType === "image/jpeg") return jpegDimensions(buffer);
26202
26211
  return { width: null, height: null };
26203
26212
  }
26204
- function readMediaFiles(candidates2, cwd, mimeByExt, maxBytes) {
26213
+ function readMediaFiles(query) {
26205
26214
  const seen = /* @__PURE__ */ new Set();
26206
26215
  const files = [];
26207
- for (const candidate of candidates2) {
26208
- const path = normalizePath(candidate, cwd);
26216
+ for (const candidate of query.candidates) {
26217
+ const path = normalizePath(candidate, query.cwd);
26209
26218
  if (!path || seen.has(path) || !(0, import_fs5.existsSync)(path)) continue;
26210
26219
  seen.add(path);
26211
- const mimeType = mimeByExt[(0, import_path4.extname)(path).toLowerCase()];
26220
+ const mimeType = query.mimeByExt[(0, import_path4.extname)(path).toLowerCase()];
26212
26221
  if (!mimeType) continue;
26213
26222
  const stat15 = (0, import_fs5.statSync)(path);
26214
- if (!stat15.isFile() || stat15.size <= 0 || stat15.size > maxBytes) continue;
26223
+ if (!stat15.isFile() || stat15.size <= 0 || stat15.size > query.maxBytes) continue;
26224
+ if (stat15.mtimeMs < query.writtenSince && !query.named.has(path)) continue;
26215
26225
  files.push({ path, mimeType, buffer: (0, import_fs5.readFileSync)(path) });
26216
26226
  }
26217
26227
  return files;
26218
26228
  }
26229
+ function isCaptureTool(tool) {
26230
+ return isBrowserScreenshotToolName(tool.name) || isBrowserRecordingToolName(tool.name);
26231
+ }
26232
+ function runsCaptureCommand(tool) {
26233
+ return someString(tool.input, (text2) => AGENT_BROWSER_CAPTURE_COMMAND.test(text2));
26234
+ }
26235
+ function capturePaths(tool, content, extensions, cwd) {
26236
+ const values = isCaptureTool(tool) ? contentStrings(content, tool.input) : runsCaptureCommand(tool) ? contentStrings("", tool.input) : [];
26237
+ return pathCandidates(values, extensions).flatMap((candidate) => {
26238
+ const path = normalizePath(candidate, cwd);
26239
+ return path ? [path] : [];
26240
+ });
26241
+ }
26242
+ function base64Length(bytes) {
26243
+ return Math.ceil(bytes / 3) * 4;
26244
+ }
26219
26245
  function resultText(update) {
26220
26246
  const only2 = isJsonRecord(update._meta) ? update._meta[TOOL_RESULT_ONLY_META] : void 0;
26221
26247
  if (typeof only2 === "string") return only2;
@@ -26506,9 +26532,9 @@ function silentStartError(name, timeoutMs) {
26506
26532
  message: `${name} printed nothing for ${seconds}s after it started and was stopped. Send your message again.`
26507
26533
  };
26508
26534
  }
26509
- var import_crypto4, import_fs5, import_path4, PNG_SIGNATURE, IMAGE_MIME_BY_EXT, VIDEO_MIME_BY_EXT, IMAGE_EXTENSIONS, VIDEO_EXTENSIONS, MAX_IMAGE_BYTES, MAX_VIDEO_BYTES, AGENT_BROWSER_MENTION, LocalMediaReader, SessionOpenRefusal, JsonRpcError, ACP_PROTOCOL_VERSION, ACP_ERROR, STDERR_TAIL_LINES, RESULT_EXIT_GRACE_MS, PerTurnSession;
26510
- var init_chunk_ILZBXGVI = __esm({
26511
- "../agent-core/dist/chunk-ILZBXGVI.js"() {
26535
+ var import_crypto4, import_fs5, import_path4, PNG_SIGNATURE, IMAGE_MIME_BY_EXT, VIDEO_MIME_BY_EXT, IMAGE_EXTENSIONS, VIDEO_EXTENSIONS, MAX_IMAGE_BYTES, MAX_VIDEO_BYTES, MAX_TURN_MEDIA_DATA_CHARS, MTIME_SLACK_MS, AGENT_BROWSER_MENTION, AGENT_BROWSER_CAPTURE_COMMAND, LocalMediaReader, SessionOpenRefusal, JsonRpcError, ACP_PROTOCOL_VERSION, ACP_ERROR, STDERR_TAIL_LINES, RESULT_EXIT_GRACE_MS, PerTurnSession;
26536
+ var init_chunk_FYZKVT3E = __esm({
26537
+ "../agent-core/dist/chunk-FYZKVT3E.js"() {
26512
26538
  "use strict";
26513
26539
  init_chunk_OUEMTEUX();
26514
26540
  init_chunk_ZCTKXKYY();
@@ -26536,12 +26562,16 @@ var init_chunk_ILZBXGVI = __esm({
26536
26562
  VIDEO_EXTENSIONS = "mp4|webm|mov";
26537
26563
  MAX_IMAGE_BYTES = 20 * 1024 * 1024;
26538
26564
  MAX_VIDEO_BYTES = 100 * 1024 * 1024;
26565
+ MAX_TURN_MEDIA_DATA_CHARS = 8 * 1024 * 1024;
26566
+ MTIME_SLACK_MS = 2e3;
26539
26567
  AGENT_BROWSER_MENTION = /agent[-_]browser/i;
26568
+ AGENT_BROWSER_CAPTURE_COMMAND = /(?:^|[\s/])agent[-_]browser(?:@[^\s;&|]+)?(?:\s[^\n;&|]*?)?\s(?:screenshot|record)(?=\s|$)/im;
26540
26569
  LocalMediaReader = class {
26541
26570
  #cwd;
26542
26571
  #clock;
26543
26572
  #tools = /* @__PURE__ */ new Map();
26544
26573
  #pathHints = /* @__PURE__ */ new Set();
26574
+ #mediaDataChars = 0;
26545
26575
  constructor(cwd, clock) {
26546
26576
  this.#cwd = cwd;
26547
26577
  this.#clock = clock;
@@ -26566,7 +26596,11 @@ var init_chunk_ILZBXGVI = __esm({
26566
26596
  /** A recording names its output file when it starts; the stop's result may not repeat it. */
26567
26597
  #announce(toolCallId, name, input2) {
26568
26598
  const known = this.#tools.get(toolCallId);
26569
- const tool = { name: name ?? known?.name ?? "", input: input2 ?? known?.input };
26599
+ const tool = {
26600
+ name: name ?? known?.name ?? "",
26601
+ input: input2 ?? known?.input,
26602
+ startedAt: known?.startedAt ?? this.#clock.now()
26603
+ };
26570
26604
  this.#tools.set(toolCallId, tool);
26571
26605
  if (!isAgentBrowserMedia(tool, "", "video")) return;
26572
26606
  const cwd = toolCwd(this.#cwd, tool.input);
@@ -26575,13 +26609,30 @@ var init_chunk_ILZBXGVI = __esm({
26575
26609
  if (path) this.#pathHints.add(path);
26576
26610
  }
26577
26611
  }
26578
- #payloadFields(file2, toolId) {
26612
+ /** Media written before the call started is old, unless its capture command named it. */
26613
+ #toolFiles(tool, content, extensions, limits) {
26614
+ const cwd = toolCwd(this.#cwd, tool.input);
26615
+ return readMediaFiles({
26616
+ ...limits,
26617
+ candidates: [
26618
+ ...pathCandidates(contentStrings(content, tool.input), extensions),
26619
+ ...this.#pathHints
26620
+ ],
26621
+ cwd,
26622
+ writtenSince: tool.startedAt - MTIME_SLACK_MS,
26623
+ named: /* @__PURE__ */ new Set([...capturePaths(tool, content, extensions, cwd), ...this.#pathHints])
26624
+ });
26625
+ }
26626
+ #payloadFields(file2, toolId, explicit) {
26627
+ const dataChars = base64Length(file2.buffer.length);
26628
+ const inline = explicit || this.#mediaDataChars + dataChars <= MAX_TURN_MEDIA_DATA_CHARS;
26629
+ if (inline) this.#mediaDataChars += dataChars;
26579
26630
  return {
26580
26631
  id: (0, import_crypto4.randomUUID)(),
26581
26632
  filename: (0, import_path4.basename)(file2.path),
26582
26633
  mimeType: file2.mimeType,
26583
26634
  size: file2.buffer.length,
26584
- data: file2.buffer.toString("base64"),
26635
+ ...inline ? { data: file2.buffer.toString("base64") } : { inlineDataOmitted: true },
26585
26636
  sourcePath: file2.path,
26586
26637
  sourceHash: (0, import_crypto4.createHash)("sha256").update(file2.buffer).digest("hex"),
26587
26638
  toolId,
@@ -26590,15 +26641,14 @@ var init_chunk_ILZBXGVI = __esm({
26590
26641
  }
26591
26642
  #generatedImages(toolId, tool, content) {
26592
26643
  if (!isGeneratedImageToolName(tool.name)) return [];
26593
- const candidates2 = [
26594
- ...pathCandidates(contentStrings(content, tool.input), IMAGE_EXTENSIONS),
26595
- ...this.#pathHints
26596
- ];
26597
- const cwd = toolCwd(this.#cwd, tool.input);
26598
- return readMediaFiles(candidates2, cwd, IMAGE_MIME_BY_EXT, MAX_IMAGE_BYTES).map((file2) => {
26644
+ const files = this.#toolFiles(tool, content, IMAGE_EXTENSIONS, {
26645
+ mimeByExt: IMAGE_MIME_BY_EXT,
26646
+ maxBytes: MAX_IMAGE_BYTES
26647
+ });
26648
+ return files.map((file2) => {
26599
26649
  const image = {
26600
26650
  type: "generated_image",
26601
- ...this.#payloadFields(file2, toolId),
26651
+ ...this.#payloadFields(file2, toolId, true),
26602
26652
  ...imageDimensions(file2.buffer, file2.mimeType)
26603
26653
  };
26604
26654
  return { sessionUpdate: "_alan/generated_image", image };
@@ -26608,25 +26658,19 @@ var init_chunk_ILZBXGVI = __esm({
26608
26658
  const isVideo = isAgentBrowserMedia(tool, content, "video");
26609
26659
  if (!isVideo && !isAgentBrowserMedia(tool, content, "image")) return [];
26610
26660
  const kind = isVideo ? "video" : "image";
26611
- const candidates2 = [
26612
- ...pathCandidates(
26613
- contentStrings(content, tool.input),
26614
- isVideo ? VIDEO_EXTENSIONS : IMAGE_EXTENSIONS
26615
- ),
26616
- ...this.#pathHints
26617
- ];
26618
- const files = readMediaFiles(
26619
- candidates2,
26620
- toolCwd(this.#cwd, tool.input),
26621
- isVideo ? VIDEO_MIME_BY_EXT : IMAGE_MIME_BY_EXT,
26622
- isVideo ? MAX_VIDEO_BYTES : MAX_IMAGE_BYTES
26661
+ const explicit = isCaptureTool(tool) || runsCaptureCommand(tool);
26662
+ const files = this.#toolFiles(
26663
+ tool,
26664
+ content,
26665
+ isVideo ? VIDEO_EXTENSIONS : IMAGE_EXTENSIONS,
26666
+ isVideo ? { mimeByExt: VIDEO_MIME_BY_EXT, maxBytes: MAX_VIDEO_BYTES } : { mimeByExt: IMAGE_MIME_BY_EXT, maxBytes: MAX_IMAGE_BYTES }
26623
26667
  );
26624
26668
  return files.map((file2) => {
26625
26669
  this.#pathHints.delete(file2.path);
26626
26670
  const media = {
26627
26671
  type: "session_media",
26628
26672
  kind,
26629
- ...this.#payloadFields(file2, toolId),
26673
+ ...this.#payloadFields(file2, toolId, explicit),
26630
26674
  ...kind === "image" ? imageDimensions(file2.buffer, file2.mimeType) : {}
26631
26675
  };
26632
26676
  return { sessionUpdate: "_alan/session_media", media };
@@ -26858,128 +26902,6 @@ var init_chunk_ILZBXGVI = __esm({
26858
26902
  }
26859
26903
  });
26860
26904
 
26861
- // ../agent-core/dist/chunk-J2KTV5FP.js
26862
- function selectExternalMcpServers(spec) {
26863
- const isWorkflow = Boolean(spec.workflowId || spec.workflowExecutionId);
26864
- const filter = isWorkflow ? createWorkflowToolsFilter(spec.workflowTools) : null;
26865
- if (filter && !filter.hasFilter) return {};
26866
- const selected = {};
26867
- for (const [name, server] of Object.entries(spec.externalMcpServers ?? {})) {
26868
- if (!isExternalMcpServerName(name)) continue;
26869
- if (filter && !filter.isMcpServerAllowed(name)) continue;
26870
- selected[name] = server;
26871
- }
26872
- return selected;
26873
- }
26874
- function headerEnvName(server, header) {
26875
- const fingerprint = (0, import_crypto5.createHash)("sha256").update(`${server}\0${header}`).digest("hex").slice(0, 12);
26876
- return `ALAN_MCP_HEADER_${fingerprint.toUpperCase()}`;
26877
- }
26878
- function deliverRemote(name, server, env) {
26879
- const headerEnv = {};
26880
- for (const [header, value] of Object.entries(server.headers ?? {})) {
26881
- if (!isMcpConfigKey(header)) continue;
26882
- const variable = headerEnvName(name, header);
26883
- env[variable] = value;
26884
- headerEnv[header] = variable;
26885
- }
26886
- return {
26887
- type: server.type === "sse" ? "sse" : "http",
26888
- url: server.url,
26889
- ...Object.keys(headerEnv).length ? { headerEnv } : {}
26890
- };
26891
- }
26892
- function deliverStdio(server, cliEnv, delivery) {
26893
- const envKeys = [];
26894
- for (const [key, value] of Object.entries(server.env ?? {})) {
26895
- if (!isMcpConfigKey(key)) continue;
26896
- if (key in cliEnv || key in delivery.env && delivery.env[key] !== value) {
26897
- delivery.skippedEnvKeys.push(key);
26898
- continue;
26899
- }
26900
- delivery.env[key] = value;
26901
- envKeys.push(key);
26902
- }
26903
- return {
26904
- type: "stdio",
26905
- command: server.command,
26906
- ...server.args?.length ? { args: server.args } : {},
26907
- ...envKeys.length ? { envKeys } : {}
26908
- };
26909
- }
26910
- function planExternalMcpDelivery(spec, cliEnv) {
26911
- const delivery = { servers: {}, env: {}, skippedEnvKeys: [] };
26912
- for (const [name, server] of Object.entries(selectExternalMcpServers(spec))) {
26913
- const { url: url2, command } = server;
26914
- if (url2 && server.type !== "stdio") {
26915
- delivery.servers[name] = deliverRemote(name, { ...server, url: url2 }, delivery.env);
26916
- } else if (command) {
26917
- delivery.servers[name] = deliverStdio({ ...server, command }, cliEnv, delivery);
26918
- }
26919
- }
26920
- return delivery;
26921
- }
26922
- function envReference(name) {
26923
- return `\${${name}}`;
26924
- }
26925
- function managedMcpHeaders(identity) {
26926
- if (identity.scope === "run") {
26927
- return {
26928
- Authorization: envReference(ALAN_MCP_AUTHORIZATION_ENV),
26929
- [MANAGED_MCP_RUN_HEADER]: envReference(ALAN_RUN_ID_ENV)
26930
- };
26931
- }
26932
- return {
26933
- ...identity.authorization === void 0 ? {} : { Authorization: identity.authorization },
26934
- [MANAGED_MCP_CONVERSATION_HEADER]: identity.conversationId
26935
- };
26936
- }
26937
- function credentialDigest(authorization) {
26938
- if (authorization === void 0) return void 0;
26939
- return (0, import_crypto6.createHash)("sha256").update(authorization).digest("hex").slice(0, 16);
26940
- }
26941
- function canonicalizeManagedMcpServerName(serverName) {
26942
- return RUN_SCOPED_SERVER.test(serverName) ? MANAGED_MCP_SERVER_NAME : serverName;
26943
- }
26944
- function canonicalizeManagedMcpToolName(toolName) {
26945
- const namespaced = NAMESPACED_RUN_TOOL.exec(toolName);
26946
- if (namespaced) return `mcp__${MANAGED_MCP_SERVER_NAME}__${namespaced[2]}`;
26947
- const flattened = FLATTENED_RUN_TOOL.exec(toolName);
26948
- return flattened ? `mcp__${MANAGED_MCP_SERVER_NAME}__${flattened[1]}` : toolName;
26949
- }
26950
- async function writeSecretFile(name, contents) {
26951
- const dir = await (0, import_promises14.mkdtemp)((0, import_path5.join)((0, import_os2.tmpdir)(), "alan-mcp-"));
26952
- const path = (0, import_path5.join)(dir, name);
26953
- const remove = async () => {
26954
- await (0, import_promises14.rm)(dir, { recursive: true, force: true });
26955
- };
26956
- try {
26957
- await (0, import_promises14.writeFile)(path, contents, { mode: 384, flag: "wx" });
26958
- } catch (error62) {
26959
- await remove();
26960
- throw error62;
26961
- }
26962
- return { path, remove, [Symbol.asyncDispose]: remove };
26963
- }
26964
- var import_crypto5, import_crypto6, import_promises14, import_os2, import_path5, MANAGED_MCP_SERVER_NAME, RUN_SCOPED_SERVER, NAMESPACED_RUN_TOOL, FLATTENED_RUN_TOOL;
26965
- var init_chunk_J2KTV5FP = __esm({
26966
- "../agent-core/dist/chunk-J2KTV5FP.js"() {
26967
- "use strict";
26968
- import_crypto5 = require("crypto");
26969
- init_agent2();
26970
- import_crypto6 = require("crypto");
26971
- init_agent2();
26972
- init_runtime_v2();
26973
- import_promises14 = require("fs/promises");
26974
- import_os2 = require("os");
26975
- import_path5 = require("path");
26976
- MANAGED_MCP_SERVER_NAME = "alan";
26977
- RUN_SCOPED_SERVER = /^alan-run-[a-f0-9]{16}$/;
26978
- NAMESPACED_RUN_TOOL = /^mcp__(alan-run-[a-f0-9]{16})__(.+)$/;
26979
- FLATTENED_RUN_TOOL = /^alan-run-[a-f0-9]{16}_(.+)$/;
26980
- }
26981
- });
26982
-
26983
26905
  // ../agent-core/dist/chunk-DLVRNIY5.js
26984
26906
  function repositories(input2, context) {
26985
26907
  const problems = new Map(input2.problems.map((problem) => [problem.path, problem]));
@@ -27790,6 +27712,131 @@ var init_chunk_DLVRNIY5 = __esm({
27790
27712
  }
27791
27713
  });
27792
27714
 
27715
+ // ../agent-core/dist/chunk-OSECDZA5.js
27716
+ function selectExternalMcpServers(spec) {
27717
+ const isWorkflow = Boolean(spec.workflowId || spec.workflowExecutionId);
27718
+ const filter = isWorkflow ? createWorkflowToolsFilter(spec.workflowTools) : null;
27719
+ if (filter && !filter.hasFilter) return {};
27720
+ const selected = {};
27721
+ for (const [name, server] of Object.entries(spec.externalMcpServers ?? {})) {
27722
+ if (!isExternalMcpServerName(name)) continue;
27723
+ if (filter && !filter.isMcpServerAllowed(name)) continue;
27724
+ selected[name] = server;
27725
+ }
27726
+ return selected;
27727
+ }
27728
+ function headerEnvName(server, header) {
27729
+ const fingerprint = (0, import_crypto5.createHash)("sha256").update(`${server}\0${header}`).digest("hex").slice(0, 12);
27730
+ return `ALAN_MCP_HEADER_${fingerprint.toUpperCase()}`;
27731
+ }
27732
+ function deliverRemote(name, server, env) {
27733
+ const headerEnv = {};
27734
+ for (const [header, value] of Object.entries(server.headers ?? {})) {
27735
+ if (!isMcpConfigKey(header)) continue;
27736
+ const variable = headerEnvName(name, header);
27737
+ env[variable] = value;
27738
+ headerEnv[header] = variable;
27739
+ }
27740
+ return {
27741
+ type: server.type === "sse" ? "sse" : "http",
27742
+ url: server.url,
27743
+ ...Object.keys(headerEnv).length ? { headerEnv } : {}
27744
+ };
27745
+ }
27746
+ function deliverStdio(server, cliEnv, delivery) {
27747
+ const envKeys = [];
27748
+ for (const [key, value] of Object.entries(server.env ?? {})) {
27749
+ if (!isMcpConfigKey(key)) continue;
27750
+ if (key in cliEnv || key in delivery.env && delivery.env[key] !== value) {
27751
+ delivery.skippedEnvKeys.push(key);
27752
+ continue;
27753
+ }
27754
+ delivery.env[key] = value;
27755
+ envKeys.push(key);
27756
+ }
27757
+ return {
27758
+ type: "stdio",
27759
+ command: server.command,
27760
+ ...server.args?.length ? { args: server.args } : {},
27761
+ ...envKeys.length ? { envKeys } : {}
27762
+ };
27763
+ }
27764
+ function planExternalMcpDelivery(spec, cliEnv) {
27765
+ const delivery = { servers: {}, env: {}, skippedEnvKeys: [] };
27766
+ for (const [name, server] of Object.entries(selectExternalMcpServers(spec))) {
27767
+ const { url: url2, command } = server;
27768
+ if (url2 && server.type !== "stdio") {
27769
+ delivery.servers[name] = deliverRemote(name, { ...server, url: url2 }, delivery.env);
27770
+ } else if (command) {
27771
+ delivery.servers[name] = deliverStdio({ ...server, command }, cliEnv, delivery);
27772
+ }
27773
+ }
27774
+ return delivery;
27775
+ }
27776
+ function envReference(name) {
27777
+ return `\${${name}}`;
27778
+ }
27779
+ function managedMcpHeaders(identity) {
27780
+ if (identity.scope === "run") {
27781
+ return {
27782
+ Authorization: envReference(ALAN_MCP_AUTHORIZATION_ENV),
27783
+ [MANAGED_MCP_RUN_HEADER]: envReference(ALAN_RUN_ID_ENV)
27784
+ };
27785
+ }
27786
+ return {
27787
+ ...identity.authorization === void 0 ? {} : { Authorization: identity.authorization },
27788
+ [MANAGED_MCP_CONVERSATION_HEADER]: identity.conversationId
27789
+ };
27790
+ }
27791
+ function credentialDigest(authorization) {
27792
+ if (authorization === void 0) return void 0;
27793
+ return (0, import_crypto6.createHash)("sha256").update(authorization).digest("hex").slice(0, 16);
27794
+ }
27795
+ function canonicalizeManagedMcpServerName(serverName) {
27796
+ return RUN_SCOPED_SERVER.test(serverName) ? MANAGED_MCP_SERVER_NAME : serverName;
27797
+ }
27798
+ function canonicalizeManagedMcpToolName(toolName) {
27799
+ const namespaced = NAMESPACED_RUN_TOOL.exec(toolName);
27800
+ if (namespaced) return `mcp__${MANAGED_MCP_SERVER_NAME}__${namespaced[2]}`;
27801
+ const flattened = FLATTENED_RUN_TOOL.exec(toolName);
27802
+ return flattened ? `mcp__${MANAGED_MCP_SERVER_NAME}__${flattened[1]}` : toolName;
27803
+ }
27804
+ async function writeSecretFile(name, contents) {
27805
+ const dir = await (0, import_promises14.mkdtemp)((0, import_path5.join)((0, import_os2.tmpdir)(), "alan-mcp-"));
27806
+ const path = (0, import_path5.join)(dir, name);
27807
+ const remove = async () => {
27808
+ await (0, import_promises14.rm)(dir, { recursive: true, force: true });
27809
+ };
27810
+ try {
27811
+ await (0, import_promises14.writeFile)(path, contents, { mode: 384, flag: "wx" });
27812
+ } catch (error62) {
27813
+ await remove();
27814
+ throw error62;
27815
+ }
27816
+ return { path, remove, [Symbol.asyncDispose]: remove };
27817
+ }
27818
+ var import_crypto5, import_crypto6, import_promises14, import_os2, import_path5, OPENCODE_CODE_MODE_ENV, OPENCODE_CODE_MODE_TOOL, CODE_MODE_CALLS_META, MANAGED_MCP_SERVER_NAME, RUN_SCOPED_SERVER, NAMESPACED_RUN_TOOL, FLATTENED_RUN_TOOL;
27819
+ var init_chunk_OSECDZA5 = __esm({
27820
+ "../agent-core/dist/chunk-OSECDZA5.js"() {
27821
+ "use strict";
27822
+ import_crypto5 = require("crypto");
27823
+ init_agent2();
27824
+ import_crypto6 = require("crypto");
27825
+ init_agent2();
27826
+ init_runtime_v2();
27827
+ import_promises14 = require("fs/promises");
27828
+ import_os2 = require("os");
27829
+ import_path5 = require("path");
27830
+ OPENCODE_CODE_MODE_ENV = "OPENCODE_EXPERIMENTAL_CODE_MODE";
27831
+ OPENCODE_CODE_MODE_TOOL = "execute";
27832
+ CODE_MODE_CALLS_META = "alanToolCalls";
27833
+ MANAGED_MCP_SERVER_NAME = "alan";
27834
+ RUN_SCOPED_SERVER = /^alan-run-[a-f0-9]{16}$/;
27835
+ NAMESPACED_RUN_TOOL = /^mcp__(alan-run-[a-f0-9]{16})__(.+)$/;
27836
+ FLATTENED_RUN_TOOL = /^alan-run-[a-f0-9]{16}_(.+)$/;
27837
+ }
27838
+ });
27839
+
27793
27840
  // ../agent-core/dist/providers/claude.js
27794
27841
  function trimmed(value) {
27795
27842
  const text2 = value?.trim();
@@ -28838,12 +28885,12 @@ var init_claude = __esm({
28838
28885
  "../agent-core/dist/providers/claude.js"() {
28839
28886
  "use strict";
28840
28887
  init_chunk_D4TIXPWZ();
28841
- init_chunk_ILZBXGVI();
28888
+ init_chunk_FYZKVT3E();
28842
28889
  init_chunk_OUEMTEUX();
28843
28890
  init_chunk_ZCTKXKYY();
28844
- init_chunk_J2KTV5FP();
28845
28891
  init_chunk_6VWVDJ2A();
28846
28892
  init_chunk_DLVRNIY5();
28893
+ init_chunk_OSECDZA5();
28847
28894
  init_chunk_YXWJW7I5();
28848
28895
  init_effort();
28849
28896
  import_crypto7 = require("crypto");
@@ -31050,12 +31097,12 @@ var init_codex = __esm({
31050
31097
  "../agent-core/dist/providers/codex.js"() {
31051
31098
  "use strict";
31052
31099
  init_chunk_D4TIXPWZ();
31053
- init_chunk_ILZBXGVI();
31100
+ init_chunk_FYZKVT3E();
31054
31101
  init_chunk_OUEMTEUX();
31055
31102
  init_chunk_ZCTKXKYY();
31056
- init_chunk_J2KTV5FP();
31057
31103
  init_chunk_6VWVDJ2A();
31058
31104
  init_chunk_DLVRNIY5();
31105
+ init_chunk_OSECDZA5();
31059
31106
  init_chunk_YXWJW7I5();
31060
31107
  import_fs7 = require("fs");
31061
31108
  import_path7 = require("path");
@@ -32328,6 +32375,47 @@ const PROVIDER_ID = ${JSON.stringify(ALAN_CODE_ENGINE_PROVIDER_ID)};
32328
32375
  const INCLUDED_RESULTS = 8;
32329
32376
  const MAX_AUTO_RETRIES = 2;
32330
32377
  const MAX_AUTO_RETRY_WAIT_MS = 10_000;
32378
+ const CODE_MODE_TOOL = ${JSON.stringify(OPENCODE_CODE_MODE_TOOL)};
32379
+ const CODE_MODE_CALLS_META = ${JSON.stringify(CODE_MODE_CALLS_META)};
32380
+ const MAX_CHILD_OUTPUT_CHARS = 64 * 1024;
32381
+ const MAX_OPEN_SCRIPTS = 64;
32382
+ const scriptCalls = new Map();
32383
+
32384
+ function mcpResultText(result) {
32385
+ return result.content
32386
+ .flatMap(function (item) {
32387
+ if (item && item.type === "text") return [item.text];
32388
+ if (item && item.type === "resource" && item.resource && item.resource.text) return [item.resource.text];
32389
+ return [];
32390
+ })
32391
+ .join("\\n\\n");
32392
+ }
32393
+
32394
+ function openScript(callID) {
32395
+ if (scriptCalls.size >= MAX_OPEN_SCRIPTS) scriptCalls.delete(scriptCalls.keys().next().value);
32396
+ scriptCalls.set(callID, []);
32397
+ }
32398
+
32399
+ // Code mode names each MCP call "<script callID>/<n>" and hands the hook the raw MCP result.
32400
+ function recordScriptCall(input, result) {
32401
+ const callID = String(input.callID || "");
32402
+ const slash = callID.lastIndexOf("/");
32403
+ const calls = slash > 0 ? scriptCalls.get(callID.slice(0, slash)) : undefined;
32404
+ if (!calls || !result || !Array.isArray(result.content)) return;
32405
+ calls.push({
32406
+ callId: callID,
32407
+ tool: input.tool,
32408
+ input: input.args,
32409
+ output: mcpResultText(result).slice(0, MAX_CHILD_OUTPUT_CHARS),
32410
+ });
32411
+ }
32412
+
32413
+ function closeScript(callID, output) {
32414
+ const calls = scriptCalls.get(callID);
32415
+ scriptCalls.delete(callID);
32416
+ if (!calls || calls.length === 0 || !output || typeof output !== "object") return;
32417
+ output.metadata = Object.assign({}, output.metadata, { [CODE_MODE_CALLS_META]: calls });
32418
+ }
32331
32419
 
32332
32420
  function truncate(text, max) {
32333
32421
  const trimmed = String(text == null ? "" : text).trim();
@@ -32435,6 +32523,13 @@ export const AlanEnginePlugin = async () => ({
32435
32523
  output.headers["x-alan-operation-key"] = Date.now() + "-" + crypto.randomUUID();
32436
32524
  }
32437
32525
  },
32526
+ "tool.execute.before": async (input) => {
32527
+ if (input.tool === CODE_MODE_TOOL) openScript(input.callID);
32528
+ },
32529
+ "tool.execute.after": async (input, output) => {
32530
+ if (input.tool === CODE_MODE_TOOL) closeScript(input.callID, output);
32531
+ else recordScriptCall(input, output);
32532
+ },
32438
32533
  tool: {
32439
32534
  web_search: {
32440
32535
  description: "web_search: Search the public web via Alan's Parallel proxy. Use when you do not already have a URL. Follow up with webfetch on the best result(s).",
@@ -32468,6 +32563,7 @@ function bearerToken(authorization) {
32468
32563
  function alanCodeEngineEnv(input2) {
32469
32564
  return {
32470
32565
  ...alanCodeEngineDirectories(input2.engineRoot),
32566
+ [OPENCODE_CODE_MODE_ENV]: "true",
32471
32567
  [ALAN_CODE_API_KEY_ENV]: input2.apiKey,
32472
32568
  [ALAN_CODE_CONVERSATION_ID_ENV]: input2.conversationId
32473
32569
  };
@@ -32511,6 +32607,7 @@ var import_http3, import_https, import_fs8, import_path8, import_crypto13, impor
32511
32607
  var init_alan_code = __esm({
32512
32608
  "../agent-core/dist/providers/alan-code.js"() {
32513
32609
  "use strict";
32610
+ init_chunk_OSECDZA5();
32514
32611
  init_chunk_YXWJW7I5();
32515
32612
  import_http3 = require("http");
32516
32613
  import_https = require("https");
@@ -32553,10 +32650,15 @@ var init_alan_code = __esm({
32553
32650
  emptyTurn: "Alan Code ended the turn without producing any output. Try again or pick another model."
32554
32651
  };
32555
32652
  WINDOWS_RUNNABLE_EXTENSION = /\.(exe|com|cmd|bat)$/i;
32556
- ALAN_CODE_ENGINE_PLUGIN_VERSION = 2;
32653
+ ALAN_CODE_ENGINE_PLUGIN_VERSION = 3;
32557
32654
  ALAN_CODE_TOOL_NAMING_PROMPT = `## Alan tool names
32558
32655
 
32559
- This engine exposes Alan MCP tools with an \`alan_\` prefix. Where these instructions name an Alan tool, call its prefixed form: \`search_context\` is \`alan_search_context\`, \`search_code_context\` is \`alan_search_code_context\`, and \`find_records\` is \`alan_find_records\`.`;
32656
+ Alan's MCP tools are not separate tools in this engine. Call them inside an \`execute\` script under the \`alan\` namespace: \`search_context\` is \`tools.alan.search_context({ ... })\`, \`search_code_context\` is \`tools.alan.search_code_context({ ... })\`, and \`find_records\` is \`tools.alan.find_records({ ... })\`.
32657
+
32658
+ - Before the first call to a tool whose signature is not shown in the \`execute\` description, read it with \`tools.$codemode.search({ query: "<tool name>" })\` and pass exactly its fields.
32659
+ - Alan tools return JSON as text: \`JSON.parse\` a result before reading its fields or passing it to \`Object.keys\`.
32660
+ - Look up IDs in one script, then make the writes in the next, so a failed lookup never leaves half the writes done.
32661
+ - Pass an \`idempotencyKey\` naming each change (for example \`create-task:<title>\`) on every write that accepts one. If a script fails, check what it already created before running it again, and run only the writes that are missing.`;
32560
32662
  }
32561
32663
  });
32562
32664
 
@@ -39891,7 +39993,7 @@ var require_dist = __commonJS({
39891
39993
  var init_dist = __esm({
39892
39994
  "../agent-core/dist/index.js"() {
39893
39995
  "use strict";
39894
- init_chunk_ILZBXGVI();
39996
+ init_chunk_FYZKVT3E();
39895
39997
  init_chunk_OUEMTEUX();
39896
39998
  init_chunk_ZCTKXKYY();
39897
39999
  init_chunk_6VWVDJ2A();
@@ -40638,12 +40740,12 @@ var import_promises24, import_path10, import_crypto14, import_promises25, CURSOR
40638
40740
  var init_acp3 = __esm({
40639
40741
  "../agent-core/dist/providers/acp.js"() {
40640
40742
  "use strict";
40641
- init_chunk_ILZBXGVI();
40743
+ init_chunk_FYZKVT3E();
40642
40744
  init_chunk_OUEMTEUX();
40643
40745
  init_chunk_ZCTKXKYY();
40644
- init_chunk_J2KTV5FP();
40645
40746
  init_chunk_6VWVDJ2A();
40646
40747
  init_chunk_DLVRNIY5();
40748
+ init_chunk_OSECDZA5();
40647
40749
  init_chunk_YXWJW7I5();
40648
40750
  import_promises24 = require("fs/promises");
40649
40751
  import_path10 = require("path");
@@ -42144,6 +42246,21 @@ function classifyOpenCodeError(error62, fallback) {
42144
42246
  }
42145
42247
  return classifyCliError(message || fallback);
42146
42248
  }
42249
+ function readChildCall(value) {
42250
+ const record2 = asRecord(value);
42251
+ const callId = asString(record2?.callId);
42252
+ const tool = asString(record2?.tool);
42253
+ if (!record2 || !callId || !tool) return void 0;
42254
+ return { callId, tool, input: record2.input, output: asString(record2.output) };
42255
+ }
42256
+ function readCodeModeChildCalls(metadata) {
42257
+ return asArray(asRecord(metadata)?.[CODE_MODE_CALLS_META]).flatMap(
42258
+ (entry) => {
42259
+ const call = readChildCall(entry);
42260
+ return call ? [call] : [];
42261
+ }
42262
+ );
42263
+ }
42147
42264
  function withParent(updates, parentToolCallId) {
42148
42265
  if (!parentToolCallId) return updates;
42149
42266
  return updates.map(
@@ -42234,6 +42351,24 @@ function toolContent(tool, toolState) {
42234
42351
  const editing = tool.status === "in_progress" && (tool.name === "edit" || tool.name === "write");
42235
42352
  return editing && path ? [fileChangeContent({ path, operation: "modify", hunks: [] })] : void 0;
42236
42353
  }
42354
+ function codeModeChildUpdates(tool, metadata) {
42355
+ if (tool.name !== OPENCODE_CODE_MODE_TOOL || tool.status !== "completed") return [];
42356
+ return readCodeModeChildCalls(metadata).map((call) => {
42357
+ const name = canonicalizeManagedMcpToolName(call.tool);
42358
+ const content = outputContent(call.output);
42359
+ return {
42360
+ sessionUpdate: "tool_call_update",
42361
+ toolCallId: call.callId,
42362
+ name,
42363
+ title: name,
42364
+ kind: toolKindFor(name),
42365
+ status: "completed",
42366
+ ...call.input !== void 0 ? { rawInput: call.input } : {},
42367
+ ...content ? { content } : {},
42368
+ ...call.output ? { rawOutput: call.output } : {}
42369
+ };
42370
+ });
42371
+ }
42237
42372
  function createOpenCodeSessionMemory() {
42238
42373
  return { userMessageIds: /* @__PURE__ */ new Set() };
42239
42374
  }
@@ -42572,12 +42707,12 @@ var import_crypto15, import_http4, import_crypto16, import_promises26, import_pa
42572
42707
  var init_opencode = __esm({
42573
42708
  "../agent-core/dist/providers/opencode.js"() {
42574
42709
  "use strict";
42575
- init_chunk_ILZBXGVI();
42710
+ init_chunk_FYZKVT3E();
42576
42711
  init_chunk_OUEMTEUX();
42577
42712
  init_chunk_ZCTKXKYY();
42578
- init_chunk_J2KTV5FP();
42579
42713
  init_chunk_6VWVDJ2A();
42580
42714
  init_chunk_DLVRNIY5();
42715
+ init_chunk_OSECDZA5();
42581
42716
  init_chunk_YXWJW7I5();
42582
42717
  import_crypto15 = require("crypto");
42583
42718
  import_http4 = require("http");
@@ -43103,6 +43238,7 @@ var init_opencode = __esm({
43103
43238
  }
43104
43239
  ];
43105
43240
  updates.push(...this.#plan(toolCallId, tool.name, toolState));
43241
+ updates.push(...codeModeChildUpdates(tool, toolState?.metadata));
43106
43242
  if (tool.status === "failed" && (tool.isTask || PERMISSION_DENIED.test(tool.errorMessage))) {
43107
43243
  updates.push(
43108
43244
  noticeUpdate("error", "tool_failed", tool.errorMessage || "OpenCode tool call failed.")
@@ -44622,12 +44758,12 @@ var import_crypto18, import_promises27, FACTORY_API_VERSION, FACTORY_PROTOCOL_VE
44622
44758
  var init_droid = __esm({
44623
44759
  "../agent-core/dist/providers/droid.js"() {
44624
44760
  "use strict";
44625
- init_chunk_ILZBXGVI();
44761
+ init_chunk_FYZKVT3E();
44626
44762
  init_chunk_OUEMTEUX();
44627
44763
  init_chunk_ZCTKXKYY();
44628
- init_chunk_J2KTV5FP();
44629
44764
  init_chunk_6VWVDJ2A();
44630
44765
  init_chunk_DLVRNIY5();
44766
+ init_chunk_OSECDZA5();
44631
44767
  init_chunk_YXWJW7I5();
44632
44768
  init_runtime_v2();
44633
44769
  import_crypto18 = require("crypto");
@@ -45732,7 +45868,7 @@ var init_antigravity = __esm({
45732
45868
  "../agent-core/dist/providers/antigravity.js"() {
45733
45869
  "use strict";
45734
45870
  init_chunk_D4TIXPWZ();
45735
- init_chunk_ILZBXGVI();
45871
+ init_chunk_FYZKVT3E();
45736
45872
  init_chunk_OUEMTEUX();
45737
45873
  init_chunk_ZCTKXKYY();
45738
45874
  init_chunk_6VWVDJ2A();
@@ -46436,12 +46572,12 @@ var import_promises29, import_path16, import_crypto20, COMMAND_CODE_GRAMMAR_NAME
46436
46572
  var init_command_code = __esm({
46437
46573
  "../agent-core/dist/providers/command-code.js"() {
46438
46574
  "use strict";
46439
- init_chunk_ILZBXGVI();
46575
+ init_chunk_FYZKVT3E();
46440
46576
  init_chunk_OUEMTEUX();
46441
46577
  init_chunk_ZCTKXKYY();
46442
- init_chunk_J2KTV5FP();
46443
46578
  init_chunk_6VWVDJ2A();
46444
46579
  init_chunk_DLVRNIY5();
46580
+ init_chunk_OSECDZA5();
46445
46581
  init_chunk_YXWJW7I5();
46446
46582
  import_promises29 = require("fs/promises");
46447
46583
  import_path16 = require("path");
@@ -50686,7 +50822,19 @@ async function runHolder({
50686
50822
  ...machine?.platform === void 0 ? {} : { platform: machine.platform }
50687
50823
  });
50688
50824
  const observed = facts.observe(driver);
50689
- const writeStatus = () => writeJsonFileAtomic(launch.statusPath, {
50825
+ let statusWrites = Promise.resolve();
50826
+ const writeStatus = () => {
50827
+ const next = statusWrites.then(() => writeStatusNow());
50828
+ statusWrites = next.catch(
50829
+ (error62) => log.warn("holder.status_write_failed", { error: error62 })
50830
+ );
50831
+ return next;
50832
+ };
50833
+ const queueStatus = () => {
50834
+ writeStatus().catch(() => {
50835
+ });
50836
+ };
50837
+ const writeStatusNow = () => writeJsonFileAtomic(launch.statusPath, {
50690
50838
  protocol: HOLDER_PROTOCOL,
50691
50839
  conversationId: launch.conversationId,
50692
50840
  pid: identity.pid,
@@ -50705,7 +50853,7 @@ async function runHolder({
50705
50853
  holder.state = "idle";
50706
50854
  holder.runId = null;
50707
50855
  holder.turn = null;
50708
- void writeStatus();
50856
+ queueStatus();
50709
50857
  };
50710
50858
  const startTurn = (id, spec, client) => {
50711
50859
  if (holder.runId === spec.runId) {
@@ -50749,7 +50897,7 @@ async function runHolder({
50749
50897
  return;
50750
50898
  }
50751
50899
  writeMessage(client, { type: "ack", id });
50752
- void writeStatus();
50900
+ queueStatus();
50753
50901
  runClaimedTurn(spec, claimed, context, turn.signal).then((outcome2) => broadcast({ type: "turn_ended", runId: spec.runId, outcome: outcome2 })).catch((error62) => log.error("holder.turn_crashed", { runId: spec.runId, error: error62 })).finally(settle2);
50754
50902
  },
50755
50903
  (error62) => {
@@ -50887,6 +51035,7 @@ async function runHolder({
50887
51035
  await driver.suspend?.().catch((error62) => log.warn("holder.driver_suspend_failed", { error: error62 }));
50888
51036
  for (const client of holder.clients) client.end();
50889
51037
  await new Promise((resolve8) => server.close(() => resolve8()));
51038
+ await statusWrites;
50890
51039
  (0, import_node_fs11.rmSync)(launch.statusPath, { force: true });
50891
51040
  log.info("holder.suspended", { conversationId: launch.conversationId });
50892
51041
  } catch (_) {