@codemation/core-nodes 0.12.0 → 0.14.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 (73) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/index.cjs +180 -413
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +99 -1303
  5. package/dist/index.d.ts +99 -1303
  6. package/dist/index.js +181 -415
  7. package/dist/index.js.map +1 -1
  8. package/dist/metadata.json +1 -1
  9. package/package.json +2 -2
  10. package/src/authoring/defineRestNode.types.ts +0 -84
  11. package/src/canvasIconName.ts +0 -7
  12. package/src/chatModels/CodemationChatModelConfig.ts +0 -10
  13. package/src/chatModels/CodemationChatModelFactory.ts +0 -7
  14. package/src/chatModels/ManagedHmacSignerFactory.types.ts +0 -35
  15. package/src/chatModels/OpenAIChatModelFactory.ts +0 -2
  16. package/src/chatModels/OpenAiChatModelPresetsFactory.ts +0 -5
  17. package/src/chatModels/OpenAiCredentialSession.ts +0 -1
  18. package/src/chatModels/OpenAiStrictJsonSchemaFactory.ts +0 -21
  19. package/src/credentials/ApiKeyCredentialType.ts +0 -3
  20. package/src/credentials/BasicAuthCredentialType.ts +0 -4
  21. package/src/credentials/BearerTokenCredentialType.ts +0 -4
  22. package/src/credentials/OAuth2ClientCredentialsTypeFactory.ts +0 -19
  23. package/src/credentials/OAuth2TokenExchangeFactory.ts +0 -7
  24. package/src/http/HttpBodyBuilder.ts +0 -16
  25. package/src/http/HttpRequestExecutor.ts +0 -35
  26. package/src/http/HttpUrlBuilder.ts +0 -4
  27. package/src/http/SSRFBlockedError.ts +0 -4
  28. package/src/http/SsrfGuard.ts +10 -50
  29. package/src/http/httpRequest.types.ts +0 -49
  30. package/src/index.ts +1 -0
  31. package/src/nodes/AIAgentConfig.ts +3 -39
  32. package/src/nodes/AIAgentExecutionHelpersFactory.ts +0 -37
  33. package/src/nodes/AIAgentNode.ts +4 -134
  34. package/src/nodes/AgentBinaryContentFactory.ts +0 -12
  35. package/src/nodes/AgentLoopCheckpoint.types.ts +0 -13
  36. package/src/nodes/AgentMessageFactory.ts +17 -19
  37. package/src/nodes/AgentStructuredOutputRunner.ts +0 -17
  38. package/src/nodes/AgentToolExecutionCoordinator.ts +0 -12
  39. package/src/nodes/AgentToolResultContentFactory.ts +126 -0
  40. package/src/nodes/AssertionNode.ts +0 -14
  41. package/src/nodes/BM25Index.ts +0 -14
  42. package/src/nodes/ConnectionCredentialExecutionContextFactory.ts +0 -5
  43. package/src/nodes/ConnectionCredentialNode.ts +0 -4
  44. package/src/nodes/CronTriggerFactory.ts +0 -9
  45. package/src/nodes/DeferredMetaToolStrategy.ts +0 -18
  46. package/src/nodes/DeferredMetaToolStrategyFactory.ts +0 -5
  47. package/src/nodes/HttpRequestNodeFactory.ts +0 -14
  48. package/src/nodes/InboxApprovalNode.types.ts +0 -16
  49. package/src/nodes/IsTestRunNode.ts +0 -8
  50. package/src/nodes/ManualTriggerFactory.ts +0 -3
  51. package/src/nodes/ManualTriggerNode.ts +0 -4
  52. package/src/nodes/MergeNode.ts +0 -1
  53. package/src/nodes/NodeBackedToolRuntime.ts +0 -14
  54. package/src/nodes/SubWorkflowNode.ts +0 -3
  55. package/src/nodes/SwitchNode.ts +0 -3
  56. package/src/nodes/TestTriggerNode.ts +0 -9
  57. package/src/nodes/aiAgentSupport.types.ts +0 -16
  58. package/src/nodes/assertion.ts +0 -10
  59. package/src/nodes/codemationDocumentScannerNode.ts +0 -18
  60. package/src/nodes/collections/collectionListNode.types.ts +0 -1
  61. package/src/nodes/httpRequest.ts +0 -68
  62. package/src/nodes/isTestRun.ts +0 -4
  63. package/src/nodes/mapData.ts +0 -1
  64. package/src/nodes/merge.ts +0 -4
  65. package/src/nodes/mergeExecutionUtils.types.ts +0 -3
  66. package/src/nodes/nodeOptions.types.ts +0 -8
  67. package/src/nodes/schedulePollingTrigger.ts +37 -0
  68. package/src/nodes/split.ts +0 -4
  69. package/src/nodes/testTrigger.ts +0 -21
  70. package/src/nodes/wait.ts +0 -1
  71. package/src/nodes/webhookTriggerNode.ts +0 -5
  72. package/src/register.types.ts +0 -10
  73. package/src/workflows/AIAgentConnectionWorkflowExpander.ts +0 -3
package/dist/index.js CHANGED
@@ -1,13 +1,10 @@
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";
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, definePollingTrigger, emitPorts, getOriginIndexFromItem, inject, injectable, isPortsEmission, node } from "@codemation/core";
2
2
  import dns from "node:dns/promises";
3
3
  import { CredentialResolverFactory } from "@codemation/core/bootstrap";
4
4
  import { createHash, createHmac, randomBytes } from "node:crypto";
5
5
  import { Cron } from "croner";
6
6
 
7
7
  //#region src/credentials/ApiKeyCredentialType.ts
8
- /**
9
- * API key credential that injects a key either as an HTTP header or a query parameter.
10
- */
11
8
  const apiKeyCredentialType = defineCredential({
12
9
  key: "core-nodes.api-key",
13
10
  label: "API Key",
@@ -55,10 +52,6 @@ const apiKeyCredentialType = defineCredential({
55
52
 
56
53
  //#endregion
57
54
  //#region src/credentials/BasicAuthCredentialType.ts
58
- /**
59
- * HTTP Basic authentication credential.
60
- * Session sets `Authorization: Basic <base64(username:password)>`.
61
- */
62
55
  const basicAuthCredentialType = defineCredential({
63
56
  key: "core-nodes.basic-auth",
64
57
  label: "Basic Auth",
@@ -96,10 +89,6 @@ const basicAuthCredentialType = defineCredential({
96
89
 
97
90
  //#endregion
98
91
  //#region src/credentials/BearerTokenCredentialType.ts
99
- /**
100
- * Simple Bearer token credential.
101
- * Session sets `Authorization: Bearer <token>` on every request.
102
- */
103
92
  const bearerTokenCredentialType = defineCredential({
104
93
  key: "core-nodes.bearer-token",
105
94
  label: "Bearer Token",
@@ -158,24 +147,6 @@ var OAuth2TokenExchangeFactory = class {
158
147
 
159
148
  //#endregion
160
149
  //#region src/credentials/OAuth2ClientCredentialsTypeFactory.ts
161
- /**
162
- * OAuth2 client-credentials flow credential.
163
- *
164
- * This is a machine-to-machine flow: no user redirect occurs. The session
165
- * POSTs to the configured `tokenUrl` with `client_credentials` grant, caches
166
- * the resulting access token for the duration of the session, and injects it
167
- * as `Authorization: Bearer <token>` on each request.
168
- *
169
- * Token caching is per-session only (one createSession call = one token fetch
170
- * at most). Cross-session caching would require host-level state and is out of
171
- * scope here. Because the engine creates a fresh session per execution, a new
172
- * token is fetched once per node activation.
173
- *
174
- * NOTE: `auth` is intentionally omitted from the definition. The OAuth2
175
- * `auth: { kind: "oauth2" }` shape signals an authorization-code / user-redirect
176
- * flow; using it here would cause the host UI to render an OAuth consent button
177
- * that goes nowhere. Client-credentials is a purely server-side flow.
178
- */
179
150
  const oauth2ClientCredentialsType = defineCredential({
180
151
  key: "core-nodes.oauth2-client-credentials",
181
152
  label: "OAuth2 Client Credentials",
@@ -259,10 +230,6 @@ const oauth2ClientCredentialsType = defineCredential({
259
230
 
260
231
  //#endregion
261
232
  //#region src/http/SSRFBlockedError.ts
262
- /**
263
- * Thrown when an HTTP request target resolves to a private, link-local, or
264
- * loopback address and `allowPrivateNetworkTargets` is not set.
265
- */
266
233
  var SSRFBlockedError = class extends Error {
267
234
  resolvedIp;
268
235
  constructor(host, resolvedIp) {
@@ -274,26 +241,7 @@ var SSRFBlockedError = class extends Error {
274
241
 
275
242
  //#endregion
276
243
  //#region src/http/SsrfGuard.ts
277
- /** Emitted once per process when NODE_ENV=production and no allowedOutboundHosts is set. */
278
244
  let _productionNoAllowlistWarned = false;
279
- /**
280
- * Guards HTTP requests against Server-Side Request Forgery (SSRF) by
281
- * DNS-resolving the target host and rejecting private/link-local/loopback
282
- * addresses.
283
- *
284
- * Blocked ranges:
285
- * - RFC-1918: 10/8, 172.16/12, 192.168/16
286
- * - Link-local: 169.254/16
287
- * - Loopback: 127/8, ::1
288
- *
289
- * When `allowedOutboundHosts` is set, every resolved DNS target must match
290
- * at least one entry in the list (exact hostname or `*.example.com` wildcard).
291
- * When unset, existing behaviour applies: private ranges blocked, public allowed.
292
- *
293
- * Call {@link check} before making any outbound HTTP request.
294
- * Pass `allowPrivate: true` to bypass the private-network guard for trusted workflows
295
- * (allowedOutboundHosts allowlist is still applied when set).
296
- */
297
245
  var SsrfGuard = class {
298
246
  constructor(allowedOutboundHosts) {
299
247
  this.allowedOutboundHosts = allowedOutboundHosts;
@@ -302,15 +250,6 @@ var SsrfGuard = class {
302
250
  console.warn("[SsrfGuard] WARNING: NODE_ENV=production but no allowedOutboundHosts is configured for HttpRequest. All public destinations are permitted. Set allowedOutboundHosts to restrict outbound traffic.");
303
251
  }
304
252
  }
305
- /**
306
- * Resolves the host of `url` via DNS and throws {@link SSRFBlockedError}
307
- * if any resolved address falls in a blocked range, or if the host does not
308
- * match the operator-configured allowlist (when set).
309
- *
310
- * @param url - Fully-qualified URL of the intended request target.
311
- * @param allowPrivate - When `true`, the private-network check is skipped.
312
- * The allowedOutboundHosts check is still applied when set.
313
- */
314
253
  async check(url, allowPrivate) {
315
254
  if (allowPrivate && !this.allowedOutboundHosts?.length) return;
316
255
  let host;
@@ -334,10 +273,6 @@ var SsrfGuard = class {
334
273
  }
335
274
  for (const { address } of addresses) if (this.isPrivateAddress(address)) throw new SSRFBlockedError(host, address);
336
275
  }
337
- /**
338
- * Returns true when `host` matches at least one entry in `allowedOutboundHosts`.
339
- * Supports exact hostnames (`api.example.com`) and wildcard prefixes (`*.example.com`).
340
- */
341
276
  isHostAllowed(host) {
342
277
  for (const allowed of this.allowedOutboundHosts ?? []) if (allowed.startsWith("*.")) {
343
278
  const suffix = allowed.slice(1);
@@ -371,21 +306,6 @@ var SsrfGuard = class {
371
306
 
372
307
  //#endregion
373
308
  //#region src/http/HttpRequestExecutor.ts
374
- /**
375
- * Executes a single HTTP request described by {@link HttpRequestSpec}.
376
- *
377
- * - Credential sessions provide header/query deltas via `applyToRequest`.
378
- * - Body encoding is delegated to {@link HttpBodyBuilder}.
379
- * - URL query merging is delegated to {@link HttpUrlBuilder}.
380
- * - SSRF protection is delegated to {@link SsrfGuard} (injected).
381
- * - Binary response bodies: when `download.mode` triggers binary attach, the
382
- * `bodyBinaryName` field is set in the result but the body is NOT read here.
383
- * Callers that need binary attachment should use `buildRequest` to get the
384
- * resolved URL + init and make the fetch + binary attach themselves.
385
- *
386
- * Collaborators (`fetch`, body builder, url builder, ssrfGuard) are injected so
387
- * callers own construction at composition roots and tests can supply deterministic stubs.
388
- */
389
309
  var HttpRequestExecutor = class {
390
310
  constructor(fetchFn, bodyBuilder, urlBuilder, ssrfGuard) {
391
311
  this.fetchFn = fetchFn;
@@ -393,14 +313,6 @@ var HttpRequestExecutor = class {
393
313
  this.urlBuilder = urlBuilder;
394
314
  this.ssrfGuard = ssrfGuard;
395
315
  }
396
- /**
397
- * Builds the fetch init (headers, query, body) from the spec + credential delta,
398
- * returning both the resolved URL and the RequestInit so callers can make the
399
- * actual fetch call themselves (useful for streaming / binary attach).
400
- *
401
- * Also performs SSRF protection via the injected {@link SsrfGuard} before
402
- * returning — throws {@link SSRFBlockedError} if the target is a private address.
403
- */
404
316
  async buildRequest(spec, item) {
405
317
  await this.ssrfGuard.check(spec.url, spec.allowPrivateNetworkTargets ?? false);
406
318
  const credentialDelta = spec.credential?.applyToRequest(spec) ?? {};
@@ -424,12 +336,6 @@ var HttpRequestExecutor = class {
424
336
  }
425
337
  };
426
338
  }
427
- /**
428
- * Executes an HTTP request and returns parsed result.
429
- * For binary downloads (when `shouldAttachBody` is true), the body is NOT consumed
430
- * and callers must call `ctx.binary.attach` directly using the resolved URL + init
431
- * (available via `buildRequest`).
432
- */
433
339
  async execute(spec, item) {
434
340
  const { url: resolvedUrl, init } = await this.buildRequest(spec, item);
435
341
  const response = await this.fetchFn(resolvedUrl, init);
@@ -486,10 +392,6 @@ var HttpRequestExecutor = class {
486
392
 
487
393
  //#endregion
488
394
  //#region src/http/HttpBodyBuilder.ts
489
- /**
490
- * Builds a fetch-compatible `BodyInit` + Content-Type pair from an {@link HttpBodySpec}.
491
- * Multipart binaries are read from `item.binary` via `ctx.binary.openReadStream`.
492
- */
493
395
  var HttpBodyBuilder = class {
494
396
  async build(spec, item, ctx) {
495
397
  if (!spec || spec.kind === "none") return;
@@ -560,10 +462,6 @@ var HttpBodyBuilder = class {
560
462
 
561
463
  //#endregion
562
464
  //#region src/http/HttpUrlBuilder.ts
563
- /**
564
- * Merges query parameters into a base URL.
565
- * Handles both scalar and array values, and preserves any existing params.
566
- */
567
465
  var HttpUrlBuilder = class {
568
466
  build(baseUrl, query) {
569
467
  if (!query || Object.keys(query).length === 0) return baseUrl;
@@ -576,39 +474,12 @@ var HttpUrlBuilder = class {
576
474
 
577
475
  //#endregion
578
476
  //#region src/authoring/defineRestNode.types.ts
579
- /**
580
- * Substitutes `{name}` placeholders in a path template using values from `params`.
581
- */
582
477
  function substitutePath(template, params) {
583
478
  return template.replace(/\{([^}]+)}/g, (_match, key) => {
584
479
  const value = params[key];
585
480
  return value !== void 0 ? String(value) : `{${key}}`;
586
481
  });
587
482
  }
588
- /**
589
- * Declarative helper for creating thin API-wrapper nodes.
590
- *
591
- * Usage:
592
- * ```ts
593
- * export const postMessage = defineRestNode({
594
- * key: "slack.post-message",
595
- * title: "Send Slack message",
596
- * icon: "si:slack",
597
- * api: { baseUrl: "https://slack.com/api", path: "/chat.postMessage", method: "POST" },
598
- * credentials: { auth: bearerTokenCredentialType },
599
- * inputSchema: z.object({ channel: z.string(), text: z.string() }),
600
- * request: ({ input }) => ({
601
- * body: { kind: "json", data: { channel: input.channel, text: input.text } },
602
- * }),
603
- * response: ({ json }) => ({ messageTs: (json as any).ts }),
604
- * });
605
- * ```
606
- *
607
- * - `defineRestNode` is a thin wrapper over `defineNode`; it does not introduce a new runtime kind.
608
- * - Credential sessions are resolved via the `credentials` binding map (same as `defineNode`).
609
- * - Path `{placeholder}` substitution is applied from `input` keys before the request is made.
610
- * - Non-2xx responses throw an `Error` by default (`errorPolicy: "throw"`).
611
- */
612
483
  function defineRestNode(options) {
613
484
  const errorPolicy = options.errorPolicy ?? "throw";
614
485
  return defineNode({
@@ -4146,11 +4017,6 @@ function toJSONSchema(input, params) {
4146
4017
 
4147
4018
  //#endregion
4148
4019
  //#region src/nodes/ConnectionCredentialExecutionContextFactory.ts
4149
- /**
4150
- * Builds a {@link NodeExecutionContext} whose identity for credential binding and `getCredential`
4151
- * is a **connection-owned** workflow node id (`ConnectionNodeIdFactory` in `@codemation/core`),
4152
- * not the executing parent node. Use for LLM slots, tool slots, or any connection-scoped owner.
4153
- */
4154
4020
  var ConnectionCredentialExecutionContextFactory = class {
4155
4021
  credentialResolverFactory;
4156
4022
  constructor(credentialSessions) {
@@ -4173,15 +4039,6 @@ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
4173
4039
  createConnectionCredentialExecutionContextFactory(credentialSessions) {
4174
4040
  return new ConnectionCredentialExecutionContextFactory(credentialSessions);
4175
4041
  }
4176
- /**
4177
- * Produces a plain JSON Schema object (`draft-07`) from a Zod schema, as needed by
4178
- * OpenAI tool-parameter schemas and the structured-output repair prompt.
4179
- * - Prefers the schema's **instance** `toJSONSchema(...)` method so we stay inside the Zod
4180
- * instance that created the schema (works across consumer/framework tsx namespaces — see
4181
- * {@link ZodInstanceToJsonSchema}). Falls back to the framework-imported module function.
4182
- * - Strips root `$schema` (OpenAI ignores it).
4183
- * - Sanitizes `required` for cfworker json-schema compatibility (must be a string array or absent).
4184
- */
4185
4042
  createJsonSchemaRecord(inputSchema, options) {
4186
4043
  const { $schema: _draftSchemaOmitted,...rest } = this.convertZodSchemaToJsonSchema(inputSchema, { target: "draft-07" });
4187
4044
  if (options.requireObjectRoot && rest.type !== "object") throw new Error(`Cannot create tool "${options.schemaName}": tool input schema must be a JSON Schema object type (got type=${String(rest.type)}).`);
@@ -4190,20 +4047,11 @@ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
4190
4047
  this.sanitizeJsonSchemaRequiredKeywordsForCfworker(rest);
4191
4048
  return rest;
4192
4049
  }
4193
- /**
4194
- * Runs Zod's `toJSONSchema` via the schema's own instance method when available, so consumer
4195
- * schemas loaded under a different tsx namespace still convert correctly. If the caller handed us
4196
- * a payload that lacks that method (e.g. a plain JSON Schema record or a Zod instance whose
4197
- * prototype was stripped), we fall back to the framework-bundled module function.
4198
- */
4199
4050
  convertZodSchemaToJsonSchema(inputSchema, params) {
4200
4051
  const candidate = inputSchema.toJSONSchema;
4201
4052
  if (typeof candidate === "function") return candidate.call(inputSchema, params);
4202
4053
  return toJSONSchema(inputSchema, params);
4203
4054
  }
4204
- /**
4205
- * `@cfworker/json-schema` iterates `schema.required` with `for...of`; it must be a string array or absent.
4206
- */
4207
4055
  sanitizeJsonSchemaRequiredKeywordsForCfworker(node$1) {
4208
4056
  if (!node$1 || typeof node$1 !== "object" || Array.isArray(node$1)) return;
4209
4057
  const o = node$1;
@@ -4350,11 +4198,6 @@ var OpenAIChatModelConfig = class {
4350
4198
 
4351
4199
  //#endregion
4352
4200
  //#region src/chatModels/OpenAiChatModelPresetsFactory.ts
4353
- /**
4354
- * Default OpenAI chat model configs for scaffolds and demos (icon + label match {@link OpenAIChatModelConfig} defaults).
4355
- * Prefer importing {@link openAiChatModelPresets} from here or from the consumer template re-export
4356
- * instead of repeating {@link OpenAIChatModelConfig} construction in app workflows.
4357
- */
4358
4201
  var OpenAiChatModelPresets = class {
4359
4202
  demoGpt4oMini = new OpenAIChatModelConfig("OpenAI", "gpt-4o-mini");
4360
4203
  demoGpt41 = new OpenAIChatModelConfig("OpenAI", "gpt-4.1");
@@ -4363,19 +4206,6 @@ const openAiChatModelPresets = new OpenAiChatModelPresets();
4363
4206
 
4364
4207
  //#endregion
4365
4208
  //#region src/chatModels/ManagedHmacSignerFactory.types.ts
4366
- /**
4367
- * Creates an HMAC-signing fetch wrapper that authenticates requests to
4368
- * Codemation managed services (LLM broker, doc-scanner) with the
4369
- * Codemation-Hmac v=1 scheme.
4370
- *
4371
- * Mirrors HmacRequestSigner from @codemation/host/pairing without importing
4372
- * that package (which would create a circular dependency since @codemation/host
4373
- * depends on @codemation/core-nodes).
4374
- *
4375
- * @param workspaceId - Workspace identifier injected by the CP provisioner.
4376
- * @param pairingSecret - Base64-encoded 32-byte HMAC key injected by the provisioner.
4377
- * @param options - Optional behaviour flags and test seams.
4378
- */
4379
4209
  function managedHmacFetchFactory(workspaceId, pairingSecret, options) {
4380
4210
  const signBody = options?.signBody ?? true;
4381
4211
  return async (input, init) => {
@@ -4394,10 +4224,6 @@ function managedHmacFetchFactory(workspaceId, pairingSecret, options) {
4394
4224
  });
4395
4225
  };
4396
4226
  }
4397
- /**
4398
- * Produces a Codemation-Hmac v=1 Authorization header value.
4399
- * Algorithm must match HmacVerifier.computeSignature() in the control-plane.
4400
- */
4401
4227
  function buildHmacAuthHeader(workspaceId, pairingSecret, method, url, body, overrides) {
4402
4228
  const ts = overrides?.now ? overrides.now() : Math.floor(Date.now() / 1e3);
4403
4229
  const nonce = overrides?.nonce ? overrides.nonce() : randomBytes(16).toString("base64");
@@ -4458,21 +4284,137 @@ var CodemationChatModelConfig = class {
4458
4284
  }
4459
4285
  };
4460
4286
 
4287
+ //#endregion
4288
+ //#region src/nodes/AgentToolResultContentFactory.ts
4289
+ const MAX_INLINE_BYTES = 8 * 1024 * 1024;
4290
+ const KNOWN_MCP_BLOCK_TYPES = new Set([
4291
+ "text",
4292
+ "image",
4293
+ "audio",
4294
+ "resource",
4295
+ "resource_link"
4296
+ ]);
4297
+ var AgentToolResultContentFactory = class AgentToolResultContentFactory {
4298
+ static tryMapToContentOutput(result) {
4299
+ const blocks = AgentToolResultContentFactory.contentBlocks(result);
4300
+ if (blocks === void 0) return void 0;
4301
+ const parts = [];
4302
+ let inlinedBytes = 0;
4303
+ for (const block of blocks) {
4304
+ const mapped = AgentToolResultContentFactory.mapBlock(block, inlinedBytes);
4305
+ parts.push(mapped.part);
4306
+ inlinedBytes += mapped.bytes;
4307
+ }
4308
+ return parts;
4309
+ }
4310
+ static contentBlocks(result) {
4311
+ if (result === null || typeof result !== "object") return void 0;
4312
+ const content = result.content;
4313
+ if (!Array.isArray(content) || content.length === 0) return void 0;
4314
+ if (!content.every((block) => block !== null && typeof block === "object" && typeof block.type === "string")) return void 0;
4315
+ return content.some((block) => KNOWN_MCP_BLOCK_TYPES.has(block.type)) ? content : void 0;
4316
+ }
4317
+ static mapBlock(block, inlinedBytesSoFar) {
4318
+ const type = block.type;
4319
+ if (type === "text" && typeof block.text === "string") return {
4320
+ part: {
4321
+ type: "text",
4322
+ text: block.text
4323
+ },
4324
+ bytes: 0
4325
+ };
4326
+ if (type === "image" && typeof block.data === "string" && typeof block.mimeType === "string") return AgentToolResultContentFactory.mapBinary({
4327
+ base64: block.data,
4328
+ mediaType: block.mimeType,
4329
+ inlinedBytesSoFar
4330
+ });
4331
+ if (type === "resource" && block.resource) return AgentToolResultContentFactory.mapEmbeddedResource(block.resource, inlinedBytesSoFar);
4332
+ if (type === "resource_link" && typeof block.uri === "string") {
4333
+ const mime = typeof block.mimeType === "string" ? ` (${block.mimeType})` : "";
4334
+ return {
4335
+ part: {
4336
+ type: "text",
4337
+ text: `[linked resource: ${block.uri}${mime}]`
4338
+ },
4339
+ bytes: 0
4340
+ };
4341
+ }
4342
+ return {
4343
+ part: {
4344
+ type: "text",
4345
+ text: `[unsupported tool content block: ${String(type)}]`
4346
+ },
4347
+ bytes: 0
4348
+ };
4349
+ }
4350
+ static mapEmbeddedResource(resource, inlinedBytesSoFar) {
4351
+ if (typeof resource.text === "string") return {
4352
+ part: {
4353
+ type: "text",
4354
+ text: resource.text
4355
+ },
4356
+ bytes: 0
4357
+ };
4358
+ if (typeof resource.blob === "string" && typeof resource.mimeType === "string") return AgentToolResultContentFactory.mapBinary({
4359
+ base64: resource.blob,
4360
+ mediaType: resource.mimeType,
4361
+ filename: typeof resource.name === "string" ? resource.name : void 0,
4362
+ inlinedBytesSoFar
4363
+ });
4364
+ return {
4365
+ part: {
4366
+ type: "text",
4367
+ text: `[embedded resource: ${typeof resource.uri === "string" ? resource.uri : "unknown"}]`
4368
+ },
4369
+ bytes: 0
4370
+ };
4371
+ }
4372
+ static mapBinary(args) {
4373
+ const rawBytes = Math.floor(args.base64.length * 3 / 4);
4374
+ if (args.inlinedBytesSoFar + rawBytes > MAX_INLINE_BYTES) {
4375
+ const name = args.filename ? ` "${args.filename}"` : "";
4376
+ const kb = Math.round(rawBytes / 1024);
4377
+ return {
4378
+ part: {
4379
+ type: "text",
4380
+ text: `[binary${name} (${args.mediaType}, ~${kb} KB) omitted: exceeds the per-tool-result inline limit]`
4381
+ },
4382
+ bytes: 0
4383
+ };
4384
+ }
4385
+ if (args.mediaType.startsWith("image/")) return {
4386
+ part: {
4387
+ type: "image-data",
4388
+ data: args.base64,
4389
+ mediaType: args.mediaType
4390
+ },
4391
+ bytes: rawBytes
4392
+ };
4393
+ if (args.mediaType === "application/pdf") return {
4394
+ part: {
4395
+ type: "file-data",
4396
+ data: args.base64,
4397
+ mediaType: args.mediaType,
4398
+ ...args.filename ? { filename: args.filename } : {}
4399
+ },
4400
+ bytes: rawBytes
4401
+ };
4402
+ return {
4403
+ part: {
4404
+ type: "text",
4405
+ text: `[binary${args.filename ? ` "${args.filename}"` : ""} (${args.mediaType}) not inlined: unsupported by the model]`
4406
+ },
4407
+ bytes: 0
4408
+ };
4409
+ }
4410
+ };
4411
+
4461
4412
  //#endregion
4462
4413
  //#region src/nodes/AgentMessageFactory.ts
4463
- /**
4464
- * AI-SDK-shaped message construction for the AIAgent stack. Emits plain `ModelMessage[]`
4465
- * ( `{ role: 'system' | 'user' | 'assistant' | 'tool', content: ... }` ) as consumed by
4466
- * `generateText({ messages })` from the `ai` package.
4467
- */
4468
4414
  var AgentMessageFactory = class AgentMessageFactory {
4469
4415
  static createPromptMessages(messages) {
4470
4416
  return messages.map((message) => this.createPromptMessage(message));
4471
4417
  }
4472
- /**
4473
- * Builds the assistant message that contains optional text plus one or more tool-call parts,
4474
- * matching the shape AI SDK emits between steps.
4475
- */
4476
4418
  static createAssistantWithToolCalls(text, toolCalls) {
4477
4419
  const content = [];
4478
4420
  if (text && text.length > 0) content.push({
@@ -4490,24 +4432,30 @@ var AgentMessageFactory = class AgentMessageFactory {
4490
4432
  content
4491
4433
  };
4492
4434
  }
4493
- /**
4494
- * Builds the `{ role: "tool", content: [{ type: "tool-result", ... }, ...] }` message returned
4495
- * to the model after each tool round.
4496
- */
4497
- static createToolResultsMessage(executedToolCalls) {
4435
+ static createToolResultsMessage(executedToolCalls, passToolBinariesToModel = true) {
4498
4436
  return {
4499
4437
  role: "tool",
4500
4438
  content: executedToolCalls.map((executed) => ({
4501
4439
  type: "tool-result",
4502
4440
  toolCallId: executed.toolCallId,
4503
4441
  toolName: executed.toolName,
4504
- output: {
4505
- type: "json",
4506
- value: AgentMessageFactory.toToolResultJson(executed.result)
4507
- }
4442
+ output: AgentMessageFactory.toToolResultOutput(executed.result, passToolBinariesToModel)
4508
4443
  }))
4509
4444
  };
4510
4445
  }
4446
+ static toToolResultOutput(result, passToolBinariesToModel) {
4447
+ if (passToolBinariesToModel) {
4448
+ const content = AgentToolResultContentFactory.tryMapToContentOutput(result);
4449
+ if (content !== void 0) return {
4450
+ type: "content",
4451
+ value: content
4452
+ };
4453
+ }
4454
+ return {
4455
+ type: "json",
4456
+ value: AgentMessageFactory.toToolResultJson(result)
4457
+ };
4458
+ }
4511
4459
  static toToolResultJson(value) {
4512
4460
  if (value === void 0) return null;
4513
4461
  try {
@@ -5304,10 +5252,6 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
5304
5252
  }
5305
5253
  throw new Error(`Structured output required for AIAgent "${args.agentName}" (${args.nodeId}) but validation still failed after ${_AgentStructuredOutputRunner.repairAttemptCount} repair attempts: ${failure.validationError}`);
5306
5254
  }
5307
- /**
5308
- * Chooses strict mode for OpenAI chat-model configs, off otherwise. Extendable in future for
5309
- * other providers that adopt the same "supply a JSON Schema record directly" contract.
5310
- */
5311
5255
  resolveStructuredOutputOptions(chatModelConfig) {
5312
5256
  if (chatModelConfig.type !== OpenAIChatModelFactory) return;
5313
5257
  return {
@@ -5790,10 +5734,6 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
5790
5734
  extractErrorDetails(error) {
5791
5735
  return error.details;
5792
5736
  }
5793
- /**
5794
- * Extracts the text content from the last assistant message in the conversation snapshot.
5795
- * Used to populate `agentReasoning` in the HITL suspension metadata.
5796
- */
5797
5737
  extractLastAssistantText(conversation) {
5798
5738
  for (let i = conversation.length - 1; i >= 0; i--) {
5799
5739
  const msg = conversation[i];
@@ -5828,16 +5768,6 @@ AgentToolExecutionCoordinator = __decorate([
5828
5768
 
5829
5769
  //#endregion
5830
5770
  //#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
5771
  var AgentBinaryContentFactory = class AgentBinaryContentFactory {
5842
5772
  static toContentPart(binary) {
5843
5773
  if (binary.mediaType.startsWith("image/")) return {
@@ -5916,20 +5846,6 @@ let NodeBackedToolRuntime = class NodeBackedToolRuntime$1 {
5916
5846
  outputs
5917
5847
  });
5918
5848
  }
5919
- /**
5920
- * Returns a re-rooted child ctx for nested-agent tools (so their LLM/tool connection ids derive
5921
- * from the tool connection node, telemetry parents under the tool-call span, and connection
5922
- * invocations carry `parentInvocationId`). Plain runnable tools (non-agent) keep the orchestrator
5923
- * ctx with only `config` swapped — no nesting concern.
5924
- *
5925
- * The caller (`AIAgentNode.createItemScopedTools`) already wraps the orchestrator ctx via
5926
- * `ConnectionCredentialExecutionContextFactory.forConnectionNode`, so `args.ctx.nodeId` is the
5927
- * tool's own connection node id (e.g. `AIAgentNode:2__conn__tool__searchInMail`). We pass that
5928
- * through as the sub-agent's `nodeId`; deriving another `toolConnectionNodeId(args.ctx.nodeId,
5929
- * config.name)` here would prepend a duplicate `__conn__tool__<name>` segment and exponentially
5930
- * deepen ids on each invocation, which also breaks credential resolution because user-provided
5931
- * bindings sit on the single-level connection node id.
5932
- */
5933
5849
  resolveNodeCtx(config$1, args) {
5934
5850
  const isNestedAgent = AgentConfigInspector.isAgentNodeConfig(config$1.node);
5935
5851
  const hooks = args.hooks;
@@ -5994,22 +5910,12 @@ NodeBackedToolRuntime = __decorate([
5994
5910
 
5995
5911
  //#endregion
5996
5912
  //#region src/nodes/BM25Index.ts
5997
- /**
5998
- * Minimal BM25 (Okapi BM25) implementation for indexing MCP tool descriptions.
5999
- *
6000
- * Parameters: k1=1.5, b=0.75 (standard defaults).
6001
- * Tokenisation: lowercase, split on non-alphanumerics, filter empties.
6002
- */
6003
5913
  var BM25Index = class {
6004
5914
  k1 = 1.5;
6005
5915
  b = .75;
6006
5916
  tf = [];
6007
5917
  df = /* @__PURE__ */ new Map();
6008
5918
  avgDocLen = 0;
6009
- /**
6010
- * Add all documents at once. After calling this, search is available.
6011
- * Documents are indexed in insertion order; search returns their indices.
6012
- */
6013
5919
  add(docs) {
6014
5920
  const docTerms = docs.map((d) => this.tokenize(d));
6015
5921
  let totalLen = 0;
@@ -6022,10 +5928,6 @@ var BM25Index = class {
6022
5928
  }
6023
5929
  this.avgDocLen = docTerms.length > 0 ? totalLen / docTerms.length : 0;
6024
5930
  }
6025
- /**
6026
- * Returns up to `limit` document indices ranked by BM25 score (highest first).
6027
- * Returns an empty array if the index is empty or the query matches nothing.
6028
- */
6029
5931
  search(query, limit) {
6030
5932
  const n = this.tf.length;
6031
5933
  if (n === 0) return [];
@@ -6066,26 +5968,12 @@ const PINNED_TOOLS_SOFT_LIMIT = 8;
6066
5968
  const PINNED_TOOLS_HARD_LIMIT = 16;
6067
5969
  const FIND_TOOLS_NAME = "find_tools";
6068
5970
  const FIND_TOOLS_DEFAULT_LIMIT = 5;
6069
- /**
6070
- * Default tool-loading strategy: BM25-indexed MCP tool deferral via a `find_tools` meta-tool.
6071
- *
6072
- * - Node-backed tools and pinned MCP tools are always included in every turn.
6073
- * - `find_tools(query, limit?)` is added to the tool set when MCP tools are indexed.
6074
- * - Tools surfaced by `find_tools` are included in subsequent turns.
6075
- *
6076
- * Not DI-managed; instantiated per agent execution by DeferredMetaToolStrategyFactory.
6077
- */
6078
5971
  var DeferredMetaToolStrategy = class {
6079
5972
  nodeBackedTools = {};
6080
5973
  pinnedTools = {};
6081
5974
  mcpEntries = [];
6082
5975
  toolsByServerId = /* @__PURE__ */ new Map();
6083
5976
  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
5977
  jsonSchema;
6090
5978
  constructor(bm25, warnFn) {
6091
5979
  this.bm25 = bm25;
@@ -6233,11 +6121,6 @@ let AIAgentNode = class AIAgentNode$1 {
6233
6121
  inputSchema = unknown();
6234
6122
  connectionCredentialExecutionContextFactory;
6235
6123
  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
6124
  aiSdk;
6242
6125
  aiSdkPromise = null;
6243
6126
  constructor(nodeResolver, credentialSessions, nodeBackedToolRuntime, executionHelpers, structuredOutputRunner, toolExecutionCoordinator, toolLoadingStrategyFactory, agentMcpIntegration) {
@@ -6261,15 +6144,9 @@ let AIAgentNode = class AIAgentNode$1 {
6261
6144
  };
6262
6145
  return (await this.runAgentForItem(prepared, itemWithMappedJson, args.itemIndex, args.items)).json;
6263
6146
  }
6264
- /** Load the `ai` SDK once per node instance (cached promise guards concurrent items). */
6265
6147
  async ensureAiSdk() {
6266
6148
  this.aiSdk = await (this.aiSdkPromise ??= import("ai"));
6267
6149
  }
6268
- /**
6269
- * Resume path: re-enters the agent loop after a HITL suspension.
6270
- * Reconstructs the conversation from the checkpoint, injects the human decision
6271
- * as a tool_result, and continues the loop from where it suspended.
6272
- */
6273
6150
  async executeResumed(args, resumeContext) {
6274
6151
  const { ctx } = args;
6275
6152
  const taskMetadata = resumeContext.task.metadata ?? {};
@@ -6296,7 +6173,7 @@ let AIAgentNode = class AIAgentNode$1 {
6296
6173
  result: decision,
6297
6174
  serialized: JSON.stringify(decision)
6298
6175
  };
6299
- const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry])];
6176
+ const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry], ctx.config.passToolBinariesToModel !== false)];
6300
6177
  const loopResult = await this.runTurnLoopUntilFinalAnswer({
6301
6178
  prepared,
6302
6179
  itemInputsByPort,
@@ -6316,10 +6193,6 @@ let AIAgentNode = class AIAgentNode$1 {
6316
6193
  const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
6317
6194
  return this.buildOutputItem(item, outputJson).json;
6318
6195
  }
6319
- /**
6320
- * Normalizes a {@link ResumeContext} decision into a flat JSON-serializable shape
6321
- * suitable for injection as a tool_result content.
6322
- */
6323
6196
  normalizeDecision(resumeContext) {
6324
6197
  const { decision } = resumeContext;
6325
6198
  if (decision.kind === "decided") {
@@ -6446,16 +6319,6 @@ let AIAgentNode = class AIAgentNode$1 {
6446
6319
  const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
6447
6320
  return this.buildOutputItem(item, outputJson);
6448
6321
  }
6449
- /**
6450
- * Multi-turn loop:
6451
- * - Each turn is a single `generateText` call with tools exposed but **not auto-executed**
6452
- * (we control tool dispatch so that {@link AgentToolExecutionCoordinator} drives repair /
6453
- * connection-invocation recording / transient-error handling exactly like before).
6454
- * - When the model returns no tool calls the loop ends with the model's text as the final answer.
6455
- * - Respects `guardrails.maxTurns` and `guardrails.onTurnLimitReached`.
6456
- * - Strategy-owned tool calls (e.g. `find_tools`) are dispatched via the strategy, not the
6457
- * coordinator; their results are tracked so subsequent turns receive the discovered tools.
6458
- */
6459
6322
  async runTurnLoopUntilFinalAnswer(args) {
6460
6323
  const { prepared, itemInputsByPort, itemScopedTools, conversation } = args;
6461
6324
  const { ctx, guardrails, toolLoadingStrategy } = prepared;
@@ -6463,7 +6326,6 @@ let AIAgentNode = class AIAgentNode$1 {
6463
6326
  let toolCallCount = args.resumedToolCallCount ?? 0;
6464
6327
  let turnCount = 0;
6465
6328
  const repairAttemptsByToolName = /* @__PURE__ */ new Map();
6466
- /** Tool IDs surfaced by find_tools across all prior turns in this item run. */
6467
6329
  let previousFoundToolIds = [];
6468
6330
  for (let turn = 1; turn <= guardrails.maxTurns; turn++) {
6469
6331
  turnCount = turn;
@@ -6516,7 +6378,7 @@ let AIAgentNode = class AIAgentNode$1 {
6516
6378
  coordinatorExecutedCalls.push(...executed);
6517
6379
  }
6518
6380
  const allExecutedCalls = [...strategyExecutedCalls, ...coordinatorExecutedCalls];
6519
- this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, allExecutedCalls);
6381
+ this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, allExecutedCalls, ctx.config.passToolBinariesToModel !== false);
6520
6382
  }
6521
6383
  return {
6522
6384
  finalText,
@@ -6531,8 +6393,8 @@ let AIAgentNode = class AIAgentNode$1 {
6531
6393
  if (guardrails.onTurnLimitReached === "respondWithLastMessage") return;
6532
6394
  throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" reached maxTurns=${guardrails.maxTurns} before producing a final response.`);
6533
6395
  }
6534
- appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls) {
6535
- conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls));
6396
+ appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls, passToolBinariesToModel) {
6397
+ conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls, passToolBinariesToModel));
6536
6398
  }
6537
6399
  async resolveFinalOutputJson(prepared, itemInputsByPort, conversation, finalText, wasToolEnabledRun) {
6538
6400
  if (!prepared.ctx.config.outputSchema) return AgentOutputFactory.fromAgentContent(finalText);
@@ -6593,12 +6455,6 @@ let AIAgentNode = class AIAgentNode$1 {
6593
6455
  };
6594
6456
  });
6595
6457
  }
6596
- /**
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.
6601
- */
6602
6458
  resolveHumanApprovalBehavior(config$1) {
6603
6459
  if (!this.isNodeBackedToolConfig(config$1)) return void 0;
6604
6460
  const marker = config$1.node.humanApprovalToolBehavior;
@@ -6606,11 +6462,6 @@ let AIAgentNode = class AIAgentNode$1 {
6606
6462
  if (marker === void 0 && perBinding === void 0) return void 0;
6607
6463
  return { onRejected: perBinding ?? marker?.onRejected ?? "return" };
6608
6464
  }
6609
- /**
6610
- * Invoke a text turn using the merged tool set from item-scoped tools (coordinator-managed)
6611
- * and strategy tools (find_tools + discovered MCP tools).
6612
- * Strategy tools take precedence for names that overlap.
6613
- */
6614
6465
  async invokeTextTurnWithStrategyTools(prepared, itemInputsByPort, messages, itemScopedTools, strategyTools) {
6615
6466
  const itemToolSet = this.buildToolSet(itemScopedTools);
6616
6467
  const strategyHasTools = Object.keys(strategyTools).length > 0;
@@ -6621,10 +6472,6 @@ let AIAgentNode = class AIAgentNode$1 {
6621
6472
  } : void 0;
6622
6473
  return this.invokeTextTurnWithToolSet(prepared, itemInputsByPort, messages, mergedTools);
6623
6474
  }
6624
- /**
6625
- * Removes `execute` properties from ToolSet entries so the AI SDK does not
6626
- * auto-execute them within `generateText`. Codemation owns all tool dispatch.
6627
- */
6628
6475
  stripExecuteCallbacks(tools) {
6629
6476
  const stripped = {};
6630
6477
  for (const [name, def] of Object.entries(tools)) {
@@ -6633,12 +6480,6 @@ let AIAgentNode = class AIAgentNode$1 {
6633
6480
  }
6634
6481
  return stripped;
6635
6482
  }
6636
- /**
6637
- * Builds a ToolSet from resolved tools for strategy initialization.
6638
- * The strategy uses this for its "always-included" node-backed tool descriptions.
6639
- * HITL tools (detected via the `humanApprovalToolBehavior` field set by `defineHumanApprovalNode`) get the solo-constraint sentence
6640
- * appended to their description.
6641
- */
6642
6483
  buildToolSetFromResolved(resolvedTools) {
6643
6484
  if (resolvedTools.length === 0) return {};
6644
6485
  const toolSet = {};
@@ -6656,20 +6497,6 @@ let AIAgentNode = class AIAgentNode$1 {
6656
6497
  }
6657
6498
  return toolSet;
6658
6499
  }
6659
- /**
6660
- * Builds an AI SDK {@link ToolSet} where every tool ships a pre-converted JSON Schema (via
6661
- * {@link jsonSchema}) — not the raw Zod schema — and carries **no** `execute`. Two reasons:
6662
- *
6663
- * 1. Codemation owns tool dispatch + the per-tool repair loop (see {@link AgentToolExecutionCoordinator}),
6664
- * so the AI SDK must surface tool calls back to us instead of auto-running them.
6665
- * 2. The AI SDK's `asSchema` helper discriminates between Zod v3 / Zod v4 / Standard Schema via
6666
- * runtime feature-detection (`~standard`, `_zod`, etc.). Handing it a pre-built
6667
- * {@link jsonSchema} record — which is tagged with `Symbol.for('vercel.ai.schema')` — skips all
6668
- * of that detection and guarantees the provider receives a draft-07 JSON Schema with
6669
- * `additionalProperties: false` at every object depth (see {@link OpenAiStrictJsonSchemaFactory}
6670
- * for the same logic applied to structured-output schemas). Codemation still runs its own Zod
6671
- * validation on tool inputs before execute — the schema handed to the model is advisory.
6672
- */
6673
6500
  buildToolSet(itemScopedTools) {
6674
6501
  if (itemScopedTools.length === 0) return void 0;
6675
6502
  const toolSet = {};
@@ -6687,10 +6514,6 @@ let AIAgentNode = class AIAgentNode$1 {
6687
6514
  }
6688
6515
  return toolSet;
6689
6516
  }
6690
- /**
6691
- * One `generateText` turn (no auto tool execution) with Codemation-owned child-span telemetry
6692
- * and connection-invocation state recording. Accepts a pre-built ToolSet.
6693
- */
6694
6517
  async invokeTextTurnWithToolSet(prepared, itemInputsByPort, messages, tools) {
6695
6518
  const invocationId = ConnectionInvocationIdFactory.create();
6696
6519
  const startedAt = /* @__PURE__ */ new Date();
@@ -6801,11 +6624,6 @@ let AIAgentNode = class AIAgentNode$1 {
6801
6624
  });
6802
6625
  }
6803
6626
  }
6804
- /**
6805
- * Structured-output turn: runs `generateText({ output: Output.object({ schema }) })` via the
6806
- * structured-output runner. We keep this as a separate helper because the runner needs the raw
6807
- * validated value (not just text) back, and must be able to retry on Zod failures.
6808
- */
6809
6627
  async invokeStructuredTurn(prepared, itemInputsByPort, schema, messages, structuredOptions) {
6810
6628
  const invocationId = ConnectionInvocationIdFactory.create();
6811
6629
  const startedAt = /* @__PURE__ */ new Date();
@@ -6930,13 +6748,6 @@ let AIAgentNode = class AIAgentNode$1 {
6930
6748
  providerOptions: overrides?.providerOptions ?? defaults.providerOptions
6931
6749
  };
6932
6750
  }
6933
- /**
6934
- * Build a no-code-friendly output payload for an LLM round.
6935
- *
6936
- * Always includes `content` (matching the canvas snapshot shape used elsewhere) and adds a
6937
- * `toolCalls` array when the round produced tool calls so the execution inspector surfaces the
6938
- * planned calls instead of just an empty `""` for tool-only rounds.
6939
- */
6940
6751
  summarizeTurnOutput(turnResult) {
6941
6752
  if (turnResult.toolCalls.length === 0) return { content: turnResult.text };
6942
6753
  const toolCalls = turnResult.toolCalls.map((toolCall) => ({
@@ -7139,12 +6950,6 @@ let AIAgentNode = class AIAgentNode$1 {
7139
6950
  const binaries = await this.resolveInlineBinaries(attachments, ctx);
7140
6951
  return AgentBinaryContentFactory.withBinaries(promptMessages, binaries);
7141
6952
  }
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
6953
  selectBinaryAttachments(item, itemIndex, items, ctx) {
7149
6954
  const manual = ctx.config.binaries;
7150
6955
  if (manual !== void 0) return typeof manual === "function" ? manual({
@@ -7155,14 +6960,6 @@ let AIAgentNode = class AIAgentNode$1 {
7155
6960
  }) : manual;
7156
6961
  return item.binary ? Object.values(item.binary) : [];
7157
6962
  }
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
6963
  async resolveInlineBinaries(attachments, ctx) {
7167
6964
  const resolved = [];
7168
6965
  for (const attachment of attachments) {
@@ -7175,12 +6972,6 @@ let AIAgentNode = class AIAgentNode$1 {
7175
6972
  }
7176
6973
  return resolved;
7177
6974
  }
7178
- /**
7179
- * When `item.json.__source` matches an entry in `config.untrustedSources`
7180
- * (default: `["gmail", "ocr", "webhook"]`), wraps every user-role message
7181
- * content with an untrusted-external-source preamble so the LLM treats the
7182
- * content as data, not instructions.
7183
- */
7184
6975
  wrapUntrustedSourceMessages(messages, item, config$1) {
7185
6976
  const source = item.json !== null && typeof item.json === "object" ? item.json.__source : void 0;
7186
6977
  if (typeof source !== "string") return messages;
@@ -7273,10 +7064,6 @@ AIAgentNode = __decorate([
7273
7064
 
7274
7065
  //#endregion
7275
7066
  //#region src/nodes/AIAgentConfig.ts
7276
- /**
7277
- * AI agent: credential bindings are keyed to connection-owned LLM/tool node ids (ConnectionNodeIdFactory),
7278
- * not to the agent workflow node id.
7279
- */
7280
7067
  var AIAgent = class {
7281
7068
  kind = "node";
7282
7069
  type = AIAgentNode;
@@ -7296,6 +7083,7 @@ var AIAgent = class {
7296
7083
  pinnedMcpTools;
7297
7084
  untrustedSources;
7298
7085
  passBinariesToModel;
7086
+ passToolBinariesToModel;
7299
7087
  binaries;
7300
7088
  constructor(options) {
7301
7089
  this.name = options.name;
@@ -7312,6 +7100,7 @@ var AIAgent = class {
7312
7100
  this.pinnedMcpTools = options.pinnedMcpTools;
7313
7101
  this.untrustedSources = options.untrustedSources;
7314
7102
  this.passBinariesToModel = options.passBinariesToModel;
7103
+ this.passToolBinariesToModel = options.passToolBinariesToModel;
7315
7104
  this.binaries = options.binaries;
7316
7105
  }
7317
7106
  inspectorSummary() {
@@ -7372,11 +7161,6 @@ AssertionNode = __decorate([node({ packageName: "@codemation/core-nodes" })], As
7372
7161
 
7373
7162
  //#endregion
7374
7163
  //#region src/nodes/assertion.ts
7375
- /**
7376
- * Generic assertion node — the "callback" form. For declarative shorthands (StringEquals,
7377
- * JudgeByAgent) compose this with helpers added in later phases. Sets `emitsAssertions: true`
7378
- * so host-side persisters know to record its outputs as `TestAssertion` rows.
7379
- */
7380
7164
  var Assertion = class {
7381
7165
  kind = "node";
7382
7166
  type = AssertionNode;
@@ -7639,17 +7423,12 @@ HttpRequestNode = __decorate([node({ packageName: "@codemation/core-nodes" })],
7639
7423
 
7640
7424
  //#endregion
7641
7425
  //#region src/nodes/httpRequest.ts
7642
- /**
7643
- * The built-in HTTP request credential type IDs accepted by the `HttpRequest` node.
7644
- * These match the four generic credential types shipped with `@codemation/core-nodes`.
7645
- */
7646
7426
  const HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES = [
7647
7427
  bearerTokenCredentialType.definition.typeId,
7648
7428
  apiKeyCredentialType.definition.typeId,
7649
7429
  basicAuthCredentialType.definition.typeId,
7650
7430
  oauth2ClientCredentialsType.definition.typeId
7651
7431
  ];
7652
- /** Default maximum response size for binary mode: 100 MiB. */
7653
7432
  const DEFAULT_RESPONSE_SIZE_CAP_BYTES = 100 * 1024 * 1024;
7654
7433
  var HttpRequest = class {
7655
7434
  kind = "node";
@@ -7818,9 +7597,6 @@ var Filter = class {
7818
7597
  function getOriginIndex(item) {
7819
7598
  return getOriginIndexFromItem(item);
7820
7599
  }
7821
- /**
7822
- * Tags items routed to fan-in merge-by-origin (same contract as {@link IfNode} / {@link SwitchNode}).
7823
- */
7824
7600
  function tagItemForRouterFanIn(args) {
7825
7601
  const { item, itemIndex, nodeId, inputPortLabel = "$in" } = args;
7826
7602
  const metaBase = item.meta && typeof item.meta === "object" ? item.meta : {};
@@ -7911,10 +7687,6 @@ IsTestRunNode = __decorate([node({ packageName: "@codemation/core-nodes" })], Is
7911
7687
 
7912
7688
  //#endregion
7913
7689
  //#region src/nodes/isTestRun.ts
7914
- /**
7915
- * Branches per-item on whether the current run is a test run. Output ports: `true`, `false`.
7916
- * The wire payload is unchanged — this is a router, not a transform.
7917
- */
7918
7690
  var IsTestRun = class {
7919
7691
  kind = "node";
7920
7692
  type = IsTestRunNode;
@@ -7998,10 +7770,6 @@ var Split = class {
7998
7770
  type = SplitNode;
7999
7771
  execution = { hint: "local" };
8000
7772
  keepBinaries = true;
8001
- /**
8002
- * When splitting yields zero items for a batch, downstream single-input nodes still run once with an empty batch.
8003
- * Mirrors {@link MapData}'s empty-output behavior.
8004
- */
8005
7773
  continueWhenEmptyOutput = true;
8006
7774
  icon = "builtin:split-rows";
8007
7775
  id;
@@ -8055,15 +7823,6 @@ CronTriggerNode = __decorate([node({ packageName: "@codemation/core-nodes" })],
8055
7823
 
8056
7824
  //#endregion
8057
7825
  //#region src/nodes/CronTriggerFactory.ts
8058
- /**
8059
- * Schedules a workflow on a standard cron expression.
8060
- *
8061
- * Each tick emits one item: `{ firedAt: string, scheduledFor: string }` — both ISO-8601 timestamps.
8062
- * `firedAt` is the wall-clock moment the callback ran; `scheduledFor` is the cron-computed
8063
- * firing instant (these differ when the job was delayed).
8064
- *
8065
- * Timezone defaults to UTC when omitted — cron without an explicit TZ is a DST footgun.
8066
- */
8067
7826
  var CronTrigger = class {
8068
7827
  kind = "trigger";
8069
7828
  type = CronTriggerNode;
@@ -8134,7 +7893,6 @@ var ManualTrigger = class ManualTrigger {
8134
7893
  defaultItems;
8135
7894
  id;
8136
7895
  description;
8137
- /** Manual runs often emit an empty batch; still schedule downstream by default. */
8138
7896
  continueWhenEmptyOutput = true;
8139
7897
  constructor(name = "Manual trigger", defaultItemsOrId, idOrOptions) {
8140
7898
  this.name = name;
@@ -8189,7 +7947,6 @@ var MapData = class {
8189
7947
  kind = "node";
8190
7948
  type = MapDataNode;
8191
7949
  execution = { hint: "local" };
8192
- /** Zero mapped items should still allow downstream nodes to run. */
8193
7950
  continueWhenEmptyOutput = true;
8194
7951
  icon = "lucide:square-pen";
8195
7952
  keepBinaries;
@@ -8422,11 +8179,6 @@ TestTriggerNode = __decorate([node({ packageName: "@codemation/core-nodes" })],
8422
8179
 
8423
8180
  //#endregion
8424
8181
  //#region src/nodes/testTrigger.ts
8425
- /**
8426
- * Trigger config for a test fixture source. Drop one (or more) of these on the canvas alongside
8427
- * a workflow's live triggers; clicking "Run tests" on the Tests tab invokes
8428
- * {@link TestTriggerOptions.generateItems} via the TestSuiteOrchestrator.
8429
- */
8430
8182
  var TestTrigger = class {
8431
8183
  kind = "trigger";
8432
8184
  triggerKind = "test";
@@ -8500,7 +8252,6 @@ var Wait = class {
8500
8252
  kind = "node";
8501
8253
  type = WaitNode;
8502
8254
  execution = { hint: "local" };
8503
- /** Pass-through empty batches should still advance to downstream nodes. */
8504
8255
  continueWhenEmptyOutput = true;
8505
8256
  icon = "lucide:hourglass";
8506
8257
  id;
@@ -8602,6 +8353,39 @@ var WebhookTrigger = class WebhookTrigger {
8602
8353
  }
8603
8354
  };
8604
8355
 
8356
+ //#endregion
8357
+ //#region src/nodes/schedulePollingTrigger.ts
8358
+ const schedulePollingTrigger = definePollingTrigger({
8359
+ key: "schedule.interval",
8360
+ packageName: "@codemation/core-nodes",
8361
+ title: "Run on schedule",
8362
+ description: "Emit one tick item on every poll cycle.",
8363
+ icon: "lucide:clock",
8364
+ pollIntervalMs: 6e4,
8365
+ initialState() {
8366
+ return { tick: 0 };
8367
+ },
8368
+ poll({ state }) {
8369
+ const tick = (state ?? { tick: 0 }).tick + 1;
8370
+ return {
8371
+ items: [{ json: {
8372
+ firedAt: (/* @__PURE__ */ new Date()).toISOString(),
8373
+ tick
8374
+ } }],
8375
+ nextState: { tick }
8376
+ };
8377
+ },
8378
+ execute(items) {
8379
+ return { main: items };
8380
+ },
8381
+ testItems() {
8382
+ return [{ json: {
8383
+ firedAt: (/* @__PURE__ */ new Date()).toISOString(),
8384
+ tick: 0
8385
+ } }];
8386
+ }
8387
+ });
8388
+
8605
8389
  //#endregion
8606
8390
  //#region src/nodes/ConnectionCredentialNode.ts
8607
8391
  let ConnectionCredentialNode = class ConnectionCredentialNode$1 {
@@ -8615,11 +8399,6 @@ ConnectionCredentialNode = __decorate([node({ packageName: "@codemation/core-nod
8615
8399
 
8616
8400
  //#endregion
8617
8401
  //#region src/register.types.ts
8618
- /**
8619
- * Registrar for built-in nodes. In a real project, this would use tsyringe's
8620
- * container.registerSingleton(...). For the skeleton we keep it token-based:
8621
- * the engine resolves node implementations by class token.
8622
- */
8623
8402
  function registerCoreNodes(container) {}
8624
8403
 
8625
8404
  //#endregion
@@ -8848,9 +8627,6 @@ function workflow(id) {
8848
8627
 
8849
8628
  //#endregion
8850
8629
  //#region src/workflows/AIAgentConnectionWorkflowExpander.ts
8851
- /**
8852
- * Materializes connection-owned child nodes and {@link WorkflowDefinition.connections} for AI agent nodes.
8853
- */
8854
8630
  var AIAgentConnectionWorkflowExpander = class {
8855
8631
  constructor(connectionCredentialNodeConfigFactory, mcpServerResolver) {
8856
8632
  this.connectionCredentialNodeConfigFactory = connectionCredentialNodeConfigFactory;
@@ -9118,16 +8894,6 @@ const collectionDeleteNode = defineNode({
9118
8894
  function resolveSubjectField(field, item) {
9119
8895
  return typeof field === "function" ? field({ item }) : field;
9120
8896
  }
9121
- /**
9122
- * Auto-detecting inbox approval node.
9123
- *
9124
- * Uses `ctx.resolve(InboxChannelResolverToken)` to pick the right inbox channel
9125
- * at runtime:
9126
- * - In managed mode (PairingConfig present): routes to the control-plane inbox.
9127
- * - Otherwise: routes to the local inbox.
9128
- *
9129
- * Authors use this node directly; no extra wiring needed per deployment mode.
9130
- */
9131
8897
  const inboxApproval = defineHumanApprovalNode({
9132
8898
  key: "inbox.approval",
9133
8899
  title: "Inbox Approval",
@@ -9281,5 +9047,5 @@ const codemationDocumentScannerNode = defineNode({
9281
9047
  });
9282
9048
 
9283
9049
  //#endregion
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 };
9050
+ 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, schedulePollingTrigger, workflow };
9285
9051
  //# sourceMappingURL=index.js.map