@codemation/core-nodes 0.10.1 → 0.12.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 +125 -0
  2. package/dist/index.cjs +273 -108
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +212 -71
  5. package/dist/index.d.ts +213 -72
  6. package/dist/index.js +273 -105
  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/http/HttpBodyBuilder.ts +9 -0
  14. package/src/http/httpRequest.types.ts +10 -1
  15. package/src/index.ts +1 -1
  16. package/src/nodes/AIAgentConfig.ts +28 -0
  17. package/src/nodes/AIAgentNode.ts +84 -17
  18. package/src/nodes/AgentBinaryContentFactory.ts +74 -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 +7 -2
  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.js CHANGED
@@ -1,9 +1,7 @@
1
1
  import { AgentConfigInspector, AgentConnectionNodeCollector, AgentGuardrailDefaults, AgentMessageConfigNormalizer, CallableToolConfig, ChildExecutionScopeFactory, CodemationTelemetryAttributeNames, CodemationTelemetryMetricNames, ConnectionInvocationIdFactory, ConnectionNodeIdFactory, CoreTokens, DefinedNodeRegistry, GenAiTelemetryAttributeNames, InboxChannelResolverToken, ItemExprResolver, ItemsInputNormalizer, NodeBackedToolConfig, NodeOutputNormalizer, RetryPolicy, RunnableOutputBehaviorResolver, SuspensionRequest, WorkflowBuilder, chatModel, defineCredential, defineHumanApprovalNode, defineNode, emitPorts, getOriginIndexFromItem, inject, injectable, isPortsEmission, node } from "@codemation/core";
2
2
  import dns from "node:dns/promises";
3
- import { createOpenAI } from "@ai-sdk/openai";
4
3
  import { CredentialResolverFactory } from "@codemation/core/bootstrap";
5
4
  import { createHash, createHmac, randomBytes } from "node:crypto";
6
- import { Output, generateText, jsonSchema } from "ai";
7
5
  import { Cron } from "croner";
8
6
 
9
7
  //#region src/credentials/ApiKeyCredentialType.ts
