@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.cjs CHANGED
@@ -33,9 +33,6 @@ let croner = require("croner");
33
33
  croner = __toESM(croner);
34
34
 
35
35
  //#region src/credentials/ApiKeyCredentialType.ts
36
- /**
37
- * API key credential that injects a key either as an HTTP header or a query parameter.
38
- */
39
36
  const apiKeyCredentialType = (0, __codemation_core.defineCredential)({
40
37
  key: "core-nodes.api-key",
41
38
  label: "API Key",
@@ -83,10 +80,6 @@ const apiKeyCredentialType = (0, __codemation_core.defineCredential)({
83
80
 
84
81
  //#endregion
85
82
  //#region src/credentials/BasicAuthCredentialType.ts
86
- /**
87
- * HTTP Basic authentication credential.
88
- * Session sets `Authorization: Basic <base64(username:password)>`.
89
- */
90
83
  const basicAuthCredentialType = (0, __codemation_core.defineCredential)({
91
84
  key: "core-nodes.basic-auth",
92
85
  label: "Basic Auth",
@@ -124,10 +117,6 @@ const basicAuthCredentialType = (0, __codemation_core.defineCredential)({
124
117
 
125
118
  //#endregion
126
119
  //#region src/credentials/BearerTokenCredentialType.ts
127
- /**
128
- * Simple Bearer token credential.
129
- * Session sets `Authorization: Bearer <token>` on every request.
130
- */
131
120
  const bearerTokenCredentialType = (0, __codemation_core.defineCredential)({
132
121
  key: "core-nodes.bearer-token",
133
122
  label: "Bearer Token",
@@ -186,24 +175,6 @@ var OAuth2TokenExchangeFactory = class {
186
175
 
187
176
  //#endregion
188
177
  //#region src/credentials/OAuth2ClientCredentialsTypeFactory.ts
189
- /**
190
- * OAuth2 client-credentials flow credential.
191
- *
192
- * This is a machine-to-machine flow: no user redirect occurs. The session
193
- * POSTs to the configured `tokenUrl` with `client_credentials` grant, caches
194
- * the resulting access token for the duration of the session, and injects it
195
- * as `Authorization: Bearer <token>` on each request.
196
- *
197
- * Token caching is per-session only (one createSession call = one token fetch
198
- * at most). Cross-session caching would require host-level state and is out of
199
- * scope here. Because the engine creates a fresh session per execution, a new
200
- * token is fetched once per node activation.
201
- *
202
- * NOTE: `auth` is intentionally omitted from the definition. The OAuth2
203
- * `auth: { kind: "oauth2" }` shape signals an authorization-code / user-redirect
204
- * flow; using it here would cause the host UI to render an OAuth consent button
205
- * that goes nowhere. Client-credentials is a purely server-side flow.
206
- */
207
178
  const oauth2ClientCredentialsType = (0, __codemation_core.defineCredential)({
208
179
  key: "core-nodes.oauth2-client-credentials",
209
180
  label: "OAuth2 Client Credentials",
@@ -287,10 +258,6 @@ const oauth2ClientCredentialsType = (0, __codemation_core.defineCredential)({
287
258
 
288
259
  //#endregion
289
260
  //#region src/http/SSRFBlockedError.ts
290
- /**
291
- * Thrown when an HTTP request target resolves to a private, link-local, or
292
- * loopback address and `allowPrivateNetworkTargets` is not set.
293
- */
294
261
  var SSRFBlockedError = class extends Error {
295
262
  resolvedIp;
296
263
  constructor(host, resolvedIp) {
@@ -302,26 +269,7 @@ var SSRFBlockedError = class extends Error {
302
269
 
303
270
  //#endregion
304
271
  //#region src/http/SsrfGuard.ts
305
- /** Emitted once per process when NODE_ENV=production and no allowedOutboundHosts is set. */
306
272
  let _productionNoAllowlistWarned = false;
307
- /**
308
- * Guards HTTP requests against Server-Side Request Forgery (SSRF) by
309
- * DNS-resolving the target host and rejecting private/link-local/loopback
310
- * addresses.
311
- *
312
- * Blocked ranges:
313
- * - RFC-1918: 10/8, 172.16/12, 192.168/16
314
- * - Link-local: 169.254/16
315
- * - Loopback: 127/8, ::1
316
- *
317
- * When `allowedOutboundHosts` is set, every resolved DNS target must match
318
- * at least one entry in the list (exact hostname or `*.example.com` wildcard).
319
- * When unset, existing behaviour applies: private ranges blocked, public allowed.
320
- *
321
- * Call {@link check} before making any outbound HTTP request.
322
- * Pass `allowPrivate: true` to bypass the private-network guard for trusted workflows
323
- * (allowedOutboundHosts allowlist is still applied when set).
324
- */
325
273
  var SsrfGuard = class {
326
274
  constructor(allowedOutboundHosts) {
327
275
  this.allowedOutboundHosts = allowedOutboundHosts;
@@ -330,15 +278,6 @@ var SsrfGuard = class {
330
278
  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.");
331
279
  }
332
280
  }
333
- /**
334
- * Resolves the host of `url` via DNS and throws {@link SSRFBlockedError}
335
- * if any resolved address falls in a blocked range, or if the host does not
336
- * match the operator-configured allowlist (when set).
337
- *
338
- * @param url - Fully-qualified URL of the intended request target.
339
- * @param allowPrivate - When `true`, the private-network check is skipped.
340
- * The allowedOutboundHosts check is still applied when set.
341
- */
342
281
  async check(url, allowPrivate) {
343
282
  if (allowPrivate && !this.allowedOutboundHosts?.length) return;
344
283
  let host;
@@ -362,10 +301,6 @@ var SsrfGuard = class {
362
301
  }
363
302
  for (const { address } of addresses) if (this.isPrivateAddress(address)) throw new SSRFBlockedError(host, address);
364
303
  }
365
- /**
366
- * Returns true when `host` matches at least one entry in `allowedOutboundHosts`.
367
- * Supports exact hostnames (`api.example.com`) and wildcard prefixes (`*.example.com`).
368
- */
369
304
  isHostAllowed(host) {
370
305
  for (const allowed of this.allowedOutboundHosts ?? []) if (allowed.startsWith("*.")) {
371
306
  const suffix = allowed.slice(1);
@@ -399,21 +334,6 @@ var SsrfGuard = class {
399
334
 
400
335
  //#endregion
401
336
  //#region src/http/HttpRequestExecutor.ts
402
- /**
403
- * Executes a single HTTP request described by {@link HttpRequestSpec}.
404
- *
405
- * - Credential sessions provide header/query deltas via `applyToRequest`.
406
- * - Body encoding is delegated to {@link HttpBodyBuilder}.
407
- * - URL query merging is delegated to {@link HttpUrlBuilder}.
408
- * - SSRF protection is delegated to {@link SsrfGuard} (injected).
409
- * - Binary response bodies: when `download.mode` triggers binary attach, the
410
- * `bodyBinaryName` field is set in the result but the body is NOT read here.
411
- * Callers that need binary attachment should use `buildRequest` to get the
412
- * resolved URL + init and make the fetch + binary attach themselves.
413
- *
414
- * Collaborators (`fetch`, body builder, url builder, ssrfGuard) are injected so
415
- * callers own construction at composition roots and tests can supply deterministic stubs.
416
- */
417
337
  var HttpRequestExecutor = class {
418
338
  constructor(fetchFn, bodyBuilder, urlBuilder, ssrfGuard) {
419
339
  this.fetchFn = fetchFn;
@@ -421,14 +341,6 @@ var HttpRequestExecutor = class {
421
341
  this.urlBuilder = urlBuilder;
422
342
  this.ssrfGuard = ssrfGuard;
423
343
  }
424
- /**
425
- * Builds the fetch init (headers, query, body) from the spec + credential delta,
426
- * returning both the resolved URL and the RequestInit so callers can make the
427
- * actual fetch call themselves (useful for streaming / binary attach).
428
- *
429
- * Also performs SSRF protection via the injected {@link SsrfGuard} before
430
- * returning — throws {@link SSRFBlockedError} if the target is a private address.
431
- */
432
344
  async buildRequest(spec, item) {
433
345
  await this.ssrfGuard.check(spec.url, spec.allowPrivateNetworkTargets ?? false);
434
346
  const credentialDelta = spec.credential?.applyToRequest(spec) ?? {};
@@ -452,12 +364,6 @@ var HttpRequestExecutor = class {
452
364
  }
453
365
  };
454
366
  }
455
- /**
456
- * Executes an HTTP request and returns parsed result.
457
- * For binary downloads (when `shouldAttachBody` is true), the body is NOT consumed
458
- * and callers must call `ctx.binary.attach` directly using the resolved URL + init
459
- * (available via `buildRequest`).
460
- */
461
367
  async execute(spec, item) {
462
368
  const { url: resolvedUrl, init } = await this.buildRequest(spec, item);
463
369
  const response = await this.fetchFn(resolvedUrl, init);
@@ -514,10 +420,6 @@ var HttpRequestExecutor = class {
514
420
 
515
421
  //#endregion
516
422
  //#region src/http/HttpBodyBuilder.ts
517
- /**
518
- * Builds a fetch-compatible `BodyInit` + Content-Type pair from an {@link HttpBodySpec}.
519
- * Multipart binaries are read from `item.binary` via `ctx.binary.openReadStream`.
520
- */
521
423
  var HttpBodyBuilder = class {
522
424
  async build(spec, item, ctx) {
523
425
  if (!spec || spec.kind === "none") return;
@@ -588,10 +490,6 @@ var HttpBodyBuilder = class {
588
490
 
589
491
  //#endregion
590
492
  //#region src/http/HttpUrlBuilder.ts
591
- /**
592
- * Merges query parameters into a base URL.
593
- * Handles both scalar and array values, and preserves any existing params.
594
- */
595
493
  var HttpUrlBuilder = class {
596
494
  build(baseUrl, query) {
597
495
  if (!query || Object.keys(query).length === 0) return baseUrl;
@@ -604,39 +502,12 @@ var HttpUrlBuilder = class {
604
502
 
605
503
  //#endregion
606
504
  //#region src/authoring/defineRestNode.types.ts
607
- /**
608
- * Substitutes `{name}` placeholders in a path template using values from `params`.
609
- */
610
505
  function substitutePath(template, params) {
611
506
  return template.replace(/\{([^}]+)}/g, (_match, key) => {
612
507
  const value = params[key];
613
508
  return value !== void 0 ? String(value) : `{${key}}`;
614
509
  });
615
510
  }
616
- /**
617
- * Declarative helper for creating thin API-wrapper nodes.
618
- *
619
- * Usage:
620
- * ```ts
621
- * export const postMessage = defineRestNode({
622
- * key: "slack.post-message",
623
- * title: "Send Slack message",
624
- * icon: "si:slack",
625
- * api: { baseUrl: "https://slack.com/api", path: "/chat.postMessage", method: "POST" },
626
- * credentials: { auth: bearerTokenCredentialType },
627
- * inputSchema: z.object({ channel: z.string(), text: z.string() }),
628
- * request: ({ input }) => ({
629
- * body: { kind: "json", data: { channel: input.channel, text: input.text } },
630
- * }),
631
- * response: ({ json }) => ({ messageTs: (json as any).ts }),
632
- * });
633
- * ```
634
- *
635
- * - `defineRestNode` is a thin wrapper over `defineNode`; it does not introduce a new runtime kind.
636
- * - Credential sessions are resolved via the `credentials` binding map (same as `defineNode`).
637
- * - Path `{placeholder}` substitution is applied from `input` keys before the request is made.
638
- * - Non-2xx responses throw an `Error` by default (`errorPolicy: "throw"`).
639
- */
640
511
  function defineRestNode(options) {
641
512
  const errorPolicy = options.errorPolicy ?? "throw";
642
513
  return (0, __codemation_core.defineNode)({
@@ -4174,11 +4045,6 @@ function toJSONSchema(input, params) {
4174
4045
 
4175
4046
  //#endregion
4176
4047
  //#region src/nodes/ConnectionCredentialExecutionContextFactory.ts
4177
- /**
4178
- * Builds a {@link NodeExecutionContext} whose identity for credential binding and `getCredential`
4179
- * is a **connection-owned** workflow node id (`ConnectionNodeIdFactory` in `@codemation/core`),
4180
- * not the executing parent node. Use for LLM slots, tool slots, or any connection-scoped owner.
4181
- */
4182
4048
  var ConnectionCredentialExecutionContextFactory = class {
4183
4049
  credentialResolverFactory;
4184
4050
  constructor(credentialSessions) {
@@ -4201,15 +4067,6 @@ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
4201
4067
  createConnectionCredentialExecutionContextFactory(credentialSessions) {
4202
4068
  return new ConnectionCredentialExecutionContextFactory(credentialSessions);
4203
4069
  }
4204
- /**
4205
- * Produces a plain JSON Schema object (`draft-07`) from a Zod schema, as needed by
4206
- * OpenAI tool-parameter schemas and the structured-output repair prompt.
4207
- * - Prefers the schema's **instance** `toJSONSchema(...)` method so we stay inside the Zod
4208
- * instance that created the schema (works across consumer/framework tsx namespaces — see
4209
- * {@link ZodInstanceToJsonSchema}). Falls back to the framework-imported module function.
4210
- * - Strips root `$schema` (OpenAI ignores it).
4211
- * - Sanitizes `required` for cfworker json-schema compatibility (must be a string array or absent).
4212
- */
4213
4070
  createJsonSchemaRecord(inputSchema, options) {
4214
4071
  const { $schema: _draftSchemaOmitted,...rest } = this.convertZodSchemaToJsonSchema(inputSchema, { target: "draft-07" });
4215
4072
  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)}).`);
@@ -4218,20 +4075,11 @@ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
4218
4075
  this.sanitizeJsonSchemaRequiredKeywordsForCfworker(rest);
4219
4076
  return rest;
4220
4077
  }
4221
- /**
4222
- * Runs Zod's `toJSONSchema` via the schema's own instance method when available, so consumer
4223
- * schemas loaded under a different tsx namespace still convert correctly. If the caller handed us
4224
- * a payload that lacks that method (e.g. a plain JSON Schema record or a Zod instance whose
4225
- * prototype was stripped), we fall back to the framework-bundled module function.
4226
- */
4227
4078
  convertZodSchemaToJsonSchema(inputSchema, params) {
4228
4079
  const candidate = inputSchema.toJSONSchema;
4229
4080
  if (typeof candidate === "function") return candidate.call(inputSchema, params);
4230
4081
  return toJSONSchema(inputSchema, params);
4231
4082
  }
4232
- /**
4233
- * `@cfworker/json-schema` iterates `schema.required` with `for...of`; it must be a string array or absent.
4234
- */
4235
4083
  sanitizeJsonSchemaRequiredKeywordsForCfworker(node$20) {
4236
4084
  if (!node$20 || typeof node$20 !== "object" || Array.isArray(node$20)) return;
4237
4085
  const o = node$20;
@@ -4378,11 +4226,6 @@ var OpenAIChatModelConfig = class {
4378
4226
 
4379
4227
  //#endregion
4380
4228
  //#region src/chatModels/OpenAiChatModelPresetsFactory.ts
4381
- /**
4382
- * Default OpenAI chat model configs for scaffolds and demos (icon + label match {@link OpenAIChatModelConfig} defaults).
4383
- * Prefer importing {@link openAiChatModelPresets} from here or from the consumer template re-export
4384
- * instead of repeating {@link OpenAIChatModelConfig} construction in app workflows.
4385
- */
4386
4229
  var OpenAiChatModelPresets = class {
4387
4230
  demoGpt4oMini = new OpenAIChatModelConfig("OpenAI", "gpt-4o-mini");
4388
4231
  demoGpt41 = new OpenAIChatModelConfig("OpenAI", "gpt-4.1");
@@ -4391,19 +4234,6 @@ const openAiChatModelPresets = new OpenAiChatModelPresets();
4391
4234
 
4392
4235
  //#endregion
4393
4236
  //#region src/chatModels/ManagedHmacSignerFactory.types.ts
4394
- /**
4395
- * Creates an HMAC-signing fetch wrapper that authenticates requests to
4396
- * Codemation managed services (LLM broker, doc-scanner) with the
4397
- * Codemation-Hmac v=1 scheme.
4398
- *
4399
- * Mirrors HmacRequestSigner from @codemation/host/pairing without importing
4400
- * that package (which would create a circular dependency since @codemation/host
4401
- * depends on @codemation/core-nodes).
4402
- *
4403
- * @param workspaceId - Workspace identifier injected by the CP provisioner.
4404
- * @param pairingSecret - Base64-encoded 32-byte HMAC key injected by the provisioner.
4405
- * @param options - Optional behaviour flags and test seams.
4406
- */
4407
4237
  function managedHmacFetchFactory(workspaceId, pairingSecret, options) {
4408
4238
  const signBody = options?.signBody ?? true;
4409
4239
  return async (input, init) => {
@@ -4422,10 +4252,6 @@ function managedHmacFetchFactory(workspaceId, pairingSecret, options) {
4422
4252
  });
4423
4253
  };
4424
4254
  }
4425
- /**
4426
- * Produces a Codemation-Hmac v=1 Authorization header value.
4427
- * Algorithm must match HmacVerifier.computeSignature() in the control-plane.
4428
- */
4429
4255
  function buildHmacAuthHeader(workspaceId, pairingSecret, method, url, body, overrides) {
4430
4256
  const ts = overrides?.now ? overrides.now() : Math.floor(Date.now() / 1e3);
4431
4257
  const nonce = overrides?.nonce ? overrides.nonce() : (0, node_crypto.randomBytes)(16).toString("base64");
@@ -4486,21 +4312,137 @@ var CodemationChatModelConfig = class {
4486
4312
  }
4487
4313
  };
4488
4314
 
4315
+ //#endregion
4316
+ //#region src/nodes/AgentToolResultContentFactory.ts
4317
+ const MAX_INLINE_BYTES = 8 * 1024 * 1024;
4318
+ const KNOWN_MCP_BLOCK_TYPES = new Set([
4319
+ "text",
4320
+ "image",
4321
+ "audio",
4322
+ "resource",
4323
+ "resource_link"
4324
+ ]);
4325
+ var AgentToolResultContentFactory = class AgentToolResultContentFactory {
4326
+ static tryMapToContentOutput(result) {
4327
+ const blocks = AgentToolResultContentFactory.contentBlocks(result);
4328
+ if (blocks === void 0) return void 0;
4329
+ const parts = [];
4330
+ let inlinedBytes = 0;
4331
+ for (const block of blocks) {
4332
+ const mapped = AgentToolResultContentFactory.mapBlock(block, inlinedBytes);
4333
+ parts.push(mapped.part);
4334
+ inlinedBytes += mapped.bytes;
4335
+ }
4336
+ return parts;
4337
+ }
4338
+ static contentBlocks(result) {
4339
+ if (result === null || typeof result !== "object") return void 0;
4340
+ const content = result.content;
4341
+ if (!Array.isArray(content) || content.length === 0) return void 0;
4342
+ if (!content.every((block) => block !== null && typeof block === "object" && typeof block.type === "string")) return void 0;
4343
+ return content.some((block) => KNOWN_MCP_BLOCK_TYPES.has(block.type)) ? content : void 0;
4344
+ }
4345
+ static mapBlock(block, inlinedBytesSoFar) {
4346
+ const type = block.type;
4347
+ if (type === "text" && typeof block.text === "string") return {
4348
+ part: {
4349
+ type: "text",
4350
+ text: block.text
4351
+ },
4352
+ bytes: 0
4353
+ };
4354
+ if (type === "image" && typeof block.data === "string" && typeof block.mimeType === "string") return AgentToolResultContentFactory.mapBinary({
4355
+ base64: block.data,
4356
+ mediaType: block.mimeType,
4357
+ inlinedBytesSoFar
4358
+ });
4359
+ if (type === "resource" && block.resource) return AgentToolResultContentFactory.mapEmbeddedResource(block.resource, inlinedBytesSoFar);
4360
+ if (type === "resource_link" && typeof block.uri === "string") {
4361
+ const mime = typeof block.mimeType === "string" ? ` (${block.mimeType})` : "";
4362
+ return {
4363
+ part: {
4364
+ type: "text",
4365
+ text: `[linked resource: ${block.uri}${mime}]`
4366
+ },
4367
+ bytes: 0
4368
+ };
4369
+ }
4370
+ return {
4371
+ part: {
4372
+ type: "text",
4373
+ text: `[unsupported tool content block: ${String(type)}]`
4374
+ },
4375
+ bytes: 0
4376
+ };
4377
+ }
4378
+ static mapEmbeddedResource(resource, inlinedBytesSoFar) {
4379
+ if (typeof resource.text === "string") return {
4380
+ part: {
4381
+ type: "text",
4382
+ text: resource.text
4383
+ },
4384
+ bytes: 0
4385
+ };
4386
+ if (typeof resource.blob === "string" && typeof resource.mimeType === "string") return AgentToolResultContentFactory.mapBinary({
4387
+ base64: resource.blob,
4388
+ mediaType: resource.mimeType,
4389
+ filename: typeof resource.name === "string" ? resource.name : void 0,
4390
+ inlinedBytesSoFar
4391
+ });
4392
+ return {
4393
+ part: {
4394
+ type: "text",
4395
+ text: `[embedded resource: ${typeof resource.uri === "string" ? resource.uri : "unknown"}]`
4396
+ },
4397
+ bytes: 0
4398
+ };
4399
+ }
4400
+ static mapBinary(args) {
4401
+ const rawBytes = Math.floor(args.base64.length * 3 / 4);
4402
+ if (args.inlinedBytesSoFar + rawBytes > MAX_INLINE_BYTES) {
4403
+ const name = args.filename ? ` "${args.filename}"` : "";
4404
+ const kb = Math.round(rawBytes / 1024);
4405
+ return {
4406
+ part: {
4407
+ type: "text",
4408
+ text: `[binary${name} (${args.mediaType}, ~${kb} KB) omitted: exceeds the per-tool-result inline limit]`
4409
+ },
4410
+ bytes: 0
4411
+ };
4412
+ }
4413
+ if (args.mediaType.startsWith("image/")) return {
4414
+ part: {
4415
+ type: "image-data",
4416
+ data: args.base64,
4417
+ mediaType: args.mediaType
4418
+ },
4419
+ bytes: rawBytes
4420
+ };
4421
+ if (args.mediaType === "application/pdf") return {
4422
+ part: {
4423
+ type: "file-data",
4424
+ data: args.base64,
4425
+ mediaType: args.mediaType,
4426
+ ...args.filename ? { filename: args.filename } : {}
4427
+ },
4428
+ bytes: rawBytes
4429
+ };
4430
+ return {
4431
+ part: {
4432
+ type: "text",
4433
+ text: `[binary${args.filename ? ` "${args.filename}"` : ""} (${args.mediaType}) not inlined: unsupported by the model]`
4434
+ },
4435
+ bytes: 0
4436
+ };
4437
+ }
4438
+ };
4439
+
4489
4440
  //#endregion
4490
4441
  //#region src/nodes/AgentMessageFactory.ts
4491
- /**
4492
- * AI-SDK-shaped message construction for the AIAgent stack. Emits plain `ModelMessage[]`
4493
- * ( `{ role: 'system' | 'user' | 'assistant' | 'tool', content: ... }` ) as consumed by
4494
- * `generateText({ messages })` from the `ai` package.
4495
- */
4496
4442
  var AgentMessageFactory = class AgentMessageFactory {
4497
4443
  static createPromptMessages(messages) {
4498
4444
  return messages.map((message) => this.createPromptMessage(message));
4499
4445
  }
4500
- /**
4501
- * Builds the assistant message that contains optional text plus one or more tool-call parts,
4502
- * matching the shape AI SDK emits between steps.
4503
- */
4504
4446
  static createAssistantWithToolCalls(text, toolCalls) {
4505
4447
  const content = [];
4506
4448
  if (text && text.length > 0) content.push({
@@ -4518,24 +4460,30 @@ var AgentMessageFactory = class AgentMessageFactory {
4518
4460
  content
4519
4461
  };
4520
4462
  }
4521
- /**
4522
- * Builds the `{ role: "tool", content: [{ type: "tool-result", ... }, ...] }` message returned
4523
- * to the model after each tool round.
4524
- */
4525
- static createToolResultsMessage(executedToolCalls) {
4463
+ static createToolResultsMessage(executedToolCalls, passToolBinariesToModel = true) {
4526
4464
  return {
4527
4465
  role: "tool",
4528
4466
  content: executedToolCalls.map((executed) => ({
4529
4467
  type: "tool-result",
4530
4468
  toolCallId: executed.toolCallId,
4531
4469
  toolName: executed.toolName,
4532
- output: {
4533
- type: "json",
4534
- value: AgentMessageFactory.toToolResultJson(executed.result)
4535
- }
4470
+ output: AgentMessageFactory.toToolResultOutput(executed.result, passToolBinariesToModel)
4536
4471
  }))
4537
4472
  };
4538
4473
  }
4474
+ static toToolResultOutput(result, passToolBinariesToModel) {
4475
+ if (passToolBinariesToModel) {
4476
+ const content = AgentToolResultContentFactory.tryMapToContentOutput(result);
4477
+ if (content !== void 0) return {
4478
+ type: "content",
4479
+ value: content
4480
+ };
4481
+ }
4482
+ return {
4483
+ type: "json",
4484
+ value: AgentMessageFactory.toToolResultJson(result)
4485
+ };
4486
+ }
4539
4487
  static toToolResultJson(value) {
4540
4488
  if (value === void 0) return null;
4541
4489
  try {
@@ -5332,10 +5280,6 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
5332
5280
  }
5333
5281
  throw new Error(`Structured output required for AIAgent "${args.agentName}" (${args.nodeId}) but validation still failed after ${_AgentStructuredOutputRunner.repairAttemptCount} repair attempts: ${failure.validationError}`);
5334
5282
  }
5335
- /**
5336
- * Chooses strict mode for OpenAI chat-model configs, off otherwise. Extendable in future for
5337
- * other providers that adopt the same "supply a JSON Schema record directly" contract.
5338
- */
5339
5283
  resolveStructuredOutputOptions(chatModelConfig) {
5340
5284
  if (chatModelConfig.type !== OpenAIChatModelFactory) return;
5341
5285
  return {
@@ -5818,10 +5762,6 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
5818
5762
  extractErrorDetails(error) {
5819
5763
  return error.details;
5820
5764
  }
5821
- /**
5822
- * Extracts the text content from the last assistant message in the conversation snapshot.
5823
- * Used to populate `agentReasoning` in the HITL suspension metadata.
5824
- */
5825
5765
  extractLastAssistantText(conversation) {
5826
5766
  for (let i = conversation.length - 1; i >= 0; i--) {
5827
5767
  const msg = conversation[i];
@@ -5856,16 +5796,6 @@ AgentToolExecutionCoordinator = __decorate([
5856
5796
 
5857
5797
  //#endregion
5858
5798
  //#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
5799
  var AgentBinaryContentFactory = class AgentBinaryContentFactory {
5870
5800
  static toContentPart(binary) {
5871
5801
  if (binary.mediaType.startsWith("image/")) return {
@@ -5944,20 +5874,6 @@ let NodeBackedToolRuntime = class NodeBackedToolRuntime$1 {
5944
5874
  outputs
5945
5875
  });
5946
5876
  }
5947
- /**
5948
- * Returns a re-rooted child ctx for nested-agent tools (so their LLM/tool connection ids derive
5949
- * from the tool connection node, telemetry parents under the tool-call span, and connection
5950
- * invocations carry `parentInvocationId`). Plain runnable tools (non-agent) keep the orchestrator
5951
- * ctx with only `config` swapped — no nesting concern.
5952
- *
5953
- * The caller (`AIAgentNode.createItemScopedTools`) already wraps the orchestrator ctx via
5954
- * `ConnectionCredentialExecutionContextFactory.forConnectionNode`, so `args.ctx.nodeId` is the
5955
- * tool's own connection node id (e.g. `AIAgentNode:2__conn__tool__searchInMail`). We pass that
5956
- * through as the sub-agent's `nodeId`; deriving another `toolConnectionNodeId(args.ctx.nodeId,
5957
- * config.name)` here would prepend a duplicate `__conn__tool__<name>` segment and exponentially
5958
- * deepen ids on each invocation, which also breaks credential resolution because user-provided
5959
- * bindings sit on the single-level connection node id.
5960
- */
5961
5877
  resolveNodeCtx(config$1, args) {
5962
5878
  const isNestedAgent = __codemation_core.AgentConfigInspector.isAgentNodeConfig(config$1.node);
5963
5879
  const hooks = args.hooks;
@@ -6022,22 +5938,12 @@ NodeBackedToolRuntime = __decorate([
6022
5938
 
6023
5939
  //#endregion
6024
5940
  //#region src/nodes/BM25Index.ts
6025
- /**
6026
- * Minimal BM25 (Okapi BM25) implementation for indexing MCP tool descriptions.
6027
- *
6028
- * Parameters: k1=1.5, b=0.75 (standard defaults).
6029
- * Tokenisation: lowercase, split on non-alphanumerics, filter empties.
6030
- */
6031
5941
  var BM25Index = class {
6032
5942
  k1 = 1.5;
6033
5943
  b = .75;
6034
5944
  tf = [];
6035
5945
  df = /* @__PURE__ */ new Map();
6036
5946
  avgDocLen = 0;
6037
- /**
6038
- * Add all documents at once. After calling this, search is available.
6039
- * Documents are indexed in insertion order; search returns their indices.
6040
- */
6041
5947
  add(docs) {
6042
5948
  const docTerms = docs.map((d) => this.tokenize(d));
6043
5949
  let totalLen = 0;
@@ -6050,10 +5956,6 @@ var BM25Index = class {
6050
5956
  }
6051
5957
  this.avgDocLen = docTerms.length > 0 ? totalLen / docTerms.length : 0;
6052
5958
  }
6053
- /**
6054
- * Returns up to `limit` document indices ranked by BM25 score (highest first).
6055
- * Returns an empty array if the index is empty or the query matches nothing.
6056
- */
6057
5959
  search(query, limit) {
6058
5960
  const n = this.tf.length;
6059
5961
  if (n === 0) return [];
@@ -6094,26 +5996,12 @@ const PINNED_TOOLS_SOFT_LIMIT = 8;
6094
5996
  const PINNED_TOOLS_HARD_LIMIT = 16;
6095
5997
  const FIND_TOOLS_NAME = "find_tools";
6096
5998
  const FIND_TOOLS_DEFAULT_LIMIT = 5;
6097
- /**
6098
- * Default tool-loading strategy: BM25-indexed MCP tool deferral via a `find_tools` meta-tool.
6099
- *
6100
- * - Node-backed tools and pinned MCP tools are always included in every turn.
6101
- * - `find_tools(query, limit?)` is added to the tool set when MCP tools are indexed.
6102
- * - Tools surfaced by `find_tools` are included in subsequent turns.
6103
- *
6104
- * Not DI-managed; instantiated per agent execution by DeferredMetaToolStrategyFactory.
6105
- */
6106
5999
  var DeferredMetaToolStrategy = class {
6107
6000
  nodeBackedTools = {};
6108
6001
  pinnedTools = {};
6109
6002
  mcpEntries = [];
6110
6003
  toolsByServerId = /* @__PURE__ */ new Map();
6111
6004
  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
6005
  jsonSchema;
6118
6006
  constructor(bm25, warnFn) {
6119
6007
  this.bm25 = bm25;
@@ -6261,11 +6149,6 @@ let AIAgentNode = class AIAgentNode$1 {
6261
6149
  inputSchema = unknown();
6262
6150
  connectionCredentialExecutionContextFactory;
6263
6151
  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
6152
  aiSdk;
6270
6153
  aiSdkPromise = null;
6271
6154
  constructor(nodeResolver, credentialSessions, nodeBackedToolRuntime, executionHelpers, structuredOutputRunner, toolExecutionCoordinator, toolLoadingStrategyFactory, agentMcpIntegration) {
@@ -6289,15 +6172,9 @@ let AIAgentNode = class AIAgentNode$1 {
6289
6172
  };
6290
6173
  return (await this.runAgentForItem(prepared, itemWithMappedJson, args.itemIndex, args.items)).json;
6291
6174
  }
6292
- /** Load the `ai` SDK once per node instance (cached promise guards concurrent items). */
6293
6175
  async ensureAiSdk() {
6294
6176
  this.aiSdk = await (this.aiSdkPromise ??= import("ai"));
6295
6177
  }
6296
- /**
6297
- * Resume path: re-enters the agent loop after a HITL suspension.
6298
- * Reconstructs the conversation from the checkpoint, injects the human decision
6299
- * as a tool_result, and continues the loop from where it suspended.
6300
- */
6301
6178
  async executeResumed(args, resumeContext) {
6302
6179
  const { ctx } = args;
6303
6180
  const taskMetadata = resumeContext.task.metadata ?? {};
@@ -6324,7 +6201,7 @@ let AIAgentNode = class AIAgentNode$1 {
6324
6201
  result: decision,
6325
6202
  serialized: JSON.stringify(decision)
6326
6203
  };
6327
- const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry])];
6204
+ const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry], ctx.config.passToolBinariesToModel !== false)];
6328
6205
  const loopResult = await this.runTurnLoopUntilFinalAnswer({
6329
6206
  prepared,
6330
6207
  itemInputsByPort,
@@ -6344,10 +6221,6 @@ let AIAgentNode = class AIAgentNode$1 {
6344
6221
  const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
6345
6222
  return this.buildOutputItem(item, outputJson).json;
6346
6223
  }
6347
- /**
6348
- * Normalizes a {@link ResumeContext} decision into a flat JSON-serializable shape
6349
- * suitable for injection as a tool_result content.
6350
- */
6351
6224
  normalizeDecision(resumeContext) {
6352
6225
  const { decision } = resumeContext;
6353
6226
  if (decision.kind === "decided") {
@@ -6474,16 +6347,6 @@ let AIAgentNode = class AIAgentNode$1 {
6474
6347
  const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
6475
6348
  return this.buildOutputItem(item, outputJson);
6476
6349
  }
6477
- /**
6478
- * Multi-turn loop:
6479
- * - Each turn is a single `generateText` call with tools exposed but **not auto-executed**
6480
- * (we control tool dispatch so that {@link AgentToolExecutionCoordinator} drives repair /
6481
- * connection-invocation recording / transient-error handling exactly like before).
6482
- * - When the model returns no tool calls the loop ends with the model's text as the final answer.
6483
- * - Respects `guardrails.maxTurns` and `guardrails.onTurnLimitReached`.
6484
- * - Strategy-owned tool calls (e.g. `find_tools`) are dispatched via the strategy, not the
6485
- * coordinator; their results are tracked so subsequent turns receive the discovered tools.
6486
- */
6487
6350
  async runTurnLoopUntilFinalAnswer(args) {
6488
6351
  const { prepared, itemInputsByPort, itemScopedTools, conversation } = args;
6489
6352
  const { ctx, guardrails, toolLoadingStrategy } = prepared;
@@ -6491,7 +6354,6 @@ let AIAgentNode = class AIAgentNode$1 {
6491
6354
  let toolCallCount = args.resumedToolCallCount ?? 0;
6492
6355
  let turnCount = 0;
6493
6356
  const repairAttemptsByToolName = /* @__PURE__ */ new Map();
6494
- /** Tool IDs surfaced by find_tools across all prior turns in this item run. */
6495
6357
  let previousFoundToolIds = [];
6496
6358
  for (let turn = 1; turn <= guardrails.maxTurns; turn++) {
6497
6359
  turnCount = turn;
@@ -6544,7 +6406,7 @@ let AIAgentNode = class AIAgentNode$1 {
6544
6406
  coordinatorExecutedCalls.push(...executed);
6545
6407
  }
6546
6408
  const allExecutedCalls = [...strategyExecutedCalls, ...coordinatorExecutedCalls];
6547
- this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, allExecutedCalls);
6409
+ this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, allExecutedCalls, ctx.config.passToolBinariesToModel !== false);
6548
6410
  }
6549
6411
  return {
6550
6412
  finalText,
@@ -6559,8 +6421,8 @@ let AIAgentNode = class AIAgentNode$1 {
6559
6421
  if (guardrails.onTurnLimitReached === "respondWithLastMessage") return;
6560
6422
  throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" reached maxTurns=${guardrails.maxTurns} before producing a final response.`);
6561
6423
  }
6562
- appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls) {
6563
- conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls));
6424
+ appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls, passToolBinariesToModel) {
6425
+ conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls, passToolBinariesToModel));
6564
6426
  }
6565
6427
  async resolveFinalOutputJson(prepared, itemInputsByPort, conversation, finalText, wasToolEnabledRun) {
6566
6428
  if (!prepared.ctx.config.outputSchema) return AgentOutputFactory.fromAgentContent(finalText);
@@ -6621,12 +6483,6 @@ let AIAgentNode = class AIAgentNode$1 {
6621
6483
  };
6622
6484
  });
6623
6485
  }
6624
- /**
6625
- * Resolves the HITL behavior for a tool binding, or `undefined` when it is not a HITL tool.
6626
- * A binding is HITL if either the backing node carries a `defineHumanApprovalNode` marker or the
6627
- * binding sets a per-binding `onRejected` via `asTool(..., { onRejected })`. The per-binding value
6628
- * wins over the node marker, so two tools backed by the same node can reject differently.
6629
- */
6630
6486
  resolveHumanApprovalBehavior(config$1) {
6631
6487
  if (!this.isNodeBackedToolConfig(config$1)) return void 0;
6632
6488
  const marker = config$1.node.humanApprovalToolBehavior;
@@ -6634,11 +6490,6 @@ let AIAgentNode = class AIAgentNode$1 {
6634
6490
  if (marker === void 0 && perBinding === void 0) return void 0;
6635
6491
  return { onRejected: perBinding ?? marker?.onRejected ?? "return" };
6636
6492
  }
6637
- /**
6638
- * Invoke a text turn using the merged tool set from item-scoped tools (coordinator-managed)
6639
- * and strategy tools (find_tools + discovered MCP tools).
6640
- * Strategy tools take precedence for names that overlap.
6641
- */
6642
6493
  async invokeTextTurnWithStrategyTools(prepared, itemInputsByPort, messages, itemScopedTools, strategyTools) {
6643
6494
  const itemToolSet = this.buildToolSet(itemScopedTools);
6644
6495
  const strategyHasTools = Object.keys(strategyTools).length > 0;
@@ -6649,10 +6500,6 @@ let AIAgentNode = class AIAgentNode$1 {
6649
6500
  } : void 0;
6650
6501
  return this.invokeTextTurnWithToolSet(prepared, itemInputsByPort, messages, mergedTools);
6651
6502
  }
6652
- /**
6653
- * Removes `execute` properties from ToolSet entries so the AI SDK does not
6654
- * auto-execute them within `generateText`. Codemation owns all tool dispatch.
6655
- */
6656
6503
  stripExecuteCallbacks(tools) {
6657
6504
  const stripped = {};
6658
6505
  for (const [name, def] of Object.entries(tools)) {
@@ -6661,12 +6508,6 @@ let AIAgentNode = class AIAgentNode$1 {
6661
6508
  }
6662
6509
  return stripped;
6663
6510
  }
6664
- /**
6665
- * Builds a ToolSet from resolved tools for strategy initialization.
6666
- * The strategy uses this for its "always-included" node-backed tool descriptions.
6667
- * HITL tools (detected via the `humanApprovalToolBehavior` field set by `defineHumanApprovalNode`) get the solo-constraint sentence
6668
- * appended to their description.
6669
- */
6670
6511
  buildToolSetFromResolved(resolvedTools) {
6671
6512
  if (resolvedTools.length === 0) return {};
6672
6513
  const toolSet = {};
@@ -6684,20 +6525,6 @@ let AIAgentNode = class AIAgentNode$1 {
6684
6525
  }
6685
6526
  return toolSet;
6686
6527
  }
6687
- /**
6688
- * Builds an AI SDK {@link ToolSet} where every tool ships a pre-converted JSON Schema (via
6689
- * {@link jsonSchema}) — not the raw Zod schema — and carries **no** `execute`. Two reasons:
6690
- *
6691
- * 1. Codemation owns tool dispatch + the per-tool repair loop (see {@link AgentToolExecutionCoordinator}),
6692
- * so the AI SDK must surface tool calls back to us instead of auto-running them.
6693
- * 2. The AI SDK's `asSchema` helper discriminates between Zod v3 / Zod v4 / Standard Schema via
6694
- * runtime feature-detection (`~standard`, `_zod`, etc.). Handing it a pre-built
6695
- * {@link jsonSchema} record — which is tagged with `Symbol.for('vercel.ai.schema')` — skips all
6696
- * of that detection and guarantees the provider receives a draft-07 JSON Schema with
6697
- * `additionalProperties: false` at every object depth (see {@link OpenAiStrictJsonSchemaFactory}
6698
- * for the same logic applied to structured-output schemas). Codemation still runs its own Zod
6699
- * validation on tool inputs before execute — the schema handed to the model is advisory.
6700
- */
6701
6528
  buildToolSet(itemScopedTools) {
6702
6529
  if (itemScopedTools.length === 0) return void 0;
6703
6530
  const toolSet = {};
@@ -6715,10 +6542,6 @@ let AIAgentNode = class AIAgentNode$1 {
6715
6542
  }
6716
6543
  return toolSet;
6717
6544
  }
6718
- /**
6719
- * One `generateText` turn (no auto tool execution) with Codemation-owned child-span telemetry
6720
- * and connection-invocation state recording. Accepts a pre-built ToolSet.
6721
- */
6722
6545
  async invokeTextTurnWithToolSet(prepared, itemInputsByPort, messages, tools) {
6723
6546
  const invocationId = __codemation_core.ConnectionInvocationIdFactory.create();
6724
6547
  const startedAt = /* @__PURE__ */ new Date();
@@ -6829,11 +6652,6 @@ let AIAgentNode = class AIAgentNode$1 {
6829
6652
  });
6830
6653
  }
6831
6654
  }
6832
- /**
6833
- * Structured-output turn: runs `generateText({ output: Output.object({ schema }) })` via the
6834
- * structured-output runner. We keep this as a separate helper because the runner needs the raw
6835
- * validated value (not just text) back, and must be able to retry on Zod failures.
6836
- */
6837
6655
  async invokeStructuredTurn(prepared, itemInputsByPort, schema, messages, structuredOptions) {
6838
6656
  const invocationId = __codemation_core.ConnectionInvocationIdFactory.create();
6839
6657
  const startedAt = /* @__PURE__ */ new Date();
@@ -6958,13 +6776,6 @@ let AIAgentNode = class AIAgentNode$1 {
6958
6776
  providerOptions: overrides?.providerOptions ?? defaults.providerOptions
6959
6777
  };
6960
6778
  }
6961
- /**
6962
- * Build a no-code-friendly output payload for an LLM round.
6963
- *
6964
- * Always includes `content` (matching the canvas snapshot shape used elsewhere) and adds a
6965
- * `toolCalls` array when the round produced tool calls so the execution inspector surfaces the
6966
- * planned calls instead of just an empty `""` for tool-only rounds.
6967
- */
6968
6779
  summarizeTurnOutput(turnResult) {
6969
6780
  if (turnResult.toolCalls.length === 0) return { content: turnResult.text };
6970
6781
  const toolCalls = turnResult.toolCalls.map((toolCall) => ({
@@ -7167,12 +6978,6 @@ let AIAgentNode = class AIAgentNode$1 {
7167
6978
  const binaries = await this.resolveInlineBinaries(attachments, ctx);
7168
6979
  return AgentBinaryContentFactory.withBinaries(promptMessages, binaries);
7169
6980
  }
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
6981
  selectBinaryAttachments(item, itemIndex, items, ctx) {
7177
6982
  const manual = ctx.config.binaries;
7178
6983
  if (manual !== void 0) return typeof manual === "function" ? manual({
@@ -7183,14 +6988,6 @@ let AIAgentNode = class AIAgentNode$1 {
7183
6988
  }) : manual;
7184
6989
  return item.binary ? Object.values(item.binary) : [];
7185
6990
  }
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
6991
  async resolveInlineBinaries(attachments, ctx) {
7195
6992
  const resolved = [];
7196
6993
  for (const attachment of attachments) {
@@ -7203,12 +7000,6 @@ let AIAgentNode = class AIAgentNode$1 {
7203
7000
  }
7204
7001
  return resolved;
7205
7002
  }
7206
- /**
7207
- * When `item.json.__source` matches an entry in `config.untrustedSources`
7208
- * (default: `["gmail", "ocr", "webhook"]`), wraps every user-role message
7209
- * content with an untrusted-external-source preamble so the LLM treats the
7210
- * content as data, not instructions.
7211
- */
7212
7003
  wrapUntrustedSourceMessages(messages, item, config$1) {
7213
7004
  const source = item.json !== null && typeof item.json === "object" ? item.json.__source : void 0;
7214
7005
  if (typeof source !== "string") return messages;
@@ -7301,10 +7092,6 @@ AIAgentNode = __decorate([
7301
7092
 
7302
7093
  //#endregion
7303
7094
  //#region src/nodes/AIAgentConfig.ts
7304
- /**
7305
- * AI agent: credential bindings are keyed to connection-owned LLM/tool node ids (ConnectionNodeIdFactory),
7306
- * not to the agent workflow node id.
7307
- */
7308
7095
  var AIAgent = class {
7309
7096
  kind = "node";
7310
7097
  type = AIAgentNode;
@@ -7324,6 +7111,7 @@ var AIAgent = class {
7324
7111
  pinnedMcpTools;
7325
7112
  untrustedSources;
7326
7113
  passBinariesToModel;
7114
+ passToolBinariesToModel;
7327
7115
  binaries;
7328
7116
  constructor(options) {
7329
7117
  this.name = options.name;
@@ -7340,6 +7128,7 @@ var AIAgent = class {
7340
7128
  this.pinnedMcpTools = options.pinnedMcpTools;
7341
7129
  this.untrustedSources = options.untrustedSources;
7342
7130
  this.passBinariesToModel = options.passBinariesToModel;
7131
+ this.passToolBinariesToModel = options.passToolBinariesToModel;
7343
7132
  this.binaries = options.binaries;
7344
7133
  }
7345
7134
  inspectorSummary() {
@@ -7400,11 +7189,6 @@ AssertionNode = __decorate([(0, __codemation_core.node)({ packageName: "@codemat
7400
7189
 
7401
7190
  //#endregion
7402
7191
  //#region src/nodes/assertion.ts
7403
- /**
7404
- * Generic assertion node — the "callback" form. For declarative shorthands (StringEquals,
7405
- * JudgeByAgent) compose this with helpers added in later phases. Sets `emitsAssertions: true`
7406
- * so host-side persisters know to record its outputs as `TestAssertion` rows.
7407
- */
7408
7192
  var Assertion = class {
7409
7193
  kind = "node";
7410
7194
  type = AssertionNode;
@@ -7667,17 +7451,12 @@ HttpRequestNode = __decorate([(0, __codemation_core.node)({ packageName: "@codem
7667
7451
 
7668
7452
  //#endregion
7669
7453
  //#region src/nodes/httpRequest.ts
7670
- /**
7671
- * The built-in HTTP request credential type IDs accepted by the `HttpRequest` node.
7672
- * These match the four generic credential types shipped with `@codemation/core-nodes`.
7673
- */
7674
7454
  const HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES = [
7675
7455
  bearerTokenCredentialType.definition.typeId,
7676
7456
  apiKeyCredentialType.definition.typeId,
7677
7457
  basicAuthCredentialType.definition.typeId,
7678
7458
  oauth2ClientCredentialsType.definition.typeId
7679
7459
  ];
7680
- /** Default maximum response size for binary mode: 100 MiB. */
7681
7460
  const DEFAULT_RESPONSE_SIZE_CAP_BYTES = 100 * 1024 * 1024;
7682
7461
  var HttpRequest = class {
7683
7462
  kind = "node";
@@ -7846,9 +7625,6 @@ var Filter = class {
7846
7625
  function getOriginIndex(item) {
7847
7626
  return (0, __codemation_core.getOriginIndexFromItem)(item);
7848
7627
  }
7849
- /**
7850
- * Tags items routed to fan-in merge-by-origin (same contract as {@link IfNode} / {@link SwitchNode}).
7851
- */
7852
7628
  function tagItemForRouterFanIn(args) {
7853
7629
  const { item, itemIndex, nodeId, inputPortLabel = "$in" } = args;
7854
7630
  const metaBase = item.meta && typeof item.meta === "object" ? item.meta : {};
@@ -7939,10 +7715,6 @@ IsTestRunNode = __decorate([(0, __codemation_core.node)({ packageName: "@codemat
7939
7715
 
7940
7716
  //#endregion
7941
7717
  //#region src/nodes/isTestRun.ts
7942
- /**
7943
- * Branches per-item on whether the current run is a test run. Output ports: `true`, `false`.
7944
- * The wire payload is unchanged — this is a router, not a transform.
7945
- */
7946
7718
  var IsTestRun = class {
7947
7719
  kind = "node";
7948
7720
  type = IsTestRunNode;
@@ -8026,10 +7798,6 @@ var Split = class {
8026
7798
  type = SplitNode;
8027
7799
  execution = { hint: "local" };
8028
7800
  keepBinaries = true;
8029
- /**
8030
- * When splitting yields zero items for a batch, downstream single-input nodes still run once with an empty batch.
8031
- * Mirrors {@link MapData}'s empty-output behavior.
8032
- */
8033
7801
  continueWhenEmptyOutput = true;
8034
7802
  icon = "builtin:split-rows";
8035
7803
  id;
@@ -8083,15 +7851,6 @@ CronTriggerNode = __decorate([(0, __codemation_core.node)({ packageName: "@codem
8083
7851
 
8084
7852
  //#endregion
8085
7853
  //#region src/nodes/CronTriggerFactory.ts
8086
- /**
8087
- * Schedules a workflow on a standard cron expression.
8088
- *
8089
- * Each tick emits one item: `{ firedAt: string, scheduledFor: string }` — both ISO-8601 timestamps.
8090
- * `firedAt` is the wall-clock moment the callback ran; `scheduledFor` is the cron-computed
8091
- * firing instant (these differ when the job was delayed).
8092
- *
8093
- * Timezone defaults to UTC when omitted — cron without an explicit TZ is a DST footgun.
8094
- */
8095
7854
  var CronTrigger = class {
8096
7855
  kind = "trigger";
8097
7856
  type = CronTriggerNode;
@@ -8162,7 +7921,6 @@ var ManualTrigger = class ManualTrigger {
8162
7921
  defaultItems;
8163
7922
  id;
8164
7923
  description;
8165
- /** Manual runs often emit an empty batch; still schedule downstream by default. */
8166
7924
  continueWhenEmptyOutput = true;
8167
7925
  constructor(name = "Manual trigger", defaultItemsOrId, idOrOptions) {
8168
7926
  this.name = name;
@@ -8217,7 +7975,6 @@ var MapData = class {
8217
7975
  kind = "node";
8218
7976
  type = MapDataNode;
8219
7977
  execution = { hint: "local" };
8220
- /** Zero mapped items should still allow downstream nodes to run. */
8221
7978
  continueWhenEmptyOutput = true;
8222
7979
  icon = "lucide:square-pen";
8223
7980
  keepBinaries;
@@ -8450,11 +8207,6 @@ TestTriggerNode = __decorate([(0, __codemation_core.node)({ packageName: "@codem
8450
8207
 
8451
8208
  //#endregion
8452
8209
  //#region src/nodes/testTrigger.ts
8453
- /**
8454
- * Trigger config for a test fixture source. Drop one (or more) of these on the canvas alongside
8455
- * a workflow's live triggers; clicking "Run tests" on the Tests tab invokes
8456
- * {@link TestTriggerOptions.generateItems} via the TestSuiteOrchestrator.
8457
- */
8458
8210
  var TestTrigger = class {
8459
8211
  kind = "trigger";
8460
8212
  triggerKind = "test";
@@ -8528,7 +8280,6 @@ var Wait = class {
8528
8280
  kind = "node";
8529
8281
  type = WaitNode;
8530
8282
  execution = { hint: "local" };
8531
- /** Pass-through empty batches should still advance to downstream nodes. */
8532
8283
  continueWhenEmptyOutput = true;
8533
8284
  icon = "lucide:hourglass";
8534
8285
  id;
@@ -8630,6 +8381,39 @@ var WebhookTrigger = class WebhookTrigger {
8630
8381
  }
8631
8382
  };
8632
8383
 
8384
+ //#endregion
8385
+ //#region src/nodes/schedulePollingTrigger.ts
8386
+ const schedulePollingTrigger = (0, __codemation_core.definePollingTrigger)({
8387
+ key: "schedule.interval",
8388
+ packageName: "@codemation/core-nodes",
8389
+ title: "Run on schedule",
8390
+ description: "Emit one tick item on every poll cycle.",
8391
+ icon: "lucide:clock",
8392
+ pollIntervalMs: 6e4,
8393
+ initialState() {
8394
+ return { tick: 0 };
8395
+ },
8396
+ poll({ state }) {
8397
+ const tick = (state ?? { tick: 0 }).tick + 1;
8398
+ return {
8399
+ items: [{ json: {
8400
+ firedAt: (/* @__PURE__ */ new Date()).toISOString(),
8401
+ tick
8402
+ } }],
8403
+ nextState: { tick }
8404
+ };
8405
+ },
8406
+ execute(items) {
8407
+ return { main: items };
8408
+ },
8409
+ testItems() {
8410
+ return [{ json: {
8411
+ firedAt: (/* @__PURE__ */ new Date()).toISOString(),
8412
+ tick: 0
8413
+ } }];
8414
+ }
8415
+ });
8416
+
8633
8417
  //#endregion
8634
8418
  //#region src/nodes/ConnectionCredentialNode.ts
8635
8419
  let ConnectionCredentialNode = class ConnectionCredentialNode$1 {
@@ -8643,11 +8427,6 @@ ConnectionCredentialNode = __decorate([(0, __codemation_core.node)({ packageName
8643
8427
 
8644
8428
  //#endregion
8645
8429
  //#region src/register.types.ts
8646
- /**
8647
- * Registrar for built-in nodes. In a real project, this would use tsyringe's
8648
- * container.registerSingleton(...). For the skeleton we keep it token-based:
8649
- * the engine resolves node implementations by class token.
8650
- */
8651
8430
  function registerCoreNodes(container) {}
8652
8431
 
8653
8432
  //#endregion
@@ -8876,9 +8655,6 @@ function workflow(id) {
8876
8655
 
8877
8656
  //#endregion
8878
8657
  //#region src/workflows/AIAgentConnectionWorkflowExpander.ts
8879
- /**
8880
- * Materializes connection-owned child nodes and {@link WorkflowDefinition.connections} for AI agent nodes.
8881
- */
8882
8658
  var AIAgentConnectionWorkflowExpander = class {
8883
8659
  constructor(connectionCredentialNodeConfigFactory, mcpServerResolver) {
8884
8660
  this.connectionCredentialNodeConfigFactory = connectionCredentialNodeConfigFactory;
@@ -9146,16 +8922,6 @@ const collectionDeleteNode = (0, __codemation_core.defineNode)({
9146
8922
  function resolveSubjectField(field, item) {
9147
8923
  return typeof field === "function" ? field({ item }) : field;
9148
8924
  }
9149
- /**
9150
- * Auto-detecting inbox approval node.
9151
- *
9152
- * Uses `ctx.resolve(InboxChannelResolverToken)` to pick the right inbox channel
9153
- * at runtime:
9154
- * - In managed mode (PairingConfig present): routes to the control-plane inbox.
9155
- * - Otherwise: routes to the local inbox.
9156
- *
9157
- * Authors use this node directly; no extra wiring needed per deployment mode.
9158
- */
9159
8925
  const inboxApproval = (0, __codemation_core.defineHumanApprovalNode)({
9160
8926
  key: "inbox.approval",
9161
8927
  title: "Inbox Approval",
@@ -9548,5 +9314,6 @@ exports.inboxApproval = inboxApproval;
9548
9314
  exports.oauth2ClientCredentialsType = oauth2ClientCredentialsType;
9549
9315
  exports.openAiChatModelPresets = openAiChatModelPresets;
9550
9316
  exports.registerCoreNodes = registerCoreNodes;
9317
+ exports.schedulePollingTrigger = schedulePollingTrigger;
9551
9318
  exports.workflow = workflow;
9552
9319
  //# sourceMappingURL=index.cjs.map