@codemation/core-nodes 0.10.2 → 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 (37) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/dist/index.cjs +259 -100
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +191 -66
  5. package/dist/index.d.ts +192 -67
  6. package/dist/index.js +259 -97
  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 +28 -0
  15. package/src/nodes/AIAgentNode.ts +77 -13
  16. package/src/nodes/AgentBinaryContentFactory.ts +74 -0
  17. package/src/nodes/CallbackNodeFactory.ts +9 -6
  18. package/src/nodes/CronTriggerFactory.ts +6 -2
  19. package/src/nodes/DeferredMetaToolStrategy.ts +8 -2
  20. package/src/nodes/ManualTriggerFactory.ts +15 -11
  21. package/src/nodes/WebhookTriggerFactory.ts +9 -2
  22. package/src/nodes/aggregate.ts +9 -2
  23. package/src/nodes/assertion.ts +3 -0
  24. package/src/nodes/filter.ts +9 -2
  25. package/src/nodes/httpRequest.ts +6 -1
  26. package/src/nodes/if.ts +9 -2
  27. package/src/nodes/isTestRun.ts +6 -2
  28. package/src/nodes/mapData.ts +4 -2
  29. package/src/nodes/merge.ts +9 -2
  30. package/src/nodes/noOp.ts +9 -2
  31. package/src/nodes/nodeOptions.types.ts +12 -0
  32. package/src/nodes/split.ts +9 -2
  33. package/src/nodes/subWorkflow.ts +9 -2
  34. package/src/nodes/switch.ts +7 -1
  35. package/src/nodes/wait.ts +9 -2
  36. package/src/workflowAuthoring/WorkflowChatModelFactory.types.ts +8 -2
  37. 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
@@ -4492,28 +4486,6 @@ var CodemationChatModelConfig = class {
4492
4486
  }
4493
4487
  };
4494
4488
 
4495
- //#endregion
4496
- //#region src/chatModels/ManagedModelFetcher.ts
4497
- /**
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.
4502
- */
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 [];
4513
- }
4514
- }
4515
- };
4516
-
4517
4489
  //#endregion
4518
4490
  //#region src/nodes/AgentMessageFactory.ts
4519
4491
  /**
@@ -5882,6 +5854,63 @@ AgentToolExecutionCoordinator = __decorate([
5882
5854
  __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
5855
  ], AgentToolExecutionCoordinator);
5884
5856
 
5857
+ //#endregion
5858
+ //#region src/nodes/AgentBinaryContentFactory.ts
5859
+ /**
5860
+ * Turns resolved file binaries into native AI SDK multimodal content parts and merges them into the
5861
+ * agent prompt. Images (`image/*`) become {@link ImagePart}s; every other type (PDFs, office docs,
5862
+ * CSV, JSON, …) becomes a {@link FilePart}. The provider maps these to its wire-level `image` /
5863
+ * `document` blocks; an unsupported file type surfaces as a provider error at runtime.
5864
+ *
5865
+ * Parts are appended to the LAST user message so the binary travels alongside the author's prompt
5866
+ * text (preserving any untrusted-source preamble that already wrapped that text). When no user
5867
+ * message exists, a new user message carrying only the binaries is appended.
5868
+ */
5869
+ var AgentBinaryContentFactory = class AgentBinaryContentFactory {
5870
+ static toContentPart(binary) {
5871
+ if (binary.mediaType.startsWith("image/")) return {
5872
+ type: "image",
5873
+ image: binary.base64,
5874
+ mediaType: binary.mediaType
5875
+ };
5876
+ return {
5877
+ type: "file",
5878
+ data: binary.base64,
5879
+ mediaType: binary.mediaType,
5880
+ ...binary.filename ? { filename: binary.filename } : {}
5881
+ };
5882
+ }
5883
+ static withBinaries(messages, binaries) {
5884
+ if (binaries.length === 0) return messages;
5885
+ const parts = binaries.map((binary) => AgentBinaryContentFactory.toContentPart(binary));
5886
+ const lastUserIndex = AgentBinaryContentFactory.lastUserMessageIndex(messages);
5887
+ if (lastUserIndex === -1) {
5888
+ const appended = {
5889
+ role: "user",
5890
+ content: parts
5891
+ };
5892
+ return [...messages, appended];
5893
+ }
5894
+ const next = [...messages];
5895
+ next[lastUserIndex] = AgentBinaryContentFactory.appendPartsToUserMessage(messages[lastUserIndex], parts);
5896
+ return next;
5897
+ }
5898
+ static lastUserMessageIndex(messages) {
5899
+ for (let index = messages.length - 1; index >= 0; index--) if (messages[index]?.role === "user") return index;
5900
+ return -1;
5901
+ }
5902
+ static appendPartsToUserMessage(message, parts) {
5903
+ const existing = typeof message.content === "string" ? message.content.length > 0 ? [{
5904
+ type: "text",
5905
+ text: message.content
5906
+ }] : [] : message.content;
5907
+ return {
5908
+ ...message,
5909
+ content: [...existing, ...parts]
5910
+ };
5911
+ }
5912
+ };
5913
+
5885
5914
  //#endregion
5886
5915
  //#region src/nodes/NodeBackedToolRuntime.ts
5887
5916
  var _ref$1, _ref2$1, _ref3$1, _ref4$1;
@@ -6080,11 +6109,18 @@ var DeferredMetaToolStrategy = class {
6080
6109
  mcpEntries = [];
6081
6110
  toolsByServerId = /* @__PURE__ */ new Map();
6082
6111
  foundToolIds = /* @__PURE__ */ new Set();
6112
+ /**
6113
+ * `jsonSchema` from the `ai` SDK, loaded lazily in {@link initialize} so the SDK
6114
+ * (~28MB RSS) stays off the boot path. `initialize` always runs before the sync
6115
+ * `getToolsForTurn` → `buildFindToolsDefinition` path, so this is set before use.
6116
+ */
6117
+ jsonSchema;
6083
6118
  constructor(bm25, warnFn) {
6084
6119
  this.bm25 = bm25;
6085
6120
  this.warnFn = warnFn;
6086
6121
  }
6087
6122
  async initialize(input) {
6123
+ this.jsonSchema = (await import("ai")).jsonSchema;
6088
6124
  this.nodeBackedTools = { ...input.nodeBackedTools };
6089
6125
  const pinnedIds = input.pinnedMcpTools ?? [];
6090
6126
  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 +6208,26 @@ var DeferredMetaToolStrategy = class {
6172
6208
  return [...this.foundToolIds];
6173
6209
  }
6174
6210
  buildFindToolsDefinition() {
6211
+ const inputSchemaRecord = {
6212
+ type: "object",
6213
+ properties: {
6214
+ query: {
6215
+ type: "string",
6216
+ description: "Natural language description of what you want to do."
6217
+ },
6218
+ limit: {
6219
+ type: "integer",
6220
+ minimum: 1,
6221
+ maximum: 10,
6222
+ description: `Maximum number of tools to return (default ${FIND_TOOLS_DEFAULT_LIMIT}).`
6223
+ }
6224
+ },
6225
+ required: ["query"],
6226
+ additionalProperties: false
6227
+ };
6175
6228
  return {
6176
6229
  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
- })
6230
+ inputSchema: this.jsonSchema(inputSchemaRecord)
6194
6231
  };
6195
6232
  }
6196
6233
  };
@@ -6224,6 +6261,13 @@ let AIAgentNode = class AIAgentNode$1 {
6224
6261
  inputSchema = unknown();
6225
6262
  connectionCredentialExecutionContextFactory;
6226
6263
  preparedByExecutionContext = /* @__PURE__ */ new WeakMap();
6264
+ /**
6265
+ * The `ai` SDK, loaded lazily in {@link execute} so the SDK (~28MB RSS) stays
6266
+ * off the boot path — non-AI workflows never load it. Every path runs through
6267
+ * `execute` → `ensureAiSdk` before any sync helper touches `this.aiSdk`.
6268
+ */
6269
+ aiSdk;
6270
+ aiSdkPromise = null;
6227
6271
  constructor(nodeResolver, credentialSessions, nodeBackedToolRuntime, executionHelpers, structuredOutputRunner, toolExecutionCoordinator, toolLoadingStrategyFactory, agentMcpIntegration) {
6228
6272
  this.nodeResolver = nodeResolver;
6229
6273
  this.nodeBackedToolRuntime = nodeBackedToolRuntime;
@@ -6236,6 +6280,7 @@ let AIAgentNode = class AIAgentNode$1 {
6236
6280
  }
6237
6281
  async execute(args) {
6238
6282
  const { ctx } = args;
6283
+ await this.ensureAiSdk();
6239
6284
  if (ctx.resumeContext) return this.executeResumed(args, ctx.resumeContext);
6240
6285
  const prepared = await this.getOrPrepareExecution(ctx);
6241
6286
  const itemWithMappedJson = {
@@ -6244,6 +6289,10 @@ let AIAgentNode = class AIAgentNode$1 {
6244
6289
  };
6245
6290
  return (await this.runAgentForItem(prepared, itemWithMappedJson, args.itemIndex, args.items)).json;
6246
6291
  }
6292
+ /** Load the `ai` SDK once per node instance (cached promise guards concurrent items). */
6293
+ async ensureAiSdk() {
6294
+ this.aiSdk = await (this.aiSdkPromise ??= import("ai"));
6295
+ }
6247
6296
  /**
6248
6297
  * Resume path: re-enters the agent loop after a HITL suspension.
6249
6298
  * Reconstructs the conversation from the checkpoint, injects the human decision
@@ -6386,7 +6435,7 @@ let AIAgentNode = class AIAgentNode$1 {
6386
6435
  const { ctx } = prepared;
6387
6436
  const itemInputsByPort = AgentItemPortMap.fromItem(item);
6388
6437
  const itemScopedTools = this.createItemScopedTools(prepared.resolvedTools, ctx, item, itemIndex, items);
6389
- const conversation = [...this.createPromptMessages(item, itemIndex, items, ctx)];
6438
+ const conversation = [...await this.createPromptMessages(item, itemIndex, items, ctx)];
6390
6439
  if (ctx.config.outputSchema && itemScopedTools.length === 0) {
6391
6440
  const structuredOutput = await this.structuredOutputRunner.resolve({
6392
6441
  model: prepared.model,
@@ -6630,7 +6679,7 @@ let AIAgentNode = class AIAgentNode$1 {
6630
6679
  const description = this.resolveHumanApprovalBehavior(entry.config) !== void 0 ? `${baseDescription} ${HITL_SOLO_CONSTRAINT_SENTENCE}` : baseDescription;
6631
6680
  toolSet[entry.config.name] = {
6632
6681
  description,
6633
- inputSchema: (0, ai.jsonSchema)(schemaRecord)
6682
+ inputSchema: this.aiSdk.jsonSchema(schemaRecord)
6634
6683
  };
6635
6684
  }
6636
6685
  return toolSet;
@@ -6661,7 +6710,7 @@ let AIAgentNode = class AIAgentNode$1 {
6661
6710
  const description = entry.humanApproval !== void 0 && baseDescription !== void 0 ? `${baseDescription} ${HITL_SOLO_CONSTRAINT_SENTENCE}` : entry.humanApproval !== void 0 ? HITL_SOLO_CONSTRAINT_SENTENCE : baseDescription;
6662
6711
  toolSet[entry.config.name] = {
6663
6712
  description,
6664
- inputSchema: (0, ai.jsonSchema)(schemaRecord)
6713
+ inputSchema: this.aiSdk.jsonSchema(schemaRecord)
6665
6714
  };
6666
6715
  }
6667
6716
  return toolSet;
@@ -6713,7 +6762,7 @@ let AIAgentNode = class AIAgentNode$1 {
6713
6762
  });
6714
6763
  try {
6715
6764
  const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
6716
- const result = await (0, ai.generateText)({
6765
+ const result = await this.aiSdk.generateText({
6717
6766
  model: model.languageModel,
6718
6767
  messages: [...messages],
6719
6768
  tools,
@@ -6832,8 +6881,8 @@ let AIAgentNode = class AIAgentNode$1 {
6832
6881
  schemaName: structuredOptions?.schemaName ?? "structured_output",
6833
6882
  requireObjectRoot: true
6834
6883
  }) : schema;
6835
- const outputSchema = ai.Output.object({ schema: (0, ai.jsonSchema)(schemaRecord) });
6836
- const result = await (0, ai.generateText)({
6884
+ const outputSchema = this.aiSdk.Output.object({ schema: this.aiSdk.jsonSchema(schemaRecord) });
6885
+ const result = await this.aiSdk.generateText({
6837
6886
  model: model.languageModel,
6838
6887
  messages: [...messages],
6839
6888
  experimental_output: outputSchema,
@@ -7104,7 +7153,7 @@ let AIAgentNode = class AIAgentNode$1 {
7104
7153
  const json = JSON.stringify(value);
7105
7154
  return JSON.parse(json);
7106
7155
  }
7107
- createPromptMessages(item, itemIndex, items, ctx) {
7156
+ async createPromptMessages(item, itemIndex, items, ctx) {
7108
7157
  const messages = __codemation_core.AgentMessageConfigNormalizer.resolveFromInputOrConfig(item.json, ctx.config, {
7109
7158
  item,
7110
7159
  itemIndex,
@@ -7112,7 +7161,47 @@ let AIAgentNode = class AIAgentNode$1 {
7112
7161
  ctx
7113
7162
  });
7114
7163
  const wrapped = this.wrapUntrustedSourceMessages(messages, item, ctx.config);
7115
- return AgentMessageFactory.createPromptMessages(wrapped);
7164
+ const promptMessages = AgentMessageFactory.createPromptMessages(wrapped);
7165
+ if (ctx.config.passBinariesToModel === false) return promptMessages;
7166
+ const attachments = this.selectBinaryAttachments(item, itemIndex, items, ctx);
7167
+ const binaries = await this.resolveInlineBinaries(attachments, ctx);
7168
+ return AgentBinaryContentFactory.withBinaries(promptMessages, binaries);
7169
+ }
7170
+ /**
7171
+ * Picks which attachments feed the passdown. When the author supplies `config.binaries`
7172
+ * (a static array or a per-item function — e.g. to forward binaries from an earlier node),
7173
+ * those replace the current item's attachments; otherwise the current item's `item.binary`
7174
+ * is used.
7175
+ */
7176
+ selectBinaryAttachments(item, itemIndex, items, ctx) {
7177
+ const manual = ctx.config.binaries;
7178
+ if (manual !== void 0) return typeof manual === "function" ? manual({
7179
+ item,
7180
+ itemIndex,
7181
+ items,
7182
+ ctx
7183
+ }) : manual;
7184
+ return item.binary ? Object.values(item.binary) : [];
7185
+ }
7186
+ /**
7187
+ * Reads every attachment through `ctx.binary` (storage-backed, by reference — never base64 on
7188
+ * `item.json`) and resolves it to inline base64 so the agent can pass it to the chat model as a
7189
+ * native multimodal block. Images become image blocks; every other type (PDF, office docs, CSV,
7190
+ * JSON, …) becomes a file block — we don't filter by media type, so any binary can be fed to the
7191
+ * model. If the provider rejects an unsupported type the error surfaces at runtime, and the
7192
+ * workflow can filter the binary upstream.
7193
+ */
7194
+ async resolveInlineBinaries(attachments, ctx) {
7195
+ const resolved = [];
7196
+ for (const attachment of attachments) {
7197
+ const bytes = await ctx.binary.getBytes(attachment);
7198
+ resolved.push({
7199
+ mediaType: attachment.mimeType,
7200
+ base64: Buffer.from(bytes).toString("base64"),
7201
+ ...attachment.filename ? { filename: attachment.filename } : {}
7202
+ });
7203
+ }
7204
+ return resolved;
7116
7205
  }
7117
7206
  /**
7118
7207
  * When `item.json.__source` matches an entry in `config.untrustedSources`
@@ -7226,6 +7315,7 @@ var AIAgent = class {
7226
7315
  chatModel;
7227
7316
  tools;
7228
7317
  id;
7318
+ description;
7229
7319
  retryPolicy;
7230
7320
  guardrails;
7231
7321
  inputSchema;
@@ -7233,12 +7323,15 @@ var AIAgent = class {
7233
7323
  mcpServers;
7234
7324
  pinnedMcpTools;
7235
7325
  untrustedSources;
7326
+ passBinariesToModel;
7327
+ binaries;
7236
7328
  constructor(options) {
7237
7329
  this.name = options.name;
7238
7330
  this.messages = options.messages;
7239
7331
  this.chatModel = options.chatModel;
7240
7332
  this.tools = options.tools ?? [];
7241
7333
  this.id = options.id;
7334
+ this.description = options.description;
7242
7335
  this.retryPolicy = options.retryPolicy ?? __codemation_core.RetryPolicy.defaultForAiAgent;
7243
7336
  this.guardrails = options.guardrails;
7244
7337
  this.inputSchema = options.inputSchema;
@@ -7246,6 +7339,8 @@ var AIAgent = class {
7246
7339
  this.mcpServers = options.mcpServers;
7247
7340
  this.pinnedMcpTools = options.pinnedMcpTools;
7248
7341
  this.untrustedSources = options.untrustedSources;
7342
+ this.passBinariesToModel = options.passBinariesToModel;
7343
+ this.binaries = options.binaries;
7249
7344
  }
7250
7345
  inspectorSummary() {
7251
7346
  const rows = [];
@@ -7316,12 +7411,14 @@ var Assertion = class {
7316
7411
  icon;
7317
7412
  name;
7318
7413
  id;
7414
+ description;
7319
7415
  emitsAssertions = true;
7320
7416
  assertions;
7321
7417
  constructor(options) {
7322
7418
  this.name = options.name ?? "Assertion";
7323
7419
  this.id = options.id;
7324
7420
  this.icon = options.icon ?? "lucide:check-circle";
7421
+ this.description = options.description;
7325
7422
  this.assertions = options.assertions;
7326
7423
  }
7327
7424
  inspectorSummary() {
@@ -7373,6 +7470,7 @@ var Callback = class Callback {
7373
7470
  icon = "lucide:braces";
7374
7471
  emptyBatchExecution = "runOnce";
7375
7472
  id;
7473
+ description;
7376
7474
  retryPolicy;
7377
7475
  nodeErrorHandler;
7378
7476
  declaredOutputPorts;
@@ -7384,6 +7482,7 @@ var Callback = class Callback {
7384
7482
  id: idOrOptions
7385
7483
  } : idOrOptions;
7386
7484
  this.id = resolvedOptions?.id;
7485
+ this.description = resolvedOptions?.description;
7387
7486
  this.retryPolicy = resolvedOptions?.retryPolicy;
7388
7487
  this.nodeErrorHandler = resolvedOptions?.nodeErrorHandler;
7389
7488
  this.declaredOutputPorts = resolvedOptions?.declaredOutputPorts;
@@ -7585,10 +7684,12 @@ var HttpRequest = class {
7585
7684
  type = HttpRequestNode;
7586
7685
  execution = { hint: "local" };
7587
7686
  icon = "lucide:globe";
7687
+ description;
7588
7688
  constructor(name, args = {}, retryPolicy = __codemation_core.RetryPolicy.defaultForHttp) {
7589
7689
  this.name = name;
7590
7690
  this.args = args;
7591
7691
  this.retryPolicy = retryPolicy;
7692
+ this.description = args.description;
7592
7693
  }
7593
7694
  get id() {
7594
7695
  return this.args.id;
@@ -7683,10 +7784,14 @@ var Aggregate = class {
7683
7784
  execution = { hint: "local" };
7684
7785
  keepBinaries = true;
7685
7786
  icon = "builtin:aggregate-rows";
7686
- constructor(name, aggregate, id) {
7787
+ id;
7788
+ description;
7789
+ constructor(name, aggregate, idOrOptions) {
7687
7790
  this.name = name;
7688
7791
  this.aggregate = aggregate;
7689
- this.id = id;
7792
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7793
+ this.id = options?.id;
7794
+ this.description = options?.description;
7690
7795
  }
7691
7796
  inspectorSummary() {
7692
7797
  const fnName = this.aggregate.name;
@@ -7717,10 +7822,14 @@ var Filter = class {
7717
7822
  type = FilterNode;
7718
7823
  execution = { hint: "local" };
7719
7824
  icon = "lucide:filter";
7720
- constructor(name, predicate, id) {
7825
+ id;
7826
+ description;
7827
+ constructor(name, predicate, idOrOptions) {
7721
7828
  this.name = name;
7722
7829
  this.predicate = predicate;
7723
- this.id = id;
7830
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7831
+ this.id = options?.id;
7832
+ this.description = options?.description;
7724
7833
  }
7725
7834
  inspectorSummary() {
7726
7835
  const fnName = this.predicate.name;
@@ -7795,10 +7904,14 @@ var If = class {
7795
7904
  execution = { hint: "local" };
7796
7905
  icon = "lucide:split@rot=90";
7797
7906
  declaredOutputPorts = ["true", "false"];
7798
- constructor(name, predicate, id) {
7907
+ id;
7908
+ description;
7909
+ constructor(name, predicate, idOrOptions) {
7799
7910
  this.name = name;
7800
7911
  this.predicate = predicate;
7801
- this.id = id;
7912
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7913
+ this.id = options?.id;
7914
+ this.description = options?.description;
7802
7915
  }
7803
7916
  inspectorSummary() {
7804
7917
  const fnName = this.predicate.name;
@@ -7838,9 +7951,12 @@ var IsTestRun = class {
7838
7951
  declaredOutputPorts = ["true", "false"];
7839
7952
  name;
7840
7953
  id;
7841
- constructor(name = "Is test run?", id) {
7954
+ description;
7955
+ constructor(name = "Is test run?", idOrOptions) {
7842
7956
  this.name = name;
7843
- this.id = id;
7957
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7958
+ this.id = options?.id;
7959
+ this.description = options?.description;
7844
7960
  }
7845
7961
  };
7846
7962
 
@@ -7869,11 +7985,15 @@ var Switch = class {
7869
7985
  execution = { hint: "local" };
7870
7986
  icon = "lucide:git-branch-plus";
7871
7987
  declaredOutputPorts;
7872
- constructor(name, cfg, id) {
7988
+ id;
7989
+ description;
7990
+ constructor(name, cfg, idOrOptions) {
7873
7991
  this.name = name;
7874
7992
  this.cfg = cfg;
7875
- this.id = id;
7876
7993
  this.declaredOutputPorts = [...new Set([...cfg.cases, cfg.defaultCase])].sort();
7994
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
7995
+ this.id = options?.id;
7996
+ this.description = options?.description;
7877
7997
  }
7878
7998
  inspectorSummary() {
7879
7999
  const rows = [{
@@ -7912,10 +8032,14 @@ var Split = class {
7912
8032
  */
7913
8033
  continueWhenEmptyOutput = true;
7914
8034
  icon = "builtin:split-rows";
7915
- constructor(name, getElements, id) {
8035
+ id;
8036
+ description;
8037
+ constructor(name, getElements, idOrOptions) {
7916
8038
  this.name = name;
7917
8039
  this.getElements = getElements;
7918
- this.id = id;
8040
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8041
+ this.id = options?.id;
8042
+ this.description = options?.description;
7919
8043
  }
7920
8044
  inspectorSummary() {
7921
8045
  const fnName = this.getElements.name;
@@ -7973,14 +8097,17 @@ var CronTrigger = class {
7973
8097
  type = CronTriggerNode;
7974
8098
  icon = "lucide:clock";
7975
8099
  id;
7976
- constructor(name, args, id) {
8100
+ description;
8101
+ constructor(name, args, idOrOptions) {
7977
8102
  this.name = name;
7978
8103
  this.args = args;
7979
8104
  new croner.Cron(args.schedule, {
7980
8105
  paused: true,
7981
8106
  timezone: args.timezone
7982
8107
  });
7983
- this.id = id;
8108
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8109
+ this.id = options?.id;
8110
+ this.description = options?.description;
7984
8111
  }
7985
8112
  get schedule() {
7986
8113
  return this.args.schedule;
@@ -8034,20 +8161,21 @@ var ManualTrigger = class ManualTrigger {
8034
8161
  icon = "lucide:play";
8035
8162
  defaultItems;
8036
8163
  id;
8164
+ description;
8037
8165
  /** Manual runs often emit an empty batch; still schedule downstream by default. */
8038
8166
  continueWhenEmptyOutput = true;
8039
- constructor(name = "Manual trigger", defaultItemsOrId, id) {
8167
+ constructor(name = "Manual trigger", defaultItemsOrId, idOrOptions) {
8040
8168
  this.name = name;
8041
8169
  this.defaultItems = ManualTrigger.resolveDefaultItems(defaultItemsOrId);
8042
- this.id = ManualTrigger.resolveId(defaultItemsOrId, id);
8170
+ const trailing = idOrOptions ?? (typeof defaultItemsOrId === "string" ? defaultItemsOrId : void 0);
8171
+ const options = typeof trailing === "string" ? { id: trailing } : trailing ?? {};
8172
+ this.id = options.id;
8173
+ this.description = options.description;
8043
8174
  }
8044
8175
  static resolveDefaultItems(value) {
8045
8176
  if (typeof value === "string" || value === void 0) return;
8046
8177
  return this.itemsInputNormalizer.normalize(value);
8047
8178
  }
8048
- static resolveId(value, id) {
8049
- return typeof value === "string" ? value : id;
8050
- }
8051
8179
  inspectorSummary() {
8052
8180
  const rows = [{
8053
8181
  label: "Trigger",
@@ -8093,11 +8221,13 @@ var MapData = class {
8093
8221
  continueWhenEmptyOutput = true;
8094
8222
  icon = "lucide:square-pen";
8095
8223
  keepBinaries;
8224
+ description;
8096
8225
  constructor(name, map, options = {}) {
8097
8226
  this.name = name;
8098
8227
  this.map = map;
8099
8228
  this.options = options;
8100
8229
  this.keepBinaries = options.keepBinaries ?? true;
8230
+ this.description = options.description;
8101
8231
  }
8102
8232
  get id() {
8103
8233
  return this.options.id;
@@ -8170,10 +8300,14 @@ var Merge = class {
8170
8300
  kind = "node";
8171
8301
  type = MergeNode;
8172
8302
  icon = "lucide:merge@rot=90";
8173
- constructor(name, cfg = { mode: "passThrough" }, id) {
8303
+ id;
8304
+ description;
8305
+ constructor(name, cfg = { mode: "passThrough" }, idOrOptions) {
8174
8306
  this.name = name;
8175
8307
  this.cfg = cfg;
8176
- this.id = id;
8308
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8309
+ this.id = options?.id;
8310
+ this.description = options?.description;
8177
8311
  }
8178
8312
  inspectorSummary() {
8179
8313
  const rows = [{
@@ -8206,9 +8340,13 @@ var NoOp = class {
8206
8340
  type = NoOpNode;
8207
8341
  execution = { hint: "local" };
8208
8342
  icon = "lucide:circle-dashed";
8209
- constructor(name = "NoOp", id) {
8343
+ id;
8344
+ description;
8345
+ constructor(name = "NoOp", idOrOptions) {
8210
8346
  this.name = name;
8211
- this.id = id;
8347
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8348
+ this.id = options?.id;
8349
+ this.description = options?.description;
8212
8350
  }
8213
8351
  };
8214
8352
 
@@ -8274,12 +8412,16 @@ var SubWorkflow = class {
8274
8412
  kind = "node";
8275
8413
  type = SubWorkflowNode;
8276
8414
  icon = "lucide:workflow";
8277
- constructor(name, workflowId, upstreamRefs, startAt, id) {
8415
+ id;
8416
+ description;
8417
+ constructor(name, workflowId, upstreamRefs, startAt, idOrOptions) {
8278
8418
  this.name = name;
8279
8419
  this.workflowId = workflowId;
8280
8420
  this.upstreamRefs = upstreamRefs;
8281
8421
  this.startAt = startAt;
8282
- this.id = id;
8422
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8423
+ this.id = options?.id;
8424
+ this.description = options?.description;
8283
8425
  }
8284
8426
  inspectorSummary() {
8285
8427
  const rows = [{
@@ -8389,10 +8531,14 @@ var Wait = class {
8389
8531
  /** Pass-through empty batches should still advance to downstream nodes. */
8390
8532
  continueWhenEmptyOutput = true;
8391
8533
  icon = "lucide:hourglass";
8392
- constructor(name, milliseconds, id) {
8534
+ id;
8535
+ description;
8536
+ constructor(name, milliseconds, idOrOptions) {
8393
8537
  this.name = name;
8394
8538
  this.milliseconds = milliseconds;
8395
- this.id = id;
8539
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8540
+ this.id = options?.id;
8541
+ this.description = options?.description;
8396
8542
  }
8397
8543
  inspectorSummary() {
8398
8544
  const seconds = this.milliseconds / 1e3;
@@ -8447,11 +8593,15 @@ var WebhookTrigger = class WebhookTrigger {
8447
8593
  kind = "trigger";
8448
8594
  type = WebhookTriggerNode;
8449
8595
  icon = "lucide:globe";
8450
- constructor(name, args, handler = WebhookTrigger.defaultHandler, id) {
8596
+ id;
8597
+ description;
8598
+ constructor(name, args, handler = WebhookTrigger.defaultHandler, idOrOptions) {
8451
8599
  this.name = name;
8452
8600
  this.args = args;
8453
8601
  this.handler = handler;
8454
- this.id = id;
8602
+ const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
8603
+ this.id = options?.id;
8604
+ this.description = options?.description;
8455
8605
  }
8456
8606
  get endpointKey() {
8457
8607
  return this.args.endpointKey;
@@ -8508,11 +8658,21 @@ function createWorkflowBuilder(meta$2) {
8508
8658
 
8509
8659
  //#endregion
8510
8660
  //#region src/workflowAuthoring/WorkflowChatModelFactory.types.ts
8661
+ const VALID_COMPLEXITY = new Set([
8662
+ "low",
8663
+ "medium",
8664
+ "high",
8665
+ "xhigh"
8666
+ ]);
8511
8667
  var WorkflowChatModelFactory = class {
8512
8668
  static create(model) {
8513
8669
  if (typeof model !== "string") return model;
8514
8670
  const [provider, resolvedModel] = model.includes(":") ? model.split(":", 2) : ["openai", model];
8515
- if (provider === "codemation-managed") return new CodemationChatModelConfig("Codemation Managed", resolvedModel ?? "");
8671
+ if (provider === "codemation-managed") {
8672
+ const complexity = resolvedModel ?? "medium";
8673
+ if (!VALID_COMPLEXITY.has(complexity)) throw new Error(`Invalid managed complexity "${complexity}". Must be one of: low, medium, high, xhigh.`);
8674
+ return new CodemationChatModelConfig("Codemation Managed", complexity);
8675
+ }
8516
8676
  if (provider !== "openai") throw new Error(`Unsupported workflow().agent() model provider "${provider}".`);
8517
8677
  return new OpenAIChatModelConfig("OpenAI", resolvedModel);
8518
8678
  }
@@ -9280,7 +9440,6 @@ Object.defineProperty(exports, 'IsTestRunNode', {
9280
9440
  return IsTestRunNode;
9281
9441
  }
9282
9442
  });
9283
- exports.ManagedModelFetcher = ManagedModelFetcher;
9284
9443
  exports.ManualTrigger = ManualTrigger;
9285
9444
  Object.defineProperty(exports, 'ManualTriggerNode', {
9286
9445
  enumerable: true,