@@ -495,10 +493,13 @@ var HttpRequestExecutor = class {
495
493
  var HttpBodyBuilder = class {
496
494
  async build(spec, item, ctx) {
497
495
  if (!spec || spec.kind === "none") return;
498
- if (spec.kind === "json") return {
499
- body: JSON.stringify(spec.data),
500
- contentType: "application/json"
501
- };
496
+ if (spec.kind === "json") {
497
+ if (typeof spec.data === "string") throw new Error("HttpRequest body kind:\"json\" expects a serializable object for \"data\", but received a string. Pass the object directly (e.g. { a: 1 }), not a pre-stringified JSON string (JSON.stringify(...)).");
498
+ return {
499
+ body: JSON.stringify(spec.data),
500
+ contentType: "application/json"
501
+ };
502
+ }
502
503
  if (spec.kind === "form") {
503
504
  const params = new URLSearchParams();
504
505
  for (const [key, value] of Object.entries(spec.data)) params.append(key, value);
@@ -671,6 +672,7 @@ function __decorate(decorators, target, key, desc) {
671
672
  let OpenAIChatModelFactory = class OpenAIChatModelFactory$1 {
672
673
  async create(args) {
673
674
  const session = await args.ctx.getCredential(args.config.credentialSlotKey);
675
+ const { createOpenAI } = await import("@ai-sdk/openai");
674
676
  return {
675
677
  languageModel: createOpenAI({
676
678
  apiKey: session.apiKey,
@@ -4415,27 +4417,24 @@ function buildHmacAuthHeader(workspaceId, pairingSecret, method, url, body, over
4415
4417
  //#endregion
4416
4418
  //#region src/chatModels/CodemationChatModelFactory.ts
4417
4419
  let CodemationChatModelFactory = class CodemationChatModelFactory$1 {
4418
- create(args) {
4420
+ async create(args) {
4419
4421
  const gatewayUrl = process.env["LLM_GATEWAY_URL"];
4420
4422
  if (!gatewayUrl) throw new Error("Codemation managed AI not available in this environment (LLM_GATEWAY_URL is not set).");
4421
4423
  const workspaceId = process.env["WORKSPACE_ID"];
4422
4424
  const pairingSecret = process.env["WORKSPACE_PAIRING_SECRET"];
4423
4425
  if (!workspaceId || !pairingSecret) throw new Error("Codemation managed AI not available in this environment (workspace pairing is not configured).");
4424
4426
  const hmacFetch = managedHmacFetchFactory(workspaceId, pairingSecret);
4425
- const languageModel = createOpenAI({
4426
- baseURL: `${gatewayUrl}/v1`,
4427
- apiKey: "codemation-managed",
4428
- fetch: hmacFetch
4429
- }).chat(args.config.model);
4430
- return Promise.resolve({
4431
- languageModel,
4432
- modelName: args.config.model,
4427
+ const { createAnthropic } = await import("@ai-sdk/anthropic");
4428
+ return {
4429
+ languageModel: createAnthropic({
4430
+ baseURL: `${gatewayUrl}/v1`,
4431
+ apiKey: "codemation-managed",
4432
+ fetch: hmacFetch
4433
+ })(args.config.complexity),
4434
+ modelName: args.config.complexity,
4433
4435
  provider: "codemation-managed",
4434
- defaultCallOptions: {
4435
- maxOutputTokens: args.config.options?.maxTokens,
4436
- temperature: args.config.options?.temperature
4437
- }
4438
- });
4436
+ defaultCallOptions: { maxOutputTokens: args.config.options?.maxTokens }
4437
+ };
4439
4438
  }
4440
4439
  };
4441
4440
  CodemationChatModelFactory = __decorate([chatModel({ packageName: "@codemation/core-nodes" })], CodemationChatModelFactory);
@@ -4447,11 +4446,11 @@ var CodemationChatModelConfig = class {
4447
4446
  presentation;
4448
4447
  provider = "codemation-managed";
4449
4448
  modelName;
4450
- constructor(name, model, presentationIn, options) {
4449
+ constructor(name, complexity, presentationIn, options) {
4451
4450
  this.name = name;
4452
- this.model = model;
4451
+ this.complexity = complexity;
4453
4452
  this.options = options;
4454
- this.modelName = model;
4453
+ this.modelName = complexity;
4455
4454
  this.presentation = presentationIn ?? {
4456
4455
  icon: "lucide:bot",
4457
4456
  label: name
@@ -4459,28 +4458,6 @@ var CodemationChatModelConfig = class {
4459
4458
  }
4460
4459
  };
4461
4460
 
4462
- //#endregion
4463
- //#region src/chatModels/ManagedModelFetcher.ts
4464
- /**
4465
- * Fetches the active platform-managed model allowlist from the CP.
4466
- * Reads CONTROL_PLANE_URL from the workspace process env.
4467
- * Returns an empty array if the env var is absent or the fetch fails.
4468
- * Cache the result per session — the allowlist changes infrequently.
4469
- */
4470
- var ManagedModelFetcher = class {
4471
- async fetch() {
4472
- const cpUrl = process.env["CONTROL_PLANE_URL"];
4473
- if (!cpUrl) return [];
4474
- try {
4475
- const res = await globalThis.fetch(`${cpUrl}/api/llm/managed-models`);
4476
- if (!res.ok) return [];
4477
- return await res.json();
4478
- } catch {
4479
- return [];
4480
- }
4481
- }
4482
- };
4483
-
4484
4461
  //#endregion
4485
4462
  //#region src/nodes/AgentMessageFactory.ts
4486
4463
  /**
@@ -5849,6 +5826,63 @@ AgentToolExecutionCoordinator = __decorate([
5849
5826
  __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])
5850
5827
  ], AgentToolExecutionCoordinator);
5851
5828
 
5829
+ //#endregion
5830
+ //#region src/nodes/AgentBinaryContentFactory.ts
5831
+ /**
5832
+ * Turns resolved file binaries into native AI SDK multimodal content parts and merges them into the
5833
+ * agent prompt. Images (`image/*`) become {@link ImagePart}s; every other type (PDFs, office docs,
5834
+ * CSV, JSON, …) becomes a {@link FilePart}. The provider maps these to its wire-level `image` /
5835
+ * `document` blocks; an unsupported file type surfaces as a provider error at runtime.
5836
+ *
5837
+ * Parts are appended to the LAST user message so the binary travels alongside the author's prompt
5838
+ * text (preserving any untrusted-source preamble that already wrapped that text). When no user
5839
+ * message exists, a new user message carrying only the binaries is appended.
5840
+ */
5841
+ var AgentBinaryContentFactory = class AgentBinaryContentFactory {
5842
+ static toContentPart(binary) {
5843
+ if (binary.mediaType.startsWith("image/")) return {
5844
+ type: "image",
5845
+ image: binary.base64,
5846
+ mediaType: binary.mediaType
5847
+ };
5848
+ return {
5849
+ type: "file",
5850
+ data: binary.base64,
5851
+ mediaType: binary.mediaType,
5852
+ ...binary.filename ? { filename: binary.filename } : {}
5853
+ };
5854
+ }
5855
+ static withBinaries(messages, binaries) {
5856
+ if (binaries.length === 0) return messages;
5857
+ const parts = binaries.map((binary) => AgentBinaryContentFactory.toContentPart(binary));
5858
+ const lastUserIndex = AgentBinaryContentFactory.lastUserMessageIndex(messages);
5859
+ if (lastUserIndex === -1) {
5860
+ const appended = {
5861
+ role: "user",
5862
+ content: parts
5863
+ };
5864
+ return [...messages, appended];
5865
+ }
5866
+ const next = [...messages];
5867
+ next[lastUserIndex] = AgentBinaryContentFactory.appendPartsToUserMessage(messages[lastUserIndex], parts);
5868
+ return next;
5869
+ }
5870
+ static lastUserMessageIndex(messages) {
5871
+ for (let index = messages.length - 1; index >= 0; index--) if (messages[index]?.role === "user") return index;
5872
+ return -1;
5873
+ }
5874
+ static appendPartsToUserMessage(message, parts) {
5875
+ const existing = typeof message.content === "string" ? message.content.length > 0 ? [{
5876
+ type: "text",
5877
+ text: message.content
5878
+ }] : [] : message.content;
5879
+ return {
5880
+ ...message,
5881
+ content: [...existing, ...parts]
5882
+ };
5883
+ }
5884
+ };
5885
+
5852
5886
  //#endregion
5853
5887
  //#region src/nodes/NodeBackedToolRuntime.ts
5854
5888
  var _ref$1, _ref2$1, _ref3$1, _ref4$1;
@@ -6047,11 +6081,18 @@ var DeferredMetaToolStrategy = class {
6047
6081
  mcpEntries = [];
6048
6082
  toolsByServerId = /* @__PURE__ */ new Map();
6049
6083
  foundToolIds = /* @__PURE__ */ new Set();
6084
+ /**
6085
+ * `jsonSchema` from the `ai` SDK, loaded lazily in {@link initialize} so the SDK
6086
+ * (~28MB RSS) stays off the boot path. `initialize` always runs before the sync
6087
+ * `getToolsForTurn` → `buildFindToolsDefinition` path, so this is set before use.
6088
+ */
6089
+ jsonSchema;
6050
6090
  constructor(bm25, warnFn) {
6051
6091
  this.bm25 = bm25;
6052
6092
  this.warnFn = warnFn;
6053
6093
  }
6054
6094
  async initialize(input) {
6095
+ this.jsonSchema = (await import("ai")).jsonSchema;
6055
6096
  this.nodeBackedTools = { ...input.nodeBackedTools };
6056
6097
  const pinnedIds = input.pinnedMcpTools ?? [];
6057
6098
  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}.`);
@@ -6139,25 +6180,26 @@ var DeferredMetaToolStrategy = class {
6139
6180
  return [...this.foundToolIds];
6140
6181
  }
6141
6182
  buildFindToolsDefinition() {
6183
+ const inputSchemaRecord = {
6184
+ type: "object",
6185
+ properties: {
6186
+ query: {
6187
+ type: "string",
6188
+ description: "Natural language description of what you want to do."
6189
+ },
6190
+ limit: {
6191
+ type: "integer",
6192
+ minimum: 1,
6193
+ maximum: 10,
6194
+ description: `Maximum number of tools to return (default ${FIND_TOOLS_DEFAULT_LIMIT}).`
6195
+ }
6196
+ },
6197
+ required: ["query"],
6198
+ additionalProperties: false
6199
+ };
6142
6200
  return {
6143
6201
  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.",
6144
- inputSchema: jsonSchema({
6145
- type: "object",
6146
- properties: {
6147
- query: {
6148
- type: "string",
6149
- description: "Natural language description of what you want to do."
6150
- },
6151
- limit: {
6152
- type: "integer",
6153
- minimum: 1,
6154
- maximum: 10,
6155
- description: `Maximum number of tools to return (default ${FIND_TOOLS_DEFAULT_LIMIT}).`
6156
- }
6157
- },
6158
- required: ["query"],
6159
- additionalProperties: false
6160
- })
6202
+ inputSchema: this.jsonSchema(inputSchemaRecord)
6161
6203
  };
6162
6204
  }
6163
6205
  };
@@ -6191,6 +6233,13 @@ let AIAgentNode = class AIAgentNode$1 {
6191
6233
  inputSchema = unknown();
6192
6234
  connectionCredentialExecutionContextFactory;
6193
6235
  preparedByExecutionContext = /* @__PURE__ */ new WeakMap();
6236
+ /**
6237
+ * The `ai` SDK, loaded lazily in {@link execute} so the SDK (~28MB RSS) stays
6238
+ * off the boot path — non-AI workflows never load it. Every path runs through
6239
+ * `execute` → `ensureAiSdk` before any sync helper touches `this.aiSdk`.
6240
+ */
6241
+ aiSdk;
6242
+ aiSdkPromise = null;
6194
6243
  constructor(nodeResolver, credentialSessions, nodeBackedToolRuntime, executionHelpers, structuredOutputRunner, toolExecutionCoordinator, toolLoadingStrategyFactory, agentMcpIntegration) {
6195
6244
  this.nodeResolver = nodeResolver;
6196
6245
  this.nodeBackedToolRuntime = nodeBackedToolRuntime;
@@ -6203,6 +6252,7 @@ let AIAgentNode = class AIAgentNode$1 {
6203
6252
  }
6204
6253
  async execute(args) {
6205
6254
  const { ctx } = args;
6255
+ await this.ensureAiSdk();
6206
6256
  if (ctx.resumeContext) return this.executeResumed(args, ctx.resumeContext);
6207
6257
  const prepared = await this.getOrPrepareExecution(ctx);
6208
6258
  const itemWithMappedJson = {
@@ -6211,6 +6261,10 @@ let AIAgentNode = class AIAgentNode$1 {
6211
6261
  };
6212
6262
  return (await this.runAgentForItem(prepared, itemWithMappedJson, args.itemIndex, args.items)).json;
6213
6263
  }
6264
+ /** Load the `ai` SDK once per node instance (cached promise guards concurrent items). */
6265
+ async ensureAiSdk() {
6266
+ this.aiSdk = await (this.aiSdkPromise ??= import("ai"));
6267
+ }
6214
6268
  /**
6215
6269
  * Resume path: re-enters the agent loop after a HITL suspension.
6216
6270
  * Reconstructs the conversation from the checkpoint, injects the human decision
@@ -6353,7 +6407,7 @@ let AIAgentNode = class AIAgentNode$1 {
6353
6407
  const { ctx } = prepared;
6354
6408
  const itemInputsByPort = AgentItemPortMap.fromItem(item);
6355
6409
  const itemScopedTools = this.createItemScopedTools(prepared.resolvedTools, ctx, item, itemIndex, items);
6356
- const conversation = [...this.createPromptMessages(item, itemIndex, items, ctx)];
6410
+ const conversation = [...await this.createPromptMessages(item, itemIndex, items, ctx)];
6357
6411
  if (ctx.config.outputSchema && itemScopedTools.length === 0) {
6358
6412
  const structuredOutput = await this.structuredOutputRunner.resolve({
6359
6413
  model: prepared.model,
@@ -6540,14 +6594,17 @@ let AIAgentNode = class AIAgentNode$1 {
6540
6594
  });
6541
6595
  }
6542
6596
  /**
6543
- * Detects whether a tool config is backed by a `defineHumanApprovalNode` marker
6544
- * and returns the HITL behavior config, or `undefined` when not a HITL tool.
6597
+ * Resolves the HITL behavior for a tool binding, or `undefined` when it is not a HITL tool.
6598
+ * A binding is HITL if either the backing node carries a `defineHumanApprovalNode` marker or the
6599
+ * binding sets a per-binding `onRejected` via `asTool(..., { onRejected })`. The per-binding value
6600
+ * wins over the node marker, so two tools backed by the same node can reject differently.
6545
6601
  */
6546
6602
  resolveHumanApprovalBehavior(config$1) {
6547
6603
  if (!this.isNodeBackedToolConfig(config$1)) return void 0;
6548
6604
  const marker = config$1.node.humanApprovalToolBehavior;
6549
- if (marker === void 0) return void 0;
6550
- return { onRejected: marker.onRejected ?? "return" };
6605
+ const perBinding = config$1.onRejected;
6606
+ if (marker === void 0 && perBinding === void 0) return void 0;
6607
+ return { onRejected: perBinding ?? marker?.onRejected ?? "return" };
6551
6608
  }
6552
6609
  /**
6553
6610
  * Invoke a text turn using the merged tool set from item-scoped tools (coordinator-managed)
@@ -6594,7 +6651,7 @@ let AIAgentNode = class AIAgentNode$1 {
6594
6651
  const description = this.resolveHumanApprovalBehavior(entry.config) !== void 0 ? `${baseDescription} ${HITL_SOLO_CONSTRAINT_SENTENCE}` : baseDescription;
6595
6652
  toolSet[entry.config.name] = {
6596
6653
  description,
6597
- inputSchema: jsonSchema(schemaRecord)
6654
+ inputSchema: this.aiSdk.jsonSchema(schemaRecord)
6598
6655
  };
6599
6656
  }
6600
6657
  return toolSet;
@@ -6625,7 +6682,7 @@ let AIAgentNode = class AIAgentNode$1 {
6625
6682
  const description = entry.humanApproval !== void 0 && baseDescription !== void 0 ? `${baseDescription} ${HITL_SOLO_CONSTRAINT_SENTENCE}` : entry.humanApproval !== void 0 ? HITL_SOLO_CONSTRAINT_SENTENCE : baseDescription;
6626
6683
  toolSet[entry.config.name] = {
6627
6684
  description,
6628
- inputSchema: jsonSchema(schemaRecord)
6685
+ inputSchema: this.aiSdk.jsonSchema(schemaRecord)
6629
6686
  };
6630
6687
  }
6631
6688
  return toolSet;
@@ -6677,7 +6734,7 @@ let AIAgentNode = class AIAgentNode$1 {
6677
6734
  });
6678
6735
  try {
6679
6736
  const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
6680
- const result = await generateText({
6737
+ const result = await this.aiSdk.generateText({
6681
6738
  model: model.languageModel,
6682
6739
  messages: [...messages],
6683
6740
  tools,
@@ -6796,8 +6853,8 @@ let AIAgentNode = class AIAgentNode$1 {
6796
6853
  schemaName: structuredOptions?.schemaName ?? "structured_output",
6797
6854
  requireObjectRoot: true
6798
6855
  }) : schema;
6799
- const outputSchema = Output.object({ schema: jsonSchema(schemaRecord) });
6800
- const result = await generateText({
6856
+ const outputSchema = this.aiSdk.Output.object({ schema: this.aiSdk.jsonSchema(schemaRecord) });
6857
+ const result = await this.aiSdk.generateText({
6801
6858
  model: model.languageModel,
6802
6859
  messages: [...messages],
6803
6860
  experimental_output: outputSchema,
@@ -7068,7 +7125,7 @@ let AIAgentNode = class AIAgentNode$1 {
7068
7125
  const json = JSON.stringify(value);
7069
7126
  return JSON.parse(json);
7070
7127
  }
7071
- createPromptMessages(item, itemIndex, items, ctx) {
7128
+ async createPromptMessages(item, itemIndex, items, ctx) {
7072
7129
  const messages = AgentMessageConfigNormalizer.resolveFromInputOrConfig(item.json, ctx.config, {
7073
7130
  item,
7074
7131
  itemIndex,
@@ -7076,7 +7133,47 @@ let AIAgentNode = class AIAgentNode$1 {
7076
7133
  ctx
7077
7134
  });
7078
7135
  const wrapped = this.wrapUntrustedSourceMessages(messages, item, ctx.config);
7079
- return AgentMessageFactory.createPromptMessages(wrapped);
7136
+ const promptMessages = AgentMessageFactory.createPromptMessages(wrapped);
7137
+ if (ctx.config.passBinariesToModel === false) return promptMessages;
7138
+ const attachments = this.selectBinaryAttachments(item, itemIndex, items, ctx);
7139
+ const binaries = await this.resolveInlineBinaries(attachments, ctx);
7140
+ return AgentBinaryContentFactory.withBinaries(promptMessages, binaries);
7141
+ }
7142
+ /**
7143
+ * Picks which attachments feed the passdown. When the author supplies `config.binaries`
7144
+ * (a static array or a per-item function — e.g. to forward binaries from an earlier node),
7145
+ * those replace the current item's attachments; otherwise the current item's `item.binary`
7146
+ * is used.
7147
+ */
7148
+ selectBinaryAttachments(item, itemIndex, items, ctx) {
7149
+ const manual = ctx.config.binaries;
7150
+ if (manual !== void 0) return typeof manual === "function" ? manual({
7151
+ item,
7152
+ itemIndex,
7153
+ items,
7154
+ ctx
7155
+ }) : manual;
7156
+ return item.binary ? Object.values(item.binary) : [];
7157
+ }
7158
+ /**
7159
+ * Reads every attachment through `ctx.binary` (storage-backed, by reference — never base64 on
7160
+ * `item.json`) and resolves it to inline base64 so the agent can pass it to the chat model as a
7161
+ * native multimodal block. Images become image blocks; every other type (PDF, office docs, CSV,
7162
+ * JSON, …) becomes a file block — we don't filter by media type, so any binary can be fed to the
7163
+ * model. If the provider rejects an unsupported type the error surfaces at runtime, and the
7164
+ * workflow can filter the binary upstream.
7165
+ */
7166
+ async resolveInlineBinaries(attachments, ctx) {
7167
+ const resolved = [];
7168
+ for (const attachment of attachments) {
7169
+ const bytes = await ctx.binary.getBytes(attachment);
7170
+ resolved.push({
7171
+ mediaType: attachment.mimeType,
7172
+ base64: Buffer.from(bytes).toString("base64"),
7173
+ ...attachment.filename ? { filename: attachment.filename } : {}
7174
+ });
7175
+ }
7176
+ return resolved;
7080
7177
  }
7081
7178
  /**
7082
7179
  * When `item.json.__source` matches an entry in `config.untrustedSources`
@@ -7190,6 +7287,7 @@ var AIAgent = class {
7190
7287
  chatModel;
7191
7288
  tools;
7192
7289
  id;
7290
+ description;
7193
7291
  retryPolicy;
7194
7292
  guardrails;
7195
7293
  inputSchema;
@@ -7197,12 +7295,15 @@ var AIAgent = class {
7197
7295
  mcpServers;
7198
7296
  pinnedMcpTools;
7199
7297
  untrustedSources;
7298
+ passBinariesToModel;
7299
+ binaries;
7200
7300
  constructor(options) {
7201
7301
  this.name = options.name;
7202
7302
  this.messages = options.messages;
7203
7303
  this.chatModel = options.chatModel;
7204
7304
  this.tools = options.tools ?? [];
7205
7305
  this.id = options.id;
7306
+ this.description = options.description;
7206
7307
  this.retryPolicy = options.retryPolicy ?? RetryPolicy.defaultForAiAgent;
7207
7308
  this.guardrails = options.guardrails;
7208
7309
  this.inputSchema = options.inputSchema;
@@ -7210,6 +7311,8 @@ var AIAgent = class {
7210
7311
  this.mcpServers = options.mcpServers;
7211
7312
  this.pinnedMcpTools = options.pinnedMcpTools;
7212
7313
  this.untrustedSources = options.untrustedSources;
7314
+ this.passBinariesToModel = options.passBinariesToModel;
7315
+ this.binaries = options.binaries;
7213
7316
  }
7214
7317
  inspectorSummary() {
7215
7318
  const rows = [];
@@ -7280,12 +7383,14 @@ var Assertion = class {
7280
7383
  icon;
7281
7384
  name;
7282
7385
  id;
7386
+ description;
7283
7387
  emitsAssertions = true;
7284
7388
  assertions;
7285
7389
  constructor(options) {
7286
7390
  this.name = options.name ?? "Assertion";
7287
7391
  this.id = options.id;
7288
7392
  this.icon = options.icon ?? "lucide:check-circle";
7393
+ this.description = options.description;
7289
7394
  this.assertions = options.assertions;
7290
7395
  }
7291
7396
  inspectorSummary() {
@@ -7337,6 +7442,7 @@ var Callback = class Callback {
7337
7442
  icon = "lucide:braces";
7338
7443
  emptyBatchExecution = "runOnce";
7339
7444
  id;
7445
+ description;
7340
7446
  retryPolicy;
7341
7447
  nodeErrorHandler;
7342
7448
  declaredOutputPorts;
@@ -7348,6 +7454,7 @@ var Callback = class Callback {
7348
7454
  id: idOrOptions
7349
7455
  } : idOrOptions;
7350
7456
  this.id = resolvedOptions?.id;
7457
+ this.description = resolvedOptions?.description;
7351
7458
  this.retryPolicy = resolvedOptions?.retryPolicy;
7352
7459
  this.nodeErrorHandler = resolvedOptions?.nodeErrorHandler;
7353
7460
  this.declaredOutputPorts = resolvedOptions?.declaredOutputPorts;
@@ -7549,10 +7656,12 @@ var HttpRequest = class {
7549
7656
  type = HttpRequestNode;
7550
7657
  execution = { hint: "local" };
7551
7658
  icon = "lucide:globe";
7659
+ description;
7552
7660
  constructor(name, args = {}, retryPolicy = RetryPolicy.defaultForHttp) {
7553
7661
  this.name = name;
7554
7662
  this.args = args;
7555
7663
  this.retryPolicy = retryPolicy;
7664
+ this.description = args.description;
7556
7665
  }
7557
7666
  get id() {
7558
7667
  return this.args.id;
@@ -7647,10 +7756,14 @@ var Aggregate = class {
7647
7756
  execution = { hint: "local" };
7648
7757
  keepBinaries = true;
7649
7758
  icon = "builtin:aggregate-rows";
7650
- constructor(name, aggregate, id) {
7759
+ id;
7760
+ description;
7761
+ constructor(name, aggregate, idOrOptions) {
7651
7762
  this.name = name;
7652
7763
  this.aggregate = aggregate;
7653
- this.id = id;
7764
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7765
+ this.id = options?.id;
7766
+ this.description = options?.description;
7654
7767
  }
7655
7768
  inspectorSummary() {
7656
7769
  const fnName = this.aggregate.name;
@@ -7681,10 +7794,14 @@ var Filter = class {
7681
7794
  type = FilterNode;
7682
7795
  execution = { hint: "local" };
7683
7796
  icon = "lucide:filter";
7684
- constructor(name, predicate, id) {
7797
+ id;
7798
+ description;
7799
+ constructor(name, predicate, idOrOptions) {
7685
7800
  this.name = name;
7686
7801
  this.predicate = predicate;
7687
- this.id = id;
7802
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7803
+ this.id = options?.id;
7804
+ this.description = options?.description;
7688
7805
  }
7689
7806
  inspectorSummary() {
7690
7807
  const fnName = this.predicate.name;
@@ -7759,10 +7876,14 @@ var If = class {
7759
7876
  execution = { hint: "local" };
7760
7877
  icon = "lucide:split@rot=90";
7761
7878
  declaredOutputPorts = ["true", "false"];
7762
- constructor(name, predicate, id) {
7879
+ id;
7880
+ description;
7881
+ constructor(name, predicate, idOrOptions) {
7763
7882
  this.name = name;
7764
7883
  this.predicate = predicate;
7765
- this.id = id;
7884
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7885
+ this.id = options?.id;
7886
+ this.description = options?.description;
7766
7887
  }
7767
7888
  inspectorSummary() {
7768
7889
  const fnName = this.predicate.name;
@@ -7802,9 +7923,12 @@ var IsTestRun = class {
7802
7923
  declaredOutputPorts = ["true", "false"];
7803
7924
  name;
7804
7925
  id;
7805
- constructor(name = "Is test run?", id) {
7926
+ description;
7927
+ constructor(name = "Is test run?", idOrOptions) {
7806
7928
  this.name = name;
7807
- this.id = id;
7929
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7930
+ this.id = options?.id;
7931
+ this.description = options?.description;
7808
7932
  }
7809
7933
  };
7810
7934
 
@@ -7833,11 +7957,15 @@ var Switch = class {
7833
7957
  execution = { hint: "local" };
7834
7958
  icon = "lucide:git-branch-plus";
7835
7959
  declaredOutputPorts;
7836
- constructor(name, cfg, id) {
7960
+ id;
7961
+ description;
7962
+ constructor(name, cfg, idOrOptions) {
7837
7963
  this.name = name;
7838
7964
  this.cfg = cfg;
7839
- this.id = id;
7840
7965
  this.declaredOutputPorts = [...new Set([...cfg.cases, cfg.defaultCase])].sort();
7966
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7967
+ this.id = options?.id;
7968
+ this.description = options?.description;
7841
7969
  }
7842
7970
  inspectorSummary() {
7843
7971
  const rows = [{
@@ -7876,10 +8004,14 @@ var Split = class {
7876
8004
  */
7877
8005
  continueWhenEmptyOutput = true;
7878
8006
  icon = "builtin:split-rows";
7879
- constructor(name, getElements, id) {
8007
+ id;
8008
+ description;
8009
+ constructor(name, getElements, idOrOptions) {
7880
8010
  this.name = name;
7881
8011
  this.getElements = getElements;
7882
- this.id = id;
8012
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8013
+ this.id = options?.id;
8014
+ this.description = options?.description;
7883
8015
  }
7884
8016
  inspectorSummary() {
7885
8017
  const fnName = this.getElements.name;
@@ -7937,14 +8069,17 @@ var CronTrigger = class {
7937
8069
  type = CronTriggerNode;
7938
8070
  icon = "lucide:clock";
7939
8071
  id;
7940
- constructor(name, args, id) {
8072
+ description;
8073
+ constructor(name, args, idOrOptions) {
7941
8074
  this.name = name;
7942
8075
  this.args = args;
7943
8076
  new Cron(args.schedule, {
7944
8077
  paused: true,
7945
8078
  timezone: args.timezone
7946
8079
  });
7947
- this.id = id;
8080
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8081
+ this.id = options?.id;
8082
+ this.description = options?.description;
7948
8083
  }
7949
8084
  get schedule() {
7950
8085
  return this.args.schedule;
@@ -7998,20 +8133,21 @@ var ManualTrigger = class ManualTrigger {
7998
8133
  icon = "lucide:play";
7999
8134
  defaultItems;
8000
8135
  id;
8136
+ description;
8001
8137
  /** Manual runs often emit an empty batch; still schedule downstream by default. */
8002
8138
  continueWhenEmptyOutput = true;
8003
- constructor(name = "Manual trigger", defaultItemsOrId, id) {
8139
+ constructor(name = "Manual trigger", defaultItemsOrId, idOrOptions) {
8004
8140
  this.name = name;
8005
8141
  this.defaultItems = ManualTrigger.resolveDefaultItems(defaultItemsOrId);
8006
- this.id = ManualTrigger.resolveId(defaultItemsOrId, id);
8142
+ const trailing = idOrOptions ?? (typeof defaultItemsOrId === "string" ? defaultItemsOrId : void 0);
8143
+ const options = typeof trailing === "string" ? { id: trailing } : trailing ?? {};
8144
+ this.id = options.id;
8145
+ this.description = options.description;
8007
8146
  }
8008
8147
  static resolveDefaultItems(value) {
8009
8148
  if (typeof value === "string" || value === void 0) return;
8010
8149
  return this.itemsInputNormalizer.normalize(value);
8011
8150
  }
8012
- static resolveId(value, id) {
8013
- return typeof value === "string" ? value : id;
8014
- }
8015
8151
  inspectorSummary() {
8016
8152
  const rows = [{
8017
8153
  label: "Trigger",
@@ -8057,11 +8193,13 @@ var MapData = class {
8057
8193
  continueWhenEmptyOutput = true;
8058
8194
  icon = "lucide:square-pen";
8059
8195
  keepBinaries;
8196
+ description;
8060
8197
  constructor(name, map, options = {}) {
8061
8198
  this.name = name;
8062
8199
  this.map = map;
8063
8200
  this.options = options;
8064
8201
  this.keepBinaries = options.keepBinaries ?? true;
8202
+ this.description = options.description;
8065
8203
  }
8066
8204
  get id() {
8067
8205
  return this.options.id;
@@ -8134,10 +8272,14 @@ var Merge = class {
8134
8272
  kind = "node";
8135
8273
  type = MergeNode;
8136
8274
  icon = "lucide:merge@rot=90";
8137
- constructor(name, cfg = { mode: "passThrough" }, id) {
8275
+ id;
8276
+ description;
8277
+ constructor(name, cfg = { mode: "passThrough" }, idOrOptions) {
8138
8278
  this.name = name;
8139
8279
  this.cfg = cfg;
8140
- this.id = id;
8280
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8281
+ this.id = options?.id;
8282
+ this.description = options?.description;
8141
8283
  }
8142
8284
  inspectorSummary() {
8143
8285
  const rows = [{
@@ -8170,9 +8312,13 @@ var NoOp = class {
8170
8312
  type = NoOpNode;
8171
8313
  execution = { hint: "local" };
8172
8314
  icon = "lucide:circle-dashed";
8173
- constructor(name = "NoOp", id) {
8315
+ id;
8316
+ description;
8317
+ constructor(name = "NoOp", idOrOptions) {
8174
8318
  this.name = name;
8175
- this.id = id;
8319
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8320
+ this.id = options?.id;
8321
+ this.description = options?.description;
8176
8322
  }
8177
8323
  };
8178
8324
 
@@ -8238,12 +8384,16 @@ var SubWorkflow = class {
8238
8384
  kind = "node";
8239
8385
  type = SubWorkflowNode;
8240
8386
  icon = "lucide:workflow";
8241
- constructor(name, workflowId, upstreamRefs, startAt, id) {
8387
+ id;
8388
+ description;
8389
+ constructor(name, workflowId, upstreamRefs, startAt, idOrOptions) {
8242
8390
  this.name = name;
8243
8391
  this.workflowId = workflowId;
8244
8392
  this.upstreamRefs = upstreamRefs;
8245
8393
  this.startAt = startAt;
8246
- this.id = id;
8394
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8395
+ this.id = options?.id;
8396
+ this.description = options?.description;
8247
8397
  }
8248
8398
  inspectorSummary() {
8249
8399
  const rows = [{
@@ -8353,10 +8503,14 @@ var Wait = class {
8353
8503
  /** Pass-through empty batches should still advance to downstream nodes. */
8354
8504
  continueWhenEmptyOutput = true;
8355
8505
  icon = "lucide:hourglass";
8356
- constructor(name, milliseconds, id) {
8506
+ id;
8507
+ description;
8508
+ constructor(name, milliseconds, idOrOptions) {
8357
8509
  this.name = name;
8358
8510
  this.milliseconds = milliseconds;
8359
- this.id = id;
8511
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8512
+ this.id = options?.id;
8513
+ this.description = options?.description;
8360
8514
  }
8361
8515
  inspectorSummary() {
8362
8516
  const seconds = this.milliseconds / 1e3;
@@ -8411,11 +8565,15 @@ var WebhookTrigger = class WebhookTrigger {
8411
8565
  kind = "trigger";
8412
8566
  type = WebhookTriggerNode;
8413
8567
  icon = "lucide:globe";
8414
- constructor(name, args, handler = WebhookTrigger.defaultHandler, id) {
8568
+ id;
8569
+ description;
8570
+ constructor(name, args, handler = WebhookTrigger.defaultHandler, idOrOptions) {
8415
8571
  this.name = name;
8416
8572
  this.args = args;
8417
8573
  this.handler = handler;
8418
- this.id = id;
8574
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8575
+ this.id = options?.id;
8576
+ this.description = options?.description;
8419
8577
  }
8420
8578
  get endpointKey() {
8421
8579
  return this.args.endpointKey;
@@ -8472,11 +8630,21 @@ function createWorkflowBuilder(meta$2) {
8472
8630
 
8473
8631
  //#endregion
8474
8632
  //#region src/workflowAuthoring/WorkflowChatModelFactory.types.ts
8633
+ const VALID_COMPLEXITY = new Set([
8634
+ "low",
8635
+ "medium",
8636
+ "high",
8637
+ "xhigh"
8638
+ ]);
8475
8639
  var WorkflowChatModelFactory = class {
8476
8640
  static create(model) {
8477
8641
  if (typeof model !== "string") return model;
8478
8642
  const [provider, resolvedModel] = model.includes(":") ? model.split(":", 2) : ["openai", model];
8479
- if (provider === "codemation-managed") return new CodemationChatModelConfig("Codemation Managed", resolvedModel ?? "");
8643
+ if (provider === "codemation-managed") {
8644
+ const complexity = resolvedModel ?? "medium";
8645
+ if (!VALID_COMPLEXITY.has(complexity)) throw new Error(`Invalid managed complexity "${complexity}". Must be one of: low, medium, high, xhigh.`);
8646
+ return new CodemationChatModelConfig("Codemation Managed", complexity);
8647
+ }
8480
8648
  if (provider !== "openai") throw new Error(`Unsupported workflow().agent() model provider "${provider}".`);
8481
8649
  return new OpenAIChatModelConfig("OpenAI", resolvedModel);
8482
8650
  }
@@ -9113,5 +9281,5 @@ const codemationDocumentScannerNode = defineNode({
9113
9281
  });
9114
9282
 
9115
9283
  //#endregion
9116
- export { AIAgent, AIAgentConnectionWorkflowExpander, AIAgentExecutionHelpersFactory, AIAgentNode, AgentItemPortMap, AgentMessageFactory, AgentOutputFactory, AgentStructuredOutputRepairPromptFactory, AgentStructuredOutputRunner, AgentToolCallPortMap, AgentToolErrorClassifier, AgentToolExecutionCoordinator, AgentToolRepairExhaustedError, AgentToolRepairPolicy, Aggregate, AggregateNode, Assertion, AssertionNode, BM25Index, Callback, CallbackNode, CallbackResultNormalizer, CodemationChatModelConfig, CodemationChatModelFactory, ConnectionCredentialExecutionContextFactory, ConnectionCredentialNode, ConnectionCredentialNodeConfig, ConnectionCredentialNodeConfigFactory, CronTrigger, CronTriggerNode, DeferredMetaToolStrategy, DeferredMetaToolStrategyFactory, Filter, FilterNode, HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES, HttpRequest, HttpRequestNode, If, IfNode, IsTestRun, IsTestRunNode, ManagedModelFetcher, ManualTrigger, ManualTriggerNode, MapData, MapDataNode, Merge, MergeNode, NoOp, NoOpNode, OpenAIChatModelConfig, OpenAIChatModelFactory, OpenAiChatModelPresets, OpenAiStrictJsonSchemaFactory, SSRFBlockedError, Split, SplitNode, SsrfGuard, SubWorkflow, SubWorkflowNode, Switch, SwitchNode, TestTrigger, TestTriggerNode, Wait, WaitDuration, WaitNode, WebhookRespondNowAndContinueError, WebhookRespondNowError, WebhookTrigger, WebhookTriggerNode, WorkflowAuthoringBuilder, WorkflowBranchBuilder, WorkflowChain, apiKeyCredentialType, basicAuthCredentialType, bearerTokenCredentialType, codemationDocumentScannerNode, collectionDeleteNode, collectionFindOneNode, collectionGetNode, collectionInsertNode, collectionListNode, collectionUpdateNode, createWorkflowBuilder, defineRestNode, inboxApproval, oauth2ClientCredentialsType, openAiChatModelPresets, registerCoreNodes, workflow };
9284
+ export { AIAgent, AIAgentConnectionWorkflowExpander, AIAgentExecutionHelpersFactory, AIAgentNode, AgentItemPortMap, AgentMessageFactory, AgentOutputFactory, AgentStructuredOutputRepairPromptFactory, AgentStructuredOutputRunner, AgentToolCallPortMap, AgentToolErrorClassifier, AgentToolExecutionCoordinator, AgentToolRepairExhaustedError, AgentToolRepairPolicy, Aggregate, AggregateNode, Assertion, AssertionNode, BM25Index, Callback, CallbackNode, CallbackResultNormalizer, CodemationChatModelConfig, CodemationChatModelFactory, ConnectionCredentialExecutionContextFactory, ConnectionCredentialNode, ConnectionCredentialNodeConfig, ConnectionCredentialNodeConfigFactory, CronTrigger, CronTriggerNode, DeferredMetaToolStrategy, DeferredMetaToolStrategyFactory, Filter, FilterNode, HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES, HttpRequest, HttpRequestNode, If, IfNode, IsTestRun, IsTestRunNode, ManualTrigger, ManualTriggerNode, MapData, MapDataNode, Merge, MergeNode, NoOp, NoOpNode, OpenAIChatModelConfig, OpenAIChatModelFactory, OpenAiChatModelPresets, OpenAiStrictJsonSchemaFactory, SSRFBlockedError, Split, SplitNode, SsrfGuard, SubWorkflow, SubWorkflowNode, Switch, SwitchNode, TestTrigger, TestTriggerNode, Wait, WaitDuration, WaitNode, WebhookRespondNowAndContinueError, WebhookRespondNowError, WebhookTrigger, WebhookTriggerNode, WorkflowAuthoringBuilder, WorkflowBranchBuilder, WorkflowChain, apiKeyCredentialType, basicAuthCredentialType, bearerTokenCredentialType, codemationDocumentScannerNode, collectionDeleteNode, collectionFindOneNode, collectionGetNode, collectionInsertNode, collectionListNode, collectionUpdateNode, createWorkflowBuilder, defineRestNode, inboxApproval, oauth2ClientCredentialsType, openAiChatModelPresets, registerCoreNodes, workflow };
9117
9285
  //# sourceMappingURL=index.js.map