@codemation/core-nodes 0.10.2 → 0.13.0

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 (39) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/index.cjs +427 -102
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +205 -67
  5. package/dist/index.d.ts +206 -68
  6. package/dist/index.js +427 -99
  7. package/dist/index.js.map +1 -1
  8. package/dist/metadata.json +1 -1
  9. package/package.json +3 -2
  10. package/src/chatModels/CodemationChatModelConfig.ts +9 -21
  11. package/src/chatModels/CodemationChatModelFactory.ts +12 -9
  12. package/src/chatModels/OpenAIChatModelFactory.ts +3 -2
  13. package/src/index.ts +1 -1
  14. package/src/nodes/AIAgentConfig.ts +36 -0
  15. package/src/nodes/AIAgentNode.ts +81 -15
  16. package/src/nodes/AgentBinaryContentFactory.ts +74 -0
  17. package/src/nodes/AgentMessageFactory.ts +22 -6
  18. package/src/nodes/AgentToolResultContentFactory.ts +155 -0
  19. package/src/nodes/CallbackNodeFactory.ts +9 -6
  20. package/src/nodes/CronTriggerFactory.ts +6 -2
  21. package/src/nodes/DeferredMetaToolStrategy.ts +8 -2
  22. package/src/nodes/ManualTriggerFactory.ts +15 -11
  23. package/src/nodes/WebhookTriggerFactory.ts +9 -2
  24. package/src/nodes/aggregate.ts +9 -2
  25. package/src/nodes/assertion.ts +3 -0
  26. package/src/nodes/filter.ts +9 -2
  27. package/src/nodes/httpRequest.ts +6 -1
  28. package/src/nodes/if.ts +9 -2
  29. package/src/nodes/isTestRun.ts +6 -2
  30. package/src/nodes/mapData.ts +4 -2
  31. package/src/nodes/merge.ts +9 -2
  32. package/src/nodes/noOp.ts +9 -2
  33. package/src/nodes/nodeOptions.types.ts +12 -0
  34. package/src/nodes/split.ts +9 -2
  35. package/src/nodes/subWorkflow.ts +9 -2
  36. package/src/nodes/switch.ts +7 -1
  37. package/src/nodes/wait.ts +9 -2
  38. package/src/workflowAuthoring/WorkflowChatModelFactory.types.ts +8 -2
  39. package/src/chatModels/ManagedModelFetcher.ts +0 -23
package/dist/index.cjs CHANGED
@@ -25,14 +25,10 @@ let __codemation_core = require("@codemation/core");
25
25
  __codemation_core = __toESM(__codemation_core);
26
26
  let node_dns_promises = require("node:dns/promises");
27
27
  node_dns_promises = __toESM(node_dns_promises);
28
- let __ai_sdk_openai = require("@ai-sdk/openai");
29
- __ai_sdk_openai = __toESM(__ai_sdk_openai);
30
28
  let __codemation_core_bootstrap = require("@codemation/core/bootstrap");
31
29
  __codemation_core_bootstrap = __toESM(__codemation_core_bootstrap);
32
30
  let node_crypto = require("node:crypto");
33
31
  node_crypto = __toESM(node_crypto);
34
- let ai = require("ai");
35
- ai = __toESM(ai);
36
32
  let croner = require("croner");
37
33
  croner = __toESM(croner);
38
34
 
@@ -704,8 +700,9 @@ function __decorate(decorators, target, key, desc) {
704
700
  let OpenAIChatModelFactory = class OpenAIChatModelFactory$1 {
705
701
  async create(args) {
706
702
  const session = await args.ctx.getCredential(args.config.credentialSlotKey);
703
+ const { createOpenAI } = await import("@ai-sdk/openai");
707
704
  return {
708
- languageModel: (0, __ai_sdk_openai.createOpenAI)({
705
+ languageModel: createOpenAI({
709
706
  apiKey: session.apiKey,
710
707
  baseURL: session.baseUrl
711
708
  }).chat(args.config.model),
@@ -4448,27 +4445,24 @@ function buildHmacAuthHeader(workspaceId, pairingSecret, method, url, body, over
4448
4445
  //#endregion
4449
4446
  //#region src/chatModels/CodemationChatModelFactory.ts
4450
4447
  let CodemationChatModelFactory = class CodemationChatModelFactory$1 {
4451
- create(args) {
4448
+ async create(args) {
4452
4449
  const gatewayUrl = process.env["LLM_GATEWAY_URL"];
4453
4450
  if (!gatewayUrl) throw new Error("Codemation managed AI not available in this environment (LLM_GATEWAY_URL is not set).");
4454
4451
  const workspaceId = process.env["WORKSPACE_ID"];
4455
4452
  const pairingSecret = process.env["WORKSPACE_PAIRING_SECRET"];
4456
4453
  if (!workspaceId || !pairingSecret) throw new Error("Codemation managed AI not available in this environment (workspace pairing is not configured).");
4457
4454
  const hmacFetch = managedHmacFetchFactory(workspaceId, pairingSecret);
4458
- const languageModel = (0, __ai_sdk_openai.createOpenAI)({
4459
- baseURL: `${gatewayUrl}/v1`,
4460
- apiKey: "codemation-managed",
4461
- fetch: hmacFetch
4462
- }).chat(args.config.model);
4463
- return Promise.resolve({
4464
- languageModel,
4465
- modelName: args.config.model,
4455
+ const { createAnthropic } = await import("@ai-sdk/anthropic");
4456
+ return {
4457
+ languageModel: createAnthropic({
4458
+ baseURL: `${gatewayUrl}/v1`,
4459
+ apiKey: "codemation-managed",
4460
+ fetch: hmacFetch
4461
+ })(args.config.complexity),
4462
+ modelName: args.config.complexity,
4466
4463
  provider: "codemation-managed",
4467
- defaultCallOptions: {
4468
- maxOutputTokens: args.config.options?.maxTokens,
4469
- temperature: args.config.options?.temperature
4470
- }
4471
- });
4464
+ defaultCallOptions: { maxOutputTokens: args.config.options?.maxTokens }
4465
+ };
4472
4466
  }
4473
4467
  };
4474
4468
  CodemationChatModelFactory = __decorate([(0, __codemation_core.chatModel)({ packageName: "@codemation/core-nodes" })], CodemationChatModelFactory);
@@ -4480,11 +4474,11 @@ var CodemationChatModelConfig = class {
4480
4474
  presentation;
4481
4475
  provider = "codemation-managed";
4482
4476
  modelName;
4483
- constructor(name, model, presentationIn, options) {
4477
+ constructor(name, complexity, presentationIn, options) {
4484
4478
  this.name = name;
4485
- this.model = model;
4479
+ this.complexity = complexity;
4486
4480
  this.options = options;
4487
- this.modelName = model;
4481
+ this.modelName = complexity;
4488
4482
  this.presentation = presentationIn ?? {
4489
4483
  icon: "lucide:bot",
4490
4484
  label: name
@@ -4493,24 +4487,151 @@ var CodemationChatModelConfig = class {
4493
4487
  };
4494
4488
 
4495
4489
  //#endregion
4496
- //#region src/chatModels/ManagedModelFetcher.ts
4490
+ //#region src/nodes/AgentToolResultContentFactory.ts
4497
4491
  /**
4498
- * Fetches the active platform-managed model allowlist from the CP.
4499
- * Reads CONTROL_PLANE_URL from the workspace process env.
4500
- * Returns an empty array if the env var is absent or the fetch fails.
4501
- * Cache the result per session — the allowlist changes infrequently.
4492
+ * Cap on raw (pre-base64) bytes inlined from a single tool result. Base64 inflates ~33% and every
4493
+ * inlined byte eats model context, so oversize binaries are replaced with a text placeholder.
4502
4494
  */
4503
- var ManagedModelFetcher = class {
4504
- async fetch() {
4505
- const cpUrl = process.env["CONTROL_PLANE_URL"];
4506
- if (!cpUrl) return [];
4507
- try {
4508
- const res = await globalThis.fetch(`${cpUrl}/api/llm/managed-models`);
4509
- if (!res.ok) return [];
4510
- return await res.json();
4511
- } catch {
4512
- return [];
4495
+ const MAX_INLINE_BYTES = 8 * 1024 * 1024;
4496
+ /** MCP content-block discriminators. At least one must appear for a result to be treated as MCP-shaped. */
4497
+ const KNOWN_MCP_BLOCK_TYPES = new Set([
4498
+ "text",
4499
+ "image",
4500
+ "audio",
4501
+ "resource",
4502
+ "resource_link"
4503
+ ]);
4504
+ /**
4505
+ * Maps a tool result that is **content-block-shaped** (an MCP `CallToolResult` with a `content`
4506
+ * array) into AI SDK `{ type: "content" }` tool-result output, so binaries reach the chat model as
4507
+ * native multimodal tool-result blocks instead of being flattened to inert JSON text.
4508
+ *
4509
+ * The `@ai-sdk/anthropic` provider maps a `content`-output part as follows:
4510
+ * `text` → text block, `image-data` → image block, `file-data` (only `application/pdf`) → document
4511
+ * block. Non-PDF `file-data` is dropped by the provider, so this factory emits `image-data` for
4512
+ * images, `file-data` only for PDFs, and a text marker for every other binary type.
4513
+ *
4514
+ * Returns `undefined` when the result is not content-block-shaped — callers keep the existing
4515
+ * `{ type: "json" }` path, so plain string/object tool results are unaffected.
4516
+ */
4517
+ var AgentToolResultContentFactory = class AgentToolResultContentFactory {
4518
+ static tryMapToContentOutput(result) {
4519
+ const blocks = AgentToolResultContentFactory.contentBlocks(result);
4520
+ if (blocks === void 0) return void 0;
4521
+ const parts = [];
4522
+ let inlinedBytes = 0;
4523
+ for (const block of blocks) {
4524
+ const mapped = AgentToolResultContentFactory.mapBlock(block, inlinedBytes);
4525
+ parts.push(mapped.part);
4526
+ inlinedBytes += mapped.bytes;
4513
4527
  }
4528
+ return parts;
4529
+ }
4530
+ /**
4531
+ * Returns the `content` array iff `result` is an object whose `content` is an array of typed
4532
+ * blocks AND at least one block carries a known MCP discriminator. A plain JSON result that merely
4533
+ * has a `content` key of some other shape (e.g. Notion/Slack rich-text blocks) is rejected,
4534
+ * preserving the `{ type: "json" }` path so its payload is never lost.
4535
+ */
4536
+ static contentBlocks(result) {
4537
+ if (result === null || typeof result !== "object") return void 0;
4538
+ const content = result.content;
4539
+ if (!Array.isArray(content) || content.length === 0) return void 0;
4540
+ if (!content.every((block) => block !== null && typeof block === "object" && typeof block.type === "string")) return void 0;
4541
+ return content.some((block) => KNOWN_MCP_BLOCK_TYPES.has(block.type)) ? content : void 0;
4542
+ }
4543
+ static mapBlock(block, inlinedBytesSoFar) {
4544
+ const type = block.type;
4545
+ if (type === "text" && typeof block.text === "string") return {
4546
+ part: {
4547
+ type: "text",
4548
+ text: block.text
4549
+ },
4550
+ bytes: 0
4551
+ };
4552
+ if (type === "image" && typeof block.data === "string" && typeof block.mimeType === "string") return AgentToolResultContentFactory.mapBinary({
4553
+ base64: block.data,
4554
+ mediaType: block.mimeType,
4555
+ inlinedBytesSoFar
4556
+ });
4557
+ if (type === "resource" && block.resource) return AgentToolResultContentFactory.mapEmbeddedResource(block.resource, inlinedBytesSoFar);
4558
+ if (type === "resource_link" && typeof block.uri === "string") {
4559
+ const mime = typeof block.mimeType === "string" ? ` (${block.mimeType})` : "";
4560
+ return {
4561
+ part: {
4562
+ type: "text",
4563
+ text: `[linked resource: ${block.uri}${mime}]`
4564
+ },
4565
+ bytes: 0
4566
+ };
4567
+ }
4568
+ return {
4569
+ part: {
4570
+ type: "text",
4571
+ text: `[unsupported tool content block: ${String(type)}]`
4572
+ },
4573
+ bytes: 0
4574
+ };
4575
+ }
4576
+ static mapEmbeddedResource(resource, inlinedBytesSoFar) {
4577
+ if (typeof resource.text === "string") return {
4578
+ part: {
4579
+ type: "text",
4580
+ text: resource.text
4581
+ },
4582
+ bytes: 0
4583
+ };
4584
+ if (typeof resource.blob === "string" && typeof resource.mimeType === "string") return AgentToolResultContentFactory.mapBinary({
4585
+ base64: resource.blob,
4586
+ mediaType: resource.mimeType,
4587
+ filename: typeof resource.name === "string" ? resource.name : void 0,
4588
+ inlinedBytesSoFar
4589
+ });
4590
+ return {
4591
+ part: {
4592
+ type: "text",
4593
+ text: `[embedded resource: ${typeof resource.uri === "string" ? resource.uri : "unknown"}]`
4594
+ },
4595
+ bytes: 0
4596
+ };
4597
+ }
4598
+ static mapBinary(args) {
4599
+ const rawBytes = Math.floor(args.base64.length * 3 / 4);
4600
+ if (args.inlinedBytesSoFar + rawBytes > MAX_INLINE_BYTES) {
4601
+ const name = args.filename ? ` "${args.filename}"` : "";
4602
+ const kb = Math.round(rawBytes / 1024);
4603
+ return {
4604
+ part: {
4605
+ type: "text",
4606
+ text: `[binary${name} (${args.mediaType}, ~${kb} KB) omitted: exceeds the per-tool-result inline limit]`
4607
+ },
4608
+ bytes: 0
4609
+ };
4610
+ }
4611
+ if (args.mediaType.startsWith("image/")) return {
4612
+ part: {
4613
+ type: "image-data",
4614
+ data: args.base64,
4615
+ mediaType: args.mediaType
4616
+ },
4617
+ bytes: rawBytes
4618
+ };
4619
+ if (args.mediaType === "application/pdf") return {
4620
+ part: {
4621
+ type: "file-data",
4622
+ data: args.base64,
4623
+ mediaType: args.mediaType,
4624
+ ...args.filename ? { filename: args.filename } : {}
4625
+ },
4626
+ bytes: rawBytes
4627
+ };
4628
+ return {
4629
+ part: {
4630
+ type: "text",
4631
+ text: `[binary${args.filename ? ` "${args.filename}"` : ""} (${args.mediaType}) not inlined: unsupported by the model]`
4632
+ },
4633
+ bytes: 0
4634
+ };
4514
4635
  }
4515
4636
  };
4516
4637
 
@@ -4550,20 +4671,35 @@ var AgentMessageFactory = class AgentMessageFactory {
4550
4671
  * Builds the `{ role: "tool", content: [{ type: "tool-result", ... }, ...] }` message returned
4551
4672
  * to the model after each tool round.
4552
4673
  */
4553
- static createToolResultsMessage(executedToolCalls) {
4674
+ static createToolResultsMessage(executedToolCalls, passToolBinariesToModel = true) {
4554
4675
  return {
4555
4676
  role: "tool",
4556
4677
  content: executedToolCalls.map((executed) => ({
4557
4678
  type: "tool-result",
4558
4679
  toolCallId: executed.toolCallId,
4559
4680
  toolName: executed.toolName,
4560
- output: {
4561
- type: "json",
4562
- value: AgentMessageFactory.toToolResultJson(executed.result)
4563
- }
4681
+ output: AgentMessageFactory.toToolResultOutput(executed.result, passToolBinariesToModel)
4564
4682
  }))
4565
4683
  };
4566
4684
  }
4685
+ /**
4686
+ * Routes a tool result to a native multimodal `{ type: "content" }` output when it is
4687
+ * content-block-shaped (an MCP `CallToolResult`) and binary passdown is enabled; otherwise keeps
4688
+ * the inert `{ type: "json" }` path.
4689
+ */
4690
+ static toToolResultOutput(result, passToolBinariesToModel) {
4691
+ if (passToolBinariesToModel) {
4692
+ const content = AgentToolResultContentFactory.tryMapToContentOutput(result);
4693
+ if (content !== void 0) return {
4694
+ type: "content",
4695
+ value: content
4696
+ };
4697
+ }
4698
+ return {
4699
+ type: "json",
4700
+ value: AgentMessageFactory.toToolResultJson(result)
4701
+ };
4702
+ }
4567
4703
  static toToolResultJson(value) {
4568
4704
  if (value === void 0) return null;
4569
4705
  try {
@@ -5882,6 +6018,63 @@ AgentToolExecutionCoordinator = __decorate([
5882
6018
  __decorateMetadata("design:paramtypes", [typeof (_ref$2 = typeof AgentToolErrorClassifier !== "undefined" && AgentToolErrorClassifier) === "function" ? _ref$2 : Object, typeof (_ref2$2 = typeof AgentToolRepairPolicy !== "undefined" && AgentToolRepairPolicy) === "function" ? _ref2$2 : Object])
5883
6019
  ], AgentToolExecutionCoordinator);
5884
6020
 
6021
+ //#endregion
6022
+ //#region src/nodes/AgentBinaryContentFactory.ts
6023
+ /**
6024
+ * Turns resolved file binaries into native AI SDK multimodal content parts and merges them into the
6025
+ * agent prompt. Images (`image/*`) become {@link ImagePart}s; every other type (PDFs, office docs,
6026
+ * CSV, JSON, …) becomes a {@link FilePart}. The provider maps these to its wire-level `image` /
6027
+ * `document` blocks; an unsupported file type surfaces as a provider error at runtime.
6028
+ *
6029
+ * Parts are appended to the LAST user message so the binary travels alongside the author's prompt
6030
+ * text (preserving any untrusted-source preamble that already wrapped that text). When no user
6031
+ * message exists, a new user message carrying only the binaries is appended.
6032
+ */
6033
+ var AgentBinaryContentFactory = class AgentBinaryContentFactory {
6034
+ static toContentPart(binary) {
6035
+ if (binary.mediaType.startsWith("image/")) return {
6036
+ type: "image",
6037
+ image: binary.base64,
6038
+ mediaType: binary.mediaType
6039
+ };
6040
+ return {
6041
+ type: "file",
6042
+ data: binary.base64,
6043
+ mediaType: binary.mediaType,
6044
+ ...binary.filename ? { filename: binary.filename } : {}
6045
+ };
6046
+ }
6047
+ static withBinaries(messages, binaries) {
6048
+ if (binaries.length === 0) return messages;
6049
+ const parts = binaries.map((binary) => AgentBinaryContentFactory.toContentPart(binary));
6050
+ const lastUserIndex = AgentBinaryContentFactory.lastUserMessageIndex(messages);
6051
+ if (lastUserIndex === -1) {
6052
+ const appended = {
6053
+ role: "user",
6054
+ content: parts
6055
+ };
6056
+ return [...messages, appended];
6057
+ }
6058
+ const next = [...messages];
6059
+ next[lastUserIndex] = AgentBinaryContentFactory.appendPartsToUserMessage(messages[lastUserIndex], parts);
6060
+ return next;
6061
+ }
6062
+ static lastUserMessageIndex(messages) {
6063
+ for (let index = messages.length - 1; index >= 0; index--) if (messages[index]?.role === "user") return index;
6064
+ return -1;
6065
+ }
6066
+ static appendPartsToUserMessage(message, parts) {
6067
+ const existing = typeof message.content === "string" ? message.content.length > 0 ? [{
6068
+ type: "text",
6069
+ text: message.content
6070
+ }] : [] : message.content;
6071
+ return {
6072
+ ...message,
6073
+ content: [...existing, ...parts]
6074
+ };
6075
+ }
6076
+ };
6077
+
5885
6078
  //#endregion
5886
6079
  //#region src/nodes/NodeBackedToolRuntime.ts
5887
6080
  var _ref$1, _ref2$1, _ref3$1, _ref4$1;
@@ -6080,11 +6273,18 @@ var DeferredMetaToolStrategy = class {
6080
6273
  mcpEntries = [];
6081
6274
  toolsByServerId = /* @__PURE__ */ new Map();
6082
6275
  foundToolIds = /* @__PURE__ */ new Set();
6276
+ /**
6277
+ * `jsonSchema` from the `ai` SDK, loaded lazily in {@link initialize} so the SDK
6278
+ * (~28MB RSS) stays off the boot path. `initialize` always runs before the sync
6279
+ * `getToolsForTurn` → `buildFindToolsDefinition` path, so this is set before use.
6280
+ */
6281
+ jsonSchema;
6083
6282
  constructor(bm25, warnFn) {
6084
6283
  this.bm25 = bm25;
6085
6284
  this.warnFn = warnFn;
6086
6285
  }
6087
6286
  async initialize(input) {
6287
+ this.jsonSchema = (await import("ai")).jsonSchema;
6088
6288
  this.nodeBackedTools = { ...input.nodeBackedTools };
6089
6289
  const pinnedIds = input.pinnedMcpTools ?? [];
6090
6290
  if (pinnedIds.length > PINNED_TOOLS_HARD_LIMIT) throw new Error(`Agent config error: pinnedMcpTools count (${pinnedIds.length}) exceeds hard limit of ${PINNED_TOOLS_HARD_LIMIT}.`);
@@ -6172,25 +6372,26 @@ var DeferredMetaToolStrategy = class {
6172
6372
  return [...this.foundToolIds];
6173
6373
  }
6174
6374
  buildFindToolsDefinition() {
6375
+ const inputSchemaRecord = {
6376
+ type: "object",
6377
+ properties: {
6378
+ query: {
6379
+ type: "string",
6380
+ description: "Natural language description of what you want to do."
6381
+ },
6382
+ limit: {
6383
+ type: "integer",
6384
+ minimum: 1,
6385
+ maximum: 10,
6386
+ description: `Maximum number of tools to return (default ${FIND_TOOLS_DEFAULT_LIMIT}).`
6387
+ }
6388
+ },
6389
+ required: ["query"],
6390
+ additionalProperties: false
6391
+ };
6175
6392
  return {
6176
6393
  description: "Search for tools available from connected MCP servers. After this call, the tools listed in the result will be callable on your very next turn. Use this when you need a capability not visible in your current tool list. Do not attempt to call a tool name you have not seen yet — use find_tools to discover it first.",
6177
- inputSchema: (0, ai.jsonSchema)({
6178
- type: "object",
6179
- properties: {
6180
- query: {
6181
- type: "string",
6182
- description: "Natural language description of what you want to do."
6183
- },
6184
- limit: {
6185
- type: "integer",
6186
- minimum: 1,
6187
- maximum: 10,
6188
- description: `Maximum number of tools to return (default ${FIND_TOOLS_DEFAULT_LIMIT}).`
6189
- }
6190
- },
6191
- required: ["query"],
6192
- additionalProperties: false
6193
- })
6394
+ inputSchema: this.jsonSchema(inputSchemaRecord)
6194
6395
  };
6195
6396
  }
6196
6397
  };
@@ -6224,6 +6425,13 @@ let AIAgentNode = class AIAgentNode$1 {
6224
6425
  inputSchema = unknown();
6225
6426
  connectionCredentialExecutionContextFactory;
6226
6427
  preparedByExecutionContext = /* @__PURE__ */ new WeakMap();
6428
+ /**
6429
+ * The `ai` SDK, loaded lazily in {@link execute} so the SDK (~28MB RSS) stays
6430
+ * off the boot path — non-AI workflows never load it. Every path runs through
6431
+ * `execute` → `ensureAiSdk` before any sync helper touches `this.aiSdk`.
6432
+ */
6433
+ aiSdk;
6434
+ aiSdkPromise = null;
6227
6435
  constructor(nodeResolver, credentialSessions, nodeBackedToolRuntime, executionHelpers, structuredOutputRunner, toolExecutionCoordinator, toolLoadingStrategyFactory, agentMcpIntegration) {
6228
6436
  this.nodeResolver = nodeResolver;
6229
6437
  this.nodeBackedToolRuntime = nodeBackedToolRuntime;
@@ -6236,6 +6444,7 @@ let AIAgentNode = class AIAgentNode$1 {
6236
6444
  }
6237
6445
  async execute(args) {
6238
6446
  const { ctx } = args;
6447
+ await this.ensureAiSdk();
6239
6448
  if (ctx.resumeContext) return this.executeResumed(args, ctx.resumeContext);
6240
6449
  const prepared = await this.getOrPrepareExecution(ctx);
6241
6450
  const itemWithMappedJson = {
@@ -6244,6 +6453,10 @@ let AIAgentNode = class AIAgentNode$1 {
6244
6453
  };
6245
6454
  return (await this.runAgentForItem(prepared, itemWithMappedJson, args.itemIndex, args.items)).json;
6246
6455
  }
6456
+ /** Load the `ai` SDK once per node instance (cached promise guards concurrent items). */
6457
+ async ensureAiSdk() {
6458
+ this.aiSdk = await (this.aiSdkPromise ??= import("ai"));
6459
+ }
6247
6460
  /**
6248
6461
  * Resume path: re-enters the agent loop after a HITL suspension.
6249
6462
  * Reconstructs the conversation from the checkpoint, injects the human decision
@@ -6275,7 +6488,7 @@ let AIAgentNode = class AIAgentNode$1 {
6275
6488
  result: decision,
6276
6489
  serialized: JSON.stringify(decision)
6277
6490
  };
6278
- const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry])];
6491
+ const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry], ctx.config.passToolBinariesToModel !== false)];
6279
6492
  const loopResult = await this.runTurnLoopUntilFinalAnswer({
6280
6493
  prepared,
6281
6494
  itemInputsByPort,
@@ -6386,7 +6599,7 @@ let AIAgentNode = class AIAgentNode$1 {
6386
6599
  const { ctx } = prepared;
6387
6600
  const itemInputsByPort = AgentItemPortMap.fromItem(item);
6388
6601
  const itemScopedTools = this.createItemScopedTools(prepared.resolvedTools, ctx, item, itemIndex, items);
6389
- const conversation = [...this.createPromptMessages(item, itemIndex, items, ctx)];
6602
+ const conversation = [...await this.createPromptMessages(item, itemIndex, items, ctx)];
6390
6603
  if (ctx.config.outputSchema && itemScopedTools.length === 0) {
6391
6604
  const structuredOutput = await this.structuredOutputRunner.resolve({
6392
6605
  model: prepared.model,
@@ -6495,7 +6708,7 @@ let AIAgentNode = class AIAgentNode$1 {
6495
6708
  coordinatorExecutedCalls.push(...executed);
6496
6709
  }
6497
6710
  const allExecutedCalls = [...strategyExecutedCalls, ...coordinatorExecutedCalls];
6498
- this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, allExecutedCalls);
6711
+ this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, allExecutedCalls, ctx.config.passToolBinariesToModel !== false);
6499
6712
  }
6500
6713
  return {
6501
6714
  finalText,
@@ -6510,8 +6723,8 @@ let AIAgentNode = class AIAgentNode$1 {
6510
6723
  if (guardrails.onTurnLimitReached === "respondWithLastMessage") return;
6511
6724
  throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" reached maxTurns=${guardrails.maxTurns} before producing a final response.`);
6512
6725
  }
6513
- appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls) {
6514
- conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls));
6726
+ appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls, passToolBinariesToModel) {
6727
+ conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls, passToolBinariesToModel));
6515
6728
  }
6516
6729
  async resolveFinalOutputJson(prepared, itemInputsByPort, conversation, finalText, wasToolEnabledRun) {
6517
6730
  if (!prepared.ctx.config.outputSchema) return AgentOutputFactory.fromAgentContent(finalText);
@@ -6630,7 +6843,7 @@ let AIAgentNode = class AIAgentNode$1 {
6630
6843
  const description = this.resolveHumanApprovalBehavior(entry.config) !== void 0 ? `${baseDescription} ${HITL_SOLO_CONSTRAINT_SENTENCE}` : baseDescription;
6631
6844
  toolSet[entry.config.name] = {
6632
6845
  description,
6633
- inputSchema: (0, ai.jsonSchema)(schemaRecord)
6846
+ inputSchema: this.aiSdk.jsonSchema(schemaRecord)
6634
6847
  };
6635
6848
  }
6636
6849
  return toolSet;
@@ -6661,7 +6874,7 @@ let AIAgentNode = class AIAgentNode$1 {
6661
6874
  const description = entry.humanApproval !== void 0 && baseDescription !== void 0 ? `${baseDescription} ${HITL_SOLO_CONSTRAINT_SENTENCE}` : entry.humanApproval !== void 0 ? HITL_SOLO_CONSTRAINT_SENTENCE : baseDescription;
6662
6875
  toolSet[entry.config.name] = {
6663
6876
  description,
6664
- inputSchema: (0, ai.jsonSchema)(schemaRecord)
6877
+ inputSchema: this.aiSdk.jsonSchema(schemaRecord)
6665
6878
  };
6666
6879
  }
6667
6880
  return toolSet;
@@ -6713,7 +6926,7 @@ let AIAgentNode = class AIAgentNode$1 {
6713
6926
  });
6714
6927
  try {
6715
6928
  const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
6716
- const result = await (0, ai.generateText)({
6929
+ const result = await this.aiSdk.generateText({
6717
6930
  model: model.languageModel,
6718
6931
  messages: [...messages],
6719
6932
  tools,
@@ -6832,8 +7045,8 @@ let AIAgentNode = class AIAgentNode$1 {
6832
7045
  schemaName: structuredOptions?.schemaName ?? "structured_output",
6833
7046
  requireObjectRoot: true
6834
7047
  }) : schema;
6835
- const outputSchema = ai.Output.object({ schema: (0, ai.jsonSchema)(schemaRecord) });
6836
- const result = await (0, ai.generateText)({
7048
+ const outputSchema = this.aiSdk.Output.object({ schema: this.aiSdk.jsonSchema(schemaRecord) });
7049
+ const result = await this.aiSdk.generateText({
6837
7050
  model: model.languageModel,
6838
7051
  messages: [...messages],
6839
7052
  experimental_output: outputSchema,
@@ -7104,7 +7317,7 @@ let AIAgentNode = class AIAgentNode$1 {
7104
7317
  const json = JSON.stringify(value);
7105
7318
  return JSON.parse(json);
7106
7319
  }
7107
- createPromptMessages(item, itemIndex, items, ctx) {
7320
+ async createPromptMessages(item, itemIndex, items, ctx) {
7108
7321
  const messages = __codemation_core.AgentMessageConfigNormalizer.resolveFromInputOrConfig(item.json, ctx.config, {
7109
7322
  item,
7110
7323
  itemIndex,
@@ -7112,7 +7325,47 @@ let AIAgentNode = class AIAgentNode$1 {
7112
7325
  ctx
7113
7326
  });
7114
7327
  const wrapped = this.wrapUntrustedSourceMessages(messages, item, ctx.config);
7115
- return AgentMessageFactory.createPromptMessages(wrapped);
7328
+ const promptMessages = AgentMessageFactory.createPromptMessages(wrapped);
7329
+ if (ctx.config.passBinariesToModel === false) return promptMessages;
7330
+ const attachments = this.selectBinaryAttachments(item, itemIndex, items, ctx);
7331
+ const binaries = await this.resolveInlineBinaries(attachments, ctx);
7332
+ return AgentBinaryContentFactory.withBinaries(promptMessages, binaries);
7333
+ }
7334
+ /**
7335
+ * Picks which attachments feed the passdown. When the author supplies `config.binaries`
7336
+ * (a static array or a per-item function — e.g. to forward binaries from an earlier node),
7337
+ * those replace the current item's attachments; otherwise the current item's `item.binary`
7338
+ * is used.
7339
+ */
7340
+ selectBinaryAttachments(item, itemIndex, items, ctx) {
7341
+ const manual = ctx.config.binaries;
7342
+ if (manual !== void 0) return typeof manual === "function" ? manual({
7343
+ item,
7344
+ itemIndex,
7345
+ items,
7346
+ ctx
7347
+ }) : manual;
7348
+ return item.binary ? Object.values(item.binary) : [];
7349
+ }
7350
+ /**
7351
+ * Reads every attachment through `ctx.binary` (storage-backed, by reference — never base64 on
7352
+ * `item.json`) and resolves it to inline base64 so the agent can pass it to the chat model as a
7353
+ * native multimodal block. Images become image blocks; every other type (PDF, office docs, CSV,
7354
+ * JSON, …) becomes a file block — we don't filter by media type, so any binary can be fed to the
7355
+ * model. If the provider rejects an unsupported type the error surfaces at runtime, and the
7356
+ * workflow can filter the binary upstream.
7357
+ */
7358
+ async resolveInlineBinaries(attachments, ctx) {
7359
+ const resolved = [];
7360
+ for (const attachment of attachments) {
7361
+ const bytes = await ctx.binary.getBytes(attachment);
7362
+ resolved.push({
7363
+ mediaType: attachment.mimeType,
7364
+ base64: Buffer.from(bytes).toString("base64"),
7365
+ ...attachment.filename ? { filename: attachment.filename } : {}
7366
+ });
7367
+ }
7368
+ return resolved;
7116
7369
  }
7117
7370
  /**
7118
7371
  * When `item.json.__source` matches an entry in `config.untrustedSources`
@@ -7226,6 +7479,7 @@ var AIAgent = class {
7226
7479
  chatModel;
7227
7480
  tools;
7228
7481
  id;
7482
+ description;
7229
7483
  retryPolicy;
7230
7484
  guardrails;
7231
7485
  inputSchema;
@@ -7233,12 +7487,16 @@ var AIAgent = class {
7233
7487
  mcpServers;
7234
7488
  pinnedMcpTools;
7235
7489
  untrustedSources;
7490
+ passBinariesToModel;
7491
+ passToolBinariesToModel;
7492
+ binaries;
7236
7493
  constructor(options) {
7237
7494
  this.name = options.name;
7238
7495
  this.messages = options.messages;
7239
7496
  this.chatModel = options.chatModel;
7240
7497
  this.tools = options.tools ?? [];
7241
7498
  this.id = options.id;
7499
+ this.description = options.description;
7242
7500
  this.retryPolicy = options.retryPolicy ?? __codemation_core.RetryPolicy.defaultForAiAgent;
7243
7501
  this.guardrails = options.guardrails;
7244
7502
  this.inputSchema = options.inputSchema;
@@ -7246,6 +7504,9 @@ var AIAgent = class {
7246
7504
  this.mcpServers = options.mcpServers;
7247
7505
  this.pinnedMcpTools = options.pinnedMcpTools;
7248
7506
  this.untrustedSources = options.untrustedSources;
7507
+ this.passBinariesToModel = options.passBinariesToModel;
7508
+ this.passToolBinariesToModel = options.passToolBinariesToModel;
7509
+ this.binaries = options.binaries;
7249
7510
  }
7250
7511
  inspectorSummary() {
7251
7512
  const rows = [];
@@ -7316,12 +7577,14 @@ var Assertion = class {
7316
7577
  icon;
7317
7578
  name;
7318
7579
  id;
7580
+ description;
7319
7581
  emitsAssertions = true;
7320
7582
  assertions;
7321
7583
  constructor(options) {
7322
7584
  this.name = options.name ?? "Assertion";
7323
7585
  this.id = options.id;
7324
7586
  this.icon = options.icon ?? "lucide:check-circle";
7587
+ this.description = options.description;
7325
7588
  this.assertions = options.assertions;
7326
7589
  }
7327
7590
  inspectorSummary() {
@@ -7373,6 +7636,7 @@ var Callback = class Callback {
7373
7636
  icon = "lucide:braces";
7374
7637
  emptyBatchExecution = "runOnce";
7375
7638
  id;
7639
+ description;
7376
7640
  retryPolicy;
7377
7641
  nodeErrorHandler;
7378
7642
  declaredOutputPorts;
@@ -7384,6 +7648,7 @@ var Callback = class Callback {
7384
7648
  id: idOrOptions
7385
7649
  } : idOrOptions;
7386
7650
  this.id = resolvedOptions?.id;
7651
+ this.description = resolvedOptions?.description;
7387
7652
  this.retryPolicy = resolvedOptions?.retryPolicy;
7388
7653
  this.nodeErrorHandler = resolvedOptions?.nodeErrorHandler;
7389
7654
  this.declaredOutputPorts = resolvedOptions?.declaredOutputPorts;
@@ -7585,10 +7850,12 @@ var HttpRequest = class {
7585
7850
  type = HttpRequestNode;
7586
7851
  execution = { hint: "local" };
7587
7852
  icon = "lucide:globe";
7853
+ description;
7588
7854
  constructor(name, args = {}, retryPolicy = __codemation_core.RetryPolicy.defaultForHttp) {
7589
7855
  this.name = name;
7590
7856
  this.args = args;
7591
7857
  this.retryPolicy = retryPolicy;
7858
+ this.description = args.description;
7592
7859
  }
7593
7860
  get id() {
7594
7861
  return this.args.id;
@@ -7683,10 +7950,14 @@ var Aggregate = class {
7683
7950
  execution = { hint: "local" };
7684
7951
  keepBinaries = true;
7685
7952
  icon = "builtin:aggregate-rows";
7686
- constructor(name, aggregate, id) {
7953
+ id;
7954
+ description;
7955
+ constructor(name, aggregate, idOrOptions) {
7687
7956
  this.name = name;
7688
7957
  this.aggregate = aggregate;
7689
- this.id = id;
7958
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7959
+ this.id = options?.id;
7960
+ this.description = options?.description;
7690
7961
  }
7691
7962
  inspectorSummary() {
7692
7963
  const fnName = this.aggregate.name;
@@ -7717,10 +7988,14 @@ var Filter = class {
7717
7988
  type = FilterNode;
7718
7989
  execution = { hint: "local" };
7719
7990
  icon = "lucide:filter";
7720
- constructor(name, predicate, id) {
7991
+ id;
7992
+ description;
7993
+ constructor(name, predicate, idOrOptions) {
7721
7994
  this.name = name;
7722
7995
  this.predicate = predicate;
7723
- this.id = id;
7996
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7997
+ this.id = options?.id;
7998
+ this.description = options?.description;
7724
7999
  }
7725
8000
  inspectorSummary() {
7726
8001
  const fnName = this.predicate.name;
@@ -7795,10 +8070,14 @@ var If = class {
7795
8070
  execution = { hint: "local" };
7796
8071
  icon = "lucide:split@rot=90";
7797
8072
  declaredOutputPorts = ["true", "false"];
7798
- constructor(name, predicate, id) {
8073
+ id;
8074
+ description;
8075
+ constructor(name, predicate, idOrOptions) {
7799
8076
  this.name = name;
7800
8077
  this.predicate = predicate;
7801
- this.id = id;
8078
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8079
+ this.id = options?.id;
8080
+ this.description = options?.description;
7802
8081
  }
7803
8082
  inspectorSummary() {
7804
8083
  const fnName = this.predicate.name;
@@ -7838,9 +8117,12 @@ var IsTestRun = class {
7838
8117
  declaredOutputPorts = ["true", "false"];
7839
8118
  name;
7840
8119
  id;
7841
- constructor(name = "Is test run?", id) {
8120
+ description;
8121
+ constructor(name = "Is test run?", idOrOptions) {
7842
8122
  this.name = name;
7843
- this.id = id;
8123
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8124
+ this.id = options?.id;
8125
+ this.description = options?.description;
7844
8126
  }
7845
8127
  };
7846
8128
 
@@ -7869,11 +8151,15 @@ var Switch = class {
7869
8151
  execution = { hint: "local" };
7870
8152
  icon = "lucide:git-branch-plus";
7871
8153
  declaredOutputPorts;
7872
- constructor(name, cfg, id) {
8154
+ id;
8155
+ description;
8156
+ constructor(name, cfg, idOrOptions) {
7873
8157
  this.name = name;
7874
8158
  this.cfg = cfg;
7875
- this.id = id;
7876
8159
  this.declaredOutputPorts = [...new Set([...cfg.cases, cfg.defaultCase])].sort();
8160
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8161
+ this.id = options?.id;
8162
+ this.description = options?.description;
7877
8163
  }
7878
8164
  inspectorSummary() {
7879
8165
  const rows = [{
@@ -7912,10 +8198,14 @@ var Split = class {
7912
8198
  */
7913
8199
  continueWhenEmptyOutput = true;
7914
8200
  icon = "builtin:split-rows";
7915
- constructor(name, getElements, id) {
8201
+ id;
8202
+ description;
8203
+ constructor(name, getElements, idOrOptions) {
7916
8204
  this.name = name;
7917
8205
  this.getElements = getElements;
7918
- this.id = id;
8206
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8207
+ this.id = options?.id;
8208
+ this.description = options?.description;
7919
8209
  }
7920
8210
  inspectorSummary() {
7921
8211
  const fnName = this.getElements.name;
@@ -7973,14 +8263,17 @@ var CronTrigger = class {
7973
8263
  type = CronTriggerNode;
7974
8264
  icon = "lucide:clock";
7975
8265
  id;
7976
- constructor(name, args, id) {
8266
+ description;
8267
+ constructor(name, args, idOrOptions) {
7977
8268
  this.name = name;
7978
8269
  this.args = args;
7979
8270
  new croner.Cron(args.schedule, {
7980
8271
  paused: true,
7981
8272
  timezone: args.timezone
7982
8273
  });
7983
- this.id = id;
8274
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8275
+ this.id = options?.id;
8276
+ this.description = options?.description;
7984
8277
  }
7985
8278
  get schedule() {
7986
8279
  return this.args.schedule;
@@ -8034,20 +8327,21 @@ var ManualTrigger = class ManualTrigger {
8034
8327
  icon = "lucide:play";
8035
8328
  defaultItems;
8036
8329
  id;
8330
+ description;
8037
8331
  /** Manual runs often emit an empty batch; still schedule downstream by default. */
8038
8332
  continueWhenEmptyOutput = true;
8039
- constructor(name = "Manual trigger", defaultItemsOrId, id) {
8333
+ constructor(name = "Manual trigger", defaultItemsOrId, idOrOptions) {
8040
8334
  this.name = name;
8041
8335
  this.defaultItems = ManualTrigger.resolveDefaultItems(defaultItemsOrId);
8042
- this.id = ManualTrigger.resolveId(defaultItemsOrId, id);
8336
+ const trailing = idOrOptions ?? (typeof defaultItemsOrId === "string" ? defaultItemsOrId : void 0);
8337
+ const options = typeof trailing === "string" ? { id: trailing } : trailing ?? {};
8338
+ this.id = options.id;
8339
+ this.description = options.description;
8043
8340
  }
8044
8341
  static resolveDefaultItems(value) {
8045
8342
  if (typeof value === "string" || value === void 0) return;
8046
8343
  return this.itemsInputNormalizer.normalize(value);
8047
8344
  }
8048
- static resolveId(value, id) {
8049
- return typeof value === "string" ? value : id;
8050
- }
8051
8345
  inspectorSummary() {
8052
8346
  const rows = [{
8053
8347
  label: "Trigger",
@@ -8093,11 +8387,13 @@ var MapData = class {
8093
8387
  continueWhenEmptyOutput = true;
8094
8388
  icon = "lucide:square-pen";
8095
8389
  keepBinaries;
8390
+ description;
8096
8391
  constructor(name, map, options = {}) {
8097
8392
  this.name = name;
8098
8393
  this.map = map;
8099
8394
  this.options = options;
8100
8395
  this.keepBinaries = options.keepBinaries ?? true;
8396
+ this.description = options.description;
8101
8397
  }
8102
8398
  get id() {
8103
8399
  return this.options.id;
@@ -8170,10 +8466,14 @@ var Merge = class {
8170
8466
  kind = "node";
8171
8467
  type = MergeNode;
8172
8468
  icon = "lucide:merge@rot=90";
8173
- constructor(name, cfg = { mode: "passThrough" }, id) {
8469
+ id;
8470
+ description;
8471
+ constructor(name, cfg = { mode: "passThrough" }, idOrOptions) {
8174
8472
  this.name = name;
8175
8473
  this.cfg = cfg;
8176
- this.id = id;
8474
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8475
+ this.id = options?.id;
8476
+ this.description = options?.description;
8177
8477
  }
8178
8478
  inspectorSummary() {
8179
8479
  const rows = [{
@@ -8206,9 +8506,13 @@ var NoOp = class {
8206
8506
  type = NoOpNode;
8207
8507
  execution = { hint: "local" };
8208
8508
  icon = "lucide:circle-dashed";
8209
- constructor(name = "NoOp", id) {
8509
+ id;
8510
+ description;
8511
+ constructor(name = "NoOp", idOrOptions) {
8210
8512
  this.name = name;
8211
- this.id = id;
8513
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8514
+ this.id = options?.id;
8515
+ this.description = options?.description;
8212
8516
  }
8213
8517
  };
8214
8518
 
@@ -8274,12 +8578,16 @@ var SubWorkflow = class {
8274
8578
  kind = "node";
8275
8579
  type = SubWorkflowNode;
8276
8580
  icon = "lucide:workflow";
8277
- constructor(name, workflowId, upstreamRefs, startAt, id) {
8581
+ id;
8582
+ description;
8583
+ constructor(name, workflowId, upstreamRefs, startAt, idOrOptions) {
8278
8584
  this.name = name;
8279
8585
  this.workflowId = workflowId;
8280
8586
  this.upstreamRefs = upstreamRefs;
8281
8587
  this.startAt = startAt;
8282
- this.id = id;
8588
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8589
+ this.id = options?.id;
8590
+ this.description = options?.description;
8283
8591
  }
8284
8592
  inspectorSummary() {
8285
8593
  const rows = [{
@@ -8389,10 +8697,14 @@ var Wait = class {
8389
8697
  /** Pass-through empty batches should still advance to downstream nodes. */
8390
8698
  continueWhenEmptyOutput = true;
8391
8699
  icon = "lucide:hourglass";
8392
- constructor(name, milliseconds, id) {
8700
+ id;
8701
+ description;
8702
+ constructor(name, milliseconds, idOrOptions) {
8393
8703
  this.name = name;
8394
8704
  this.milliseconds = milliseconds;
8395
- this.id = id;
8705
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8706
+ this.id = options?.id;
8707
+ this.description = options?.description;
8396
8708
  }
8397
8709
  inspectorSummary() {
8398
8710
  const seconds = this.milliseconds / 1e3;
@@ -8447,11 +8759,15 @@ var WebhookTrigger = class WebhookTrigger {
8447
8759
  kind = "trigger";
8448
8760
  type = WebhookTriggerNode;
8449
8761
  icon = "lucide:globe";
8450
- constructor(name, args, handler = WebhookTrigger.defaultHandler, id) {
8762
+ id;
8763
+ description;
8764
+ constructor(name, args, handler = WebhookTrigger.defaultHandler, idOrOptions) {
8451
8765
  this.name = name;
8452
8766
  this.args = args;
8453
8767
  this.handler = handler;
8454
- this.id = id;
8768
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8769
+ this.id = options?.id;
8770
+ this.description = options?.description;
8455
8771
  }
8456
8772
  get endpointKey() {
8457
8773
  return this.args.endpointKey;
@@ -8508,11 +8824,21 @@ function createWorkflowBuilder(meta$2) {
8508
8824
 
8509
8825
  //#endregion
8510
8826
  //#region src/workflowAuthoring/WorkflowChatModelFactory.types.ts
8827
+ const VALID_COMPLEXITY = new Set([
8828
+ "low",
8829
+ "medium",
8830
+ "high",
8831
+ "xhigh"
8832
+ ]);
8511
8833
  var WorkflowChatModelFactory = class {
8512
8834
  static create(model) {
8513
8835
  if (typeof model !== "string") return model;
8514
8836
  const [provider, resolvedModel] = model.includes(":") ? model.split(":", 2) : ["openai", model];
8515
- if (provider === "codemation-managed") return new CodemationChatModelConfig("Codemation Managed", resolvedModel ?? "");
8837
+ if (provider === "codemation-managed") {
8838
+ const complexity = resolvedModel ?? "medium";
8839
+ if (!VALID_COMPLEXITY.has(complexity)) throw new Error(`Invalid managed complexity "${complexity}". Must be one of: low, medium, high, xhigh.`);
8840
+ return new CodemationChatModelConfig("Codemation Managed", complexity);
8841
+ }
8516
8842
  if (provider !== "openai") throw new Error(`Unsupported workflow().agent() model provider "${provider}".`);
8517
8843
  return new OpenAIChatModelConfig("OpenAI", resolvedModel);
8518
8844
  }
@@ -9280,7 +9606,6 @@ Object.defineProperty(exports, 'IsTestRunNode', {
9280
9606
  return IsTestRunNode;
9281
9607
  }
9282
9608
  });
9283
- exports.ManagedModelFetcher = ManagedModelFetcher;
9284
9609
  exports.ManualTrigger = ManualTrigger;
9285
9610
  Object.defineProperty(exports, 'ManualTriggerNode', {
9286
9611
  enumerable: true,