@codemation/core-nodes-ocr 0.2.7 → 0.2.8

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.
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require('./chunk-BaqVhFee.cjs');
2
- const require_analyzeInvoiceNode = require('./analyzeInvoiceNode-CmMsifbw.cjs');
2
+ const require_analyzeInvoiceNode = require('./analyzeInvoiceNode-CW_SXNQf.cjs');
3
3
  let __codemation_core = require("@codemation/core");
4
4
  __codemation_core = require_chunk.__toESM(__codemation_core);
5
5
  let node_dns_promises = require("node:dns/promises");
@@ -14,9 +14,6 @@ let zod = require("zod");
14
14
  zod = require_chunk.__toESM(zod);
15
15
 
16
16
  //#region ../core-nodes/src/credentials/ApiKeyCredentialType.ts
17
- /**
18
- * API key credential that injects a key either as an HTTP header or a query parameter.
19
- */
20
17
  const apiKeyCredentialType = (0, __codemation_core.defineCredential)({
21
18
  key: "core-nodes.api-key",
22
19
  label: "API Key",
@@ -64,10 +61,6 @@ const apiKeyCredentialType = (0, __codemation_core.defineCredential)({
64
61
 
65
62
  //#endregion
66
63
  //#region ../core-nodes/src/credentials/BasicAuthCredentialType.ts
67
- /**
68
- * HTTP Basic authentication credential.
69
- * Session sets `Authorization: Basic <base64(username:password)>`.
70
- */
71
64
  const basicAuthCredentialType = (0, __codemation_core.defineCredential)({
72
65
  key: "core-nodes.basic-auth",
73
66
  label: "Basic Auth",
@@ -105,10 +98,6 @@ const basicAuthCredentialType = (0, __codemation_core.defineCredential)({
105
98
 
106
99
  //#endregion
107
100
  //#region ../core-nodes/src/credentials/BearerTokenCredentialType.ts
108
- /**
109
- * Simple Bearer token credential.
110
- * Session sets `Authorization: Bearer <token>` on every request.
111
- */
112
101
  const bearerTokenCredentialType = (0, __codemation_core.defineCredential)({
113
102
  key: "core-nodes.bearer-token",
114
103
  label: "Bearer Token",
@@ -167,24 +156,6 @@ var OAuth2TokenExchangeFactory = class {
167
156
 
168
157
  //#endregion
169
158
  //#region ../core-nodes/src/credentials/OAuth2ClientCredentialsTypeFactory.ts
170
- /**
171
- * OAuth2 client-credentials flow credential.
172
- *
173
- * This is a machine-to-machine flow: no user redirect occurs. The session
174
- * POSTs to the configured `tokenUrl` with `client_credentials` grant, caches
175
- * the resulting access token for the duration of the session, and injects it
176
- * as `Authorization: Bearer <token>` on each request.
177
- *
178
- * Token caching is per-session only (one createSession call = one token fetch
179
- * at most). Cross-session caching would require host-level state and is out of
180
- * scope here. Because the engine creates a fresh session per execution, a new
181
- * token is fetched once per node activation.
182
- *
183
- * NOTE: `auth` is intentionally omitted from the definition. The OAuth2
184
- * `auth: { kind: "oauth2" }` shape signals an authorization-code / user-redirect
185
- * flow; using it here would cause the host UI to render an OAuth consent button
186
- * that goes nowhere. Client-credentials is a purely server-side flow.
187
- */
188
159
  const oauth2ClientCredentialsType = (0, __codemation_core.defineCredential)({
189
160
  key: "core-nodes.oauth2-client-credentials",
190
161
  label: "OAuth2 Client Credentials",
@@ -268,10 +239,6 @@ const oauth2ClientCredentialsType = (0, __codemation_core.defineCredential)({
268
239
 
269
240
  //#endregion
270
241
  //#region ../core-nodes/src/http/SSRFBlockedError.ts
271
- /**
272
- * Thrown when an HTTP request target resolves to a private, link-local, or
273
- * loopback address and `allowPrivateNetworkTargets` is not set.
274
- */
275
242
  var SSRFBlockedError = class extends Error {
276
243
  resolvedIp;
277
244
  constructor(host, resolvedIp) {
@@ -283,26 +250,7 @@ var SSRFBlockedError = class extends Error {
283
250
 
284
251
  //#endregion
285
252
  //#region ../core-nodes/src/http/SsrfGuard.ts
286
- /** Emitted once per process when NODE_ENV=production and no allowedOutboundHosts is set. */
287
253
  let _productionNoAllowlistWarned = false;
288
- /**
289
- * Guards HTTP requests against Server-Side Request Forgery (SSRF) by
290
- * DNS-resolving the target host and rejecting private/link-local/loopback
291
- * addresses.
292
- *
293
- * Blocked ranges:
294
- * - RFC-1918: 10/8, 172.16/12, 192.168/16
295
- * - Link-local: 169.254/16
296
- * - Loopback: 127/8, ::1
297
- *
298
- * When `allowedOutboundHosts` is set, every resolved DNS target must match
299
- * at least one entry in the list (exact hostname or `*.example.com` wildcard).
300
- * When unset, existing behaviour applies: private ranges blocked, public allowed.
301
- *
302
- * Call {@link check} before making any outbound HTTP request.
303
- * Pass `allowPrivate: true` to bypass the private-network guard for trusted workflows
304
- * (allowedOutboundHosts allowlist is still applied when set).
305
- */
306
254
  var SsrfGuard = class {
307
255
  constructor(allowedOutboundHosts) {
308
256
  this.allowedOutboundHosts = allowedOutboundHosts;
@@ -311,15 +259,6 @@ var SsrfGuard = class {
311
259
  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.");
312
260
  }
313
261
  }
314
- /**
315
- * Resolves the host of `url` via DNS and throws {@link SSRFBlockedError}
316
- * if any resolved address falls in a blocked range, or if the host does not
317
- * match the operator-configured allowlist (when set).
318
- *
319
- * @param url - Fully-qualified URL of the intended request target.
320
- * @param allowPrivate - When `true`, the private-network check is skipped.
321
- * The allowedOutboundHosts check is still applied when set.
322
- */
323
262
  async check(url, allowPrivate) {
324
263
  if (allowPrivate && !this.allowedOutboundHosts?.length) return;
325
264
  let host;
@@ -343,10 +282,6 @@ var SsrfGuard = class {
343
282
  }
344
283
  for (const { address } of addresses) if (this.isPrivateAddress(address)) throw new SSRFBlockedError(host, address);
345
284
  }
346
- /**
347
- * Returns true when `host` matches at least one entry in `allowedOutboundHosts`.
348
- * Supports exact hostnames (`api.example.com`) and wildcard prefixes (`*.example.com`).
349
- */
350
285
  isHostAllowed(host) {
351
286
  for (const allowed of this.allowedOutboundHosts ?? []) if (allowed.startsWith("*.")) {
352
287
  const suffix = allowed.slice(1);
@@ -380,21 +315,6 @@ var SsrfGuard = class {
380
315
 
381
316
  //#endregion
382
317
  //#region ../core-nodes/src/http/HttpRequestExecutor.ts
383
- /**
384
- * Executes a single HTTP request described by {@link HttpRequestSpec}.
385
- *
386
- * - Credential sessions provide header/query deltas via `applyToRequest`.
387
- * - Body encoding is delegated to {@link HttpBodyBuilder}.
388
- * - URL query merging is delegated to {@link HttpUrlBuilder}.
389
- * - SSRF protection is delegated to {@link SsrfGuard} (injected).
390
- * - Binary response bodies: when `download.mode` triggers binary attach, the
391
- * `bodyBinaryName` field is set in the result but the body is NOT read here.
392
- * Callers that need binary attachment should use `buildRequest` to get the
393
- * resolved URL + init and make the fetch + binary attach themselves.
394
- *
395
- * Collaborators (`fetch`, body builder, url builder, ssrfGuard) are injected so
396
- * callers own construction at composition roots and tests can supply deterministic stubs.
397
- */
398
318
  var HttpRequestExecutor = class {
399
319
  constructor(fetchFn, bodyBuilder, urlBuilder, ssrfGuard) {
400
320
  this.fetchFn = fetchFn;
@@ -402,14 +322,6 @@ var HttpRequestExecutor = class {
402
322
  this.urlBuilder = urlBuilder;
403
323
  this.ssrfGuard = ssrfGuard;
404
324
  }
405
- /**
406
- * Builds the fetch init (headers, query, body) from the spec + credential delta,
407
- * returning both the resolved URL and the RequestInit so callers can make the
408
- * actual fetch call themselves (useful for streaming / binary attach).
409
- *
410
- * Also performs SSRF protection via the injected {@link SsrfGuard} before
411
- * returning — throws {@link SSRFBlockedError} if the target is a private address.
412
- */
413
325
  async buildRequest(spec, item) {
414
326
  await this.ssrfGuard.check(spec.url, spec.allowPrivateNetworkTargets ?? false);
415
327
  const credentialDelta = spec.credential?.applyToRequest(spec) ?? {};
@@ -433,12 +345,6 @@ var HttpRequestExecutor = class {
433
345
  }
434
346
  };
435
347
  }
436
- /**
437
- * Executes an HTTP request and returns parsed result.
438
- * For binary downloads (when `shouldAttachBody` is true), the body is NOT consumed
439
- * and callers must call `ctx.binary.attach` directly using the resolved URL + init
440
- * (available via `buildRequest`).
441
- */
442
348
  async execute(spec, item) {
443
349
  const { url: resolvedUrl, init } = await this.buildRequest(spec, item);
444
350
  const response = await this.fetchFn(resolvedUrl, init);
@@ -495,10 +401,6 @@ var HttpRequestExecutor = class {
495
401
 
496
402
  //#endregion
497
403
  //#region ../core-nodes/src/http/HttpBodyBuilder.ts
498
- /**
499
- * Builds a fetch-compatible `BodyInit` + Content-Type pair from an {@link HttpBodySpec}.
500
- * Multipart binaries are read from `item.binary` via `ctx.binary.openReadStream`.
501
- */
502
404
  var HttpBodyBuilder = class {
503
405
  async build(spec, item, ctx) {
504
406
  if (!spec || spec.kind === "none") return;
@@ -569,10 +471,6 @@ var HttpBodyBuilder = class {
569
471
 
570
472
  //#endregion
571
473
  //#region ../core-nodes/src/http/HttpUrlBuilder.ts
572
- /**
573
- * Merges query parameters into a base URL.
574
- * Handles both scalar and array values, and preserves any existing params.
575
- */
576
474
  var HttpUrlBuilder = class {
577
475
  build(baseUrl, query) {
578
476
  if (!query || Object.keys(query).length === 0) return baseUrl;
@@ -616,11 +514,6 @@ OpenAIChatModelFactory = __decorate([(0, __codemation_core.chatModel)({ packageN
616
514
 
617
515
  //#endregion
618
516
  //#region ../core-nodes/src/nodes/ConnectionCredentialExecutionContextFactory.ts
619
- /**
620
- * Builds a {@link NodeExecutionContext} whose identity for credential binding and `getCredential`
621
- * is a **connection-owned** workflow node id (`ConnectionNodeIdFactory` in `@codemation/core`),
622
- * not the executing parent node. Use for LLM slots, tool slots, or any connection-scoped owner.
623
- */
624
517
  var ConnectionCredentialExecutionContextFactory = class {
625
518
  credentialResolverFactory;
626
519
  constructor(credentialSessions) {
@@ -643,15 +536,6 @@ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
643
536
  createConnectionCredentialExecutionContextFactory(credentialSessions) {
644
537
  return new ConnectionCredentialExecutionContextFactory(credentialSessions);
645
538
  }
646
- /**
647
- * Produces a plain JSON Schema object (`draft-07`) from a Zod schema, as needed by
648
- * OpenAI tool-parameter schemas and the structured-output repair prompt.
649
- * - Prefers the schema's **instance** `toJSONSchema(...)` method so we stay inside the Zod
650
- * instance that created the schema (works across consumer/framework tsx namespaces — see
651
- * {@link ZodInstanceToJsonSchema}). Falls back to the framework-imported module function.
652
- * - Strips root `$schema` (OpenAI ignores it).
653
- * - Sanitizes `required` for cfworker json-schema compatibility (must be a string array or absent).
654
- */
655
539
  createJsonSchemaRecord(inputSchema, options) {
656
540
  const { $schema: _draftSchemaOmitted,...rest } = this.convertZodSchemaToJsonSchema(inputSchema, { target: "draft-07" });
657
541
  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)}).`);
@@ -660,20 +544,11 @@ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
660
544
  this.sanitizeJsonSchemaRequiredKeywordsForCfworker(rest);
661
545
  return rest;
662
546
  }
663
- /**
664
- * Runs Zod's `toJSONSchema` via the schema's own instance method when available, so consumer
665
- * schemas loaded under a different tsx namespace still convert correctly. If the caller handed us
666
- * a payload that lacks that method (e.g. a plain JSON Schema record or a Zod instance whose
667
- * prototype was stripped), we fall back to the framework-bundled module function.
668
- */
669
547
  convertZodSchemaToJsonSchema(inputSchema, params) {
670
548
  const candidate = inputSchema.toJSONSchema;
671
549
  if (typeof candidate === "function") return candidate.call(inputSchema, params);
672
550
  return (0, zod_v4_core.toJSONSchema)(inputSchema, params);
673
551
  }
674
- /**
675
- * `@cfworker/json-schema` iterates `schema.required` with `for...of`; it must be a string array or absent.
676
- */
677
552
  sanitizeJsonSchemaRequiredKeywordsForCfworker(node$20) {
678
553
  if (!node$20 || typeof node$20 !== "object" || Array.isArray(node$20)) return;
679
554
  const o = node$20;
@@ -820,11 +695,6 @@ var OpenAIChatModelConfig = class {
820
695
 
821
696
  //#endregion
822
697
  //#region ../core-nodes/src/chatModels/OpenAiChatModelPresetsFactory.ts
823
- /**
824
- * Default OpenAI chat model configs for scaffolds and demos (icon + label match {@link OpenAIChatModelConfig} defaults).
825
- * Prefer importing {@link openAiChatModelPresets} from here or from the consumer template re-export
826
- * instead of repeating {@link OpenAIChatModelConfig} construction in app workflows.
827
- */
828
698
  var OpenAiChatModelPresets = class {
829
699
  demoGpt4oMini = new OpenAIChatModelConfig("OpenAI", "gpt-4o-mini");
830
700
  demoGpt41 = new OpenAIChatModelConfig("OpenAI", "gpt-4.1");
@@ -833,19 +703,6 @@ const openAiChatModelPresets = new OpenAiChatModelPresets();
833
703
 
834
704
  //#endregion
835
705
  //#region ../core-nodes/src/chatModels/ManagedHmacSignerFactory.types.ts
836
- /**
837
- * Creates an HMAC-signing fetch wrapper that authenticates requests to
838
- * Codemation managed services (LLM broker, doc-scanner) with the
839
- * Codemation-Hmac v=1 scheme.
840
- *
841
- * Mirrors HmacRequestSigner from @codemation/host/pairing without importing
842
- * that package (which would create a circular dependency since @codemation/host
843
- * depends on @codemation/core-nodes).
844
- *
845
- * @param workspaceId - Workspace identifier injected by the CP provisioner.
846
- * @param pairingSecret - Base64-encoded 32-byte HMAC key injected by the provisioner.
847
- * @param options - Optional behaviour flags and test seams.
848
- */
849
706
  function managedHmacFetchFactory(workspaceId, pairingSecret, options) {
850
707
  const signBody = options?.signBody ?? true;
851
708
  return async (input, init) => {
@@ -864,10 +721,6 @@ function managedHmacFetchFactory(workspaceId, pairingSecret, options) {
864
721
  });
865
722
  };
866
723
  }
867
- /**
868
- * Produces a Codemation-Hmac v=1 Authorization header value.
869
- * Algorithm must match HmacVerifier.computeSignature() in the control-plane.
870
- */
871
724
  function buildHmacAuthHeader(workspaceId, pairingSecret, method, url, body, overrides) {
872
725
  const ts = overrides?.now ? overrides.now() : Math.floor(Date.now() / 1e3);
873
726
  const nonce = overrides?.nonce ? overrides.nonce() : (0, node_crypto.randomBytes)(16).toString("base64");
@@ -909,21 +762,137 @@ let CodemationChatModelFactory = class CodemationChatModelFactory$1 {
909
762
  };
910
763
  CodemationChatModelFactory = __decorate([(0, __codemation_core.chatModel)({ packageName: "@codemation/core-nodes" })], CodemationChatModelFactory);
911
764
 
765
+ //#endregion
766
+ //#region ../core-nodes/src/nodes/AgentToolResultContentFactory.ts
767
+ const MAX_INLINE_BYTES = 8 * 1024 * 1024;
768
+ const KNOWN_MCP_BLOCK_TYPES = new Set([
769
+ "text",
770
+ "image",
771
+ "audio",
772
+ "resource",
773
+ "resource_link"
774
+ ]);
775
+ var AgentToolResultContentFactory = class AgentToolResultContentFactory {
776
+ static tryMapToContentOutput(result) {
777
+ const blocks = AgentToolResultContentFactory.contentBlocks(result);
778
+ if (blocks === void 0) return void 0;
779
+ const parts = [];
780
+ let inlinedBytes = 0;
781
+ for (const block of blocks) {
782
+ const mapped = AgentToolResultContentFactory.mapBlock(block, inlinedBytes);
783
+ parts.push(mapped.part);
784
+ inlinedBytes += mapped.bytes;
785
+ }
786
+ return parts;
787
+ }
788
+ static contentBlocks(result) {
789
+ if (result === null || typeof result !== "object") return void 0;
790
+ const content = result.content;
791
+ if (!Array.isArray(content) || content.length === 0) return void 0;
792
+ if (!content.every((block) => block !== null && typeof block === "object" && typeof block.type === "string")) return void 0;
793
+ return content.some((block) => KNOWN_MCP_BLOCK_TYPES.has(block.type)) ? content : void 0;
794
+ }
795
+ static mapBlock(block, inlinedBytesSoFar) {
796
+ const type = block.type;
797
+ if (type === "text" && typeof block.text === "string") return {
798
+ part: {
799
+ type: "text",
800
+ text: block.text
801
+ },
802
+ bytes: 0
803
+ };
804
+ if (type === "image" && typeof block.data === "string" && typeof block.mimeType === "string") return AgentToolResultContentFactory.mapBinary({
805
+ base64: block.data,
806
+ mediaType: block.mimeType,
807
+ inlinedBytesSoFar
808
+ });
809
+ if (type === "resource" && block.resource) return AgentToolResultContentFactory.mapEmbeddedResource(block.resource, inlinedBytesSoFar);
810
+ if (type === "resource_link" && typeof block.uri === "string") {
811
+ const mime = typeof block.mimeType === "string" ? ` (${block.mimeType})` : "";
812
+ return {
813
+ part: {
814
+ type: "text",
815
+ text: `[linked resource: ${block.uri}${mime}]`
816
+ },
817
+ bytes: 0
818
+ };
819
+ }
820
+ return {
821
+ part: {
822
+ type: "text",
823
+ text: `[unsupported tool content block: ${String(type)}]`
824
+ },
825
+ bytes: 0
826
+ };
827
+ }
828
+ static mapEmbeddedResource(resource, inlinedBytesSoFar) {
829
+ if (typeof resource.text === "string") return {
830
+ part: {
831
+ type: "text",
832
+ text: resource.text
833
+ },
834
+ bytes: 0
835
+ };
836
+ if (typeof resource.blob === "string" && typeof resource.mimeType === "string") return AgentToolResultContentFactory.mapBinary({
837
+ base64: resource.blob,
838
+ mediaType: resource.mimeType,
839
+ filename: typeof resource.name === "string" ? resource.name : void 0,
840
+ inlinedBytesSoFar
841
+ });
842
+ return {
843
+ part: {
844
+ type: "text",
845
+ text: `[embedded resource: ${typeof resource.uri === "string" ? resource.uri : "unknown"}]`
846
+ },
847
+ bytes: 0
848
+ };
849
+ }
850
+ static mapBinary(args) {
851
+ const rawBytes = Math.floor(args.base64.length * 3 / 4);
852
+ if (args.inlinedBytesSoFar + rawBytes > MAX_INLINE_BYTES) {
853
+ const name = args.filename ? ` "${args.filename}"` : "";
854
+ const kb = Math.round(rawBytes / 1024);
855
+ return {
856
+ part: {
857
+ type: "text",
858
+ text: `[binary${name} (${args.mediaType}, ~${kb} KB) omitted: exceeds the per-tool-result inline limit]`
859
+ },
860
+ bytes: 0
861
+ };
862
+ }
863
+ if (args.mediaType.startsWith("image/")) return {
864
+ part: {
865
+ type: "image-data",
866
+ data: args.base64,
867
+ mediaType: args.mediaType
868
+ },
869
+ bytes: rawBytes
870
+ };
871
+ if (args.mediaType === "application/pdf") return {
872
+ part: {
873
+ type: "file-data",
874
+ data: args.base64,
875
+ mediaType: args.mediaType,
876
+ ...args.filename ? { filename: args.filename } : {}
877
+ },
878
+ bytes: rawBytes
879
+ };
880
+ return {
881
+ part: {
882
+ type: "text",
883
+ text: `[binary${args.filename ? ` "${args.filename}"` : ""} (${args.mediaType}) not inlined: unsupported by the model]`
884
+ },
885
+ bytes: 0
886
+ };
887
+ }
888
+ };
889
+
912
890
  //#endregion
913
891
  //#region ../core-nodes/src/nodes/AgentMessageFactory.ts
914
- /**
915
- * AI-SDK-shaped message construction for the AIAgent stack. Emits plain `ModelMessage[]`
916
- * ( `{ role: 'system' | 'user' | 'assistant' | 'tool', content: ... }` ) as consumed by
917
- * `generateText({ messages })` from the `ai` package.
918
- */
919
892
  var AgentMessageFactory = class AgentMessageFactory {
920
893
  static createPromptMessages(messages) {
921
894
  return messages.map((message) => this.createPromptMessage(message));
922
895
  }
923
- /**
924
- * Builds the assistant message that contains optional text plus one or more tool-call parts,
925
- * matching the shape AI SDK emits between steps.
926
- */
927
896
  static createAssistantWithToolCalls(text, toolCalls) {
928
897
  const content = [];
929
898
  if (text && text.length > 0) content.push({
@@ -941,24 +910,30 @@ var AgentMessageFactory = class AgentMessageFactory {
941
910
  content
942
911
  };
943
912
  }
944
- /**
945
- * Builds the `{ role: "tool", content: [{ type: "tool-result", ... }, ...] }` message returned
946
- * to the model after each tool round.
947
- */
948
- static createToolResultsMessage(executedToolCalls) {
913
+ static createToolResultsMessage(executedToolCalls, passToolBinariesToModel = true) {
949
914
  return {
950
915
  role: "tool",
951
916
  content: executedToolCalls.map((executed) => ({
952
917
  type: "tool-result",
953
918
  toolCallId: executed.toolCallId,
954
919
  toolName: executed.toolName,
955
- output: {
956
- type: "json",
957
- value: AgentMessageFactory.toToolResultJson(executed.result)
958
- }
920
+ output: AgentMessageFactory.toToolResultOutput(executed.result, passToolBinariesToModel)
959
921
  }))
960
922
  };
961
923
  }
924
+ static toToolResultOutput(result, passToolBinariesToModel) {
925
+ if (passToolBinariesToModel) {
926
+ const content = AgentToolResultContentFactory.tryMapToContentOutput(result);
927
+ if (content !== void 0) return {
928
+ type: "content",
929
+ value: content
930
+ };
931
+ }
932
+ return {
933
+ type: "json",
934
+ value: AgentMessageFactory.toToolResultJson(result)
935
+ };
936
+ }
962
937
  static toToolResultJson(value) {
963
938
  if (value === void 0) return null;
964
939
  try {
@@ -1101,10 +1076,6 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
1101
1076
  }
1102
1077
  throw new Error(`Structured output required for AIAgent "${args.agentName}" (${args.nodeId}) but validation still failed after ${_AgentStructuredOutputRunner.repairAttemptCount} repair attempts: ${failure.validationError}`);
1103
1078
  }
1104
- /**
1105
- * Chooses strict mode for OpenAI chat-model configs, off otherwise. Extendable in future for
1106
- * other providers that adopt the same "supply a JSON Schema record directly" contract.
1107
- */
1108
1079
  resolveStructuredOutputOptions(chatModelConfig) {
1109
1080
  if (chatModelConfig.type !== OpenAIChatModelFactory) return;
1110
1081
  return {
@@ -1587,10 +1558,6 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
1587
1558
  extractErrorDetails(error) {
1588
1559
  return error.details;
1589
1560
  }
1590
- /**
1591
- * Extracts the text content from the last assistant message in the conversation snapshot.
1592
- * Used to populate `agentReasoning` in the HITL suspension metadata.
1593
- */
1594
1561
  extractLastAssistantText(conversation) {
1595
1562
  for (let i = conversation.length - 1; i >= 0; i--) {
1596
1563
  const msg = conversation[i];
@@ -1625,16 +1592,6 @@ AgentToolExecutionCoordinator = __decorate([
1625
1592
 
1626
1593
  //#endregion
1627
1594
  //#region ../core-nodes/src/nodes/AgentBinaryContentFactory.ts
1628
- /**
1629
- * Turns resolved file binaries into native AI SDK multimodal content parts and merges them into the
1630
- * agent prompt. Images (`image/*`) become {@link ImagePart}s; every other type (PDFs, office docs,
1631
- * CSV, JSON, …) becomes a {@link FilePart}. The provider maps these to its wire-level `image` /
1632
- * `document` blocks; an unsupported file type surfaces as a provider error at runtime.
1633
- *
1634
- * Parts are appended to the LAST user message so the binary travels alongside the author's prompt
1635
- * text (preserving any untrusted-source preamble that already wrapped that text). When no user
1636
- * message exists, a new user message carrying only the binaries is appended.
1637
- */
1638
1595
  var AgentBinaryContentFactory = class AgentBinaryContentFactory {
1639
1596
  static toContentPart(binary) {
1640
1597
  if (binary.mediaType.startsWith("image/")) return {
@@ -1713,20 +1670,6 @@ let NodeBackedToolRuntime = class NodeBackedToolRuntime$1 {
1713
1670
  outputs
1714
1671
  });
1715
1672
  }
1716
- /**
1717
- * Returns a re-rooted child ctx for nested-agent tools (so their LLM/tool connection ids derive
1718
- * from the tool connection node, telemetry parents under the tool-call span, and connection
1719
- * invocations carry `parentInvocationId`). Plain runnable tools (non-agent) keep the orchestrator
1720
- * ctx with only `config` swapped — no nesting concern.
1721
- *
1722
- * The caller (`AIAgentNode.createItemScopedTools`) already wraps the orchestrator ctx via
1723
- * `ConnectionCredentialExecutionContextFactory.forConnectionNode`, so `args.ctx.nodeId` is the
1724
- * tool's own connection node id (e.g. `AIAgentNode:2__conn__tool__searchInMail`). We pass that
1725
- * through as the sub-agent's `nodeId`; deriving another `toolConnectionNodeId(args.ctx.nodeId,
1726
- * config.name)` here would prepend a duplicate `__conn__tool__<name>` segment and exponentially
1727
- * deepen ids on each invocation, which also breaks credential resolution because user-provided
1728
- * bindings sit on the single-level connection node id.
1729
- */
1730
1673
  resolveNodeCtx(config, args) {
1731
1674
  const isNestedAgent = __codemation_core.AgentConfigInspector.isAgentNodeConfig(config.node);
1732
1675
  const hooks = args.hooks;
@@ -1791,22 +1734,12 @@ NodeBackedToolRuntime = __decorate([
1791
1734
 
1792
1735
  //#endregion
1793
1736
  //#region ../core-nodes/src/nodes/BM25Index.ts
1794
- /**
1795
- * Minimal BM25 (Okapi BM25) implementation for indexing MCP tool descriptions.
1796
- *
1797
- * Parameters: k1=1.5, b=0.75 (standard defaults).
1798
- * Tokenisation: lowercase, split on non-alphanumerics, filter empties.
1799
- */
1800
1737
  var BM25Index = class {
1801
1738
  k1 = 1.5;
1802
1739
  b = .75;
1803
1740
  tf = [];
1804
1741
  df = /* @__PURE__ */ new Map();
1805
1742
  avgDocLen = 0;
1806
- /**
1807
- * Add all documents at once. After calling this, search is available.
1808
- * Documents are indexed in insertion order; search returns their indices.
1809
- */
1810
1743
  add(docs) {
1811
1744
  const docTerms = docs.map((d) => this.tokenize(d));
1812
1745
  let totalLen = 0;
@@ -1819,10 +1752,6 @@ var BM25Index = class {
1819
1752
  }
1820
1753
  this.avgDocLen = docTerms.length > 0 ? totalLen / docTerms.length : 0;
1821
1754
  }
1822
- /**
1823
- * Returns up to `limit` document indices ranked by BM25 score (highest first).
1824
- * Returns an empty array if the index is empty or the query matches nothing.
1825
- */
1826
1755
  search(query, limit) {
1827
1756
  const n = this.tf.length;
1828
1757
  if (n === 0) return [];
@@ -1863,26 +1792,12 @@ const PINNED_TOOLS_SOFT_LIMIT = 8;
1863
1792
  const PINNED_TOOLS_HARD_LIMIT = 16;
1864
1793
  const FIND_TOOLS_NAME = "find_tools";
1865
1794
  const FIND_TOOLS_DEFAULT_LIMIT = 5;
1866
- /**
1867
- * Default tool-loading strategy: BM25-indexed MCP tool deferral via a `find_tools` meta-tool.
1868
- *
1869
- * - Node-backed tools and pinned MCP tools are always included in every turn.
1870
- * - `find_tools(query, limit?)` is added to the tool set when MCP tools are indexed.
1871
- * - Tools surfaced by `find_tools` are included in subsequent turns.
1872
- *
1873
- * Not DI-managed; instantiated per agent execution by DeferredMetaToolStrategyFactory.
1874
- */
1875
1795
  var DeferredMetaToolStrategy = class {
1876
1796
  nodeBackedTools = {};
1877
1797
  pinnedTools = {};
1878
1798
  mcpEntries = [];
1879
1799
  toolsByServerId = /* @__PURE__ */ new Map();
1880
1800
  foundToolIds = /* @__PURE__ */ new Set();
1881
- /**
1882
- * `jsonSchema` from the `ai` SDK, loaded lazily in {@link initialize} so the SDK
1883
- * (~28MB RSS) stays off the boot path. `initialize` always runs before the sync
1884
- * `getToolsForTurn` → `buildFindToolsDefinition` path, so this is set before use.
1885
- */
1886
1801
  jsonSchema;
1887
1802
  constructor(bm25, warnFn) {
1888
1803
  this.bm25 = bm25;
@@ -2030,11 +1945,6 @@ let AIAgentNode = class AIAgentNode$1 {
2030
1945
  inputSchema = zod.z.unknown();
2031
1946
  connectionCredentialExecutionContextFactory;
2032
1947
  preparedByExecutionContext = /* @__PURE__ */ new WeakMap();
2033
- /**
2034
- * The `ai` SDK, loaded lazily in {@link execute} so the SDK (~28MB RSS) stays
2035
- * off the boot path — non-AI workflows never load it. Every path runs through
2036
- * `execute` → `ensureAiSdk` before any sync helper touches `this.aiSdk`.
2037
- */
2038
1948
  aiSdk;
2039
1949
  aiSdkPromise = null;
2040
1950
  constructor(nodeResolver, credentialSessions, nodeBackedToolRuntime, executionHelpers, structuredOutputRunner, toolExecutionCoordinator, toolLoadingStrategyFactory, agentMcpIntegration) {
@@ -2058,15 +1968,9 @@ let AIAgentNode = class AIAgentNode$1 {
2058
1968
  };
2059
1969
  return (await this.runAgentForItem(prepared, itemWithMappedJson, args.itemIndex, args.items)).json;
2060
1970
  }
2061
- /** Load the `ai` SDK once per node instance (cached promise guards concurrent items). */
2062
1971
  async ensureAiSdk() {
2063
1972
  this.aiSdk = await (this.aiSdkPromise ??= Promise.resolve().then(() => require("./dist-CUcwHflE.cjs")));
2064
1973
  }
2065
- /**
2066
- * Resume path: re-enters the agent loop after a HITL suspension.
2067
- * Reconstructs the conversation from the checkpoint, injects the human decision
2068
- * as a tool_result, and continues the loop from where it suspended.
2069
- */
2070
1974
  async executeResumed(args, resumeContext) {
2071
1975
  const { ctx } = args;
2072
1976
  const taskMetadata = resumeContext.task.metadata ?? {};
@@ -2093,7 +1997,7 @@ let AIAgentNode = class AIAgentNode$1 {
2093
1997
  result: decision,
2094
1998
  serialized: JSON.stringify(decision)
2095
1999
  };
2096
- const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry])];
2000
+ const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry], ctx.config.passToolBinariesToModel !== false)];
2097
2001
  const loopResult = await this.runTurnLoopUntilFinalAnswer({
2098
2002
  prepared,
2099
2003
  itemInputsByPort,
@@ -2113,10 +2017,6 @@ let AIAgentNode = class AIAgentNode$1 {
2113
2017
  const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
2114
2018
  return this.buildOutputItem(item, outputJson).json;
2115
2019
  }
2116
- /**
2117
- * Normalizes a {@link ResumeContext} decision into a flat JSON-serializable shape
2118
- * suitable for injection as a tool_result content.
2119
- */
2120
2020
  normalizeDecision(resumeContext) {
2121
2021
  const { decision } = resumeContext;
2122
2022
  if (decision.kind === "decided") {
@@ -2243,16 +2143,6 @@ let AIAgentNode = class AIAgentNode$1 {
2243
2143
  const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
2244
2144
  return this.buildOutputItem(item, outputJson);
2245
2145
  }
2246
- /**
2247
- * Multi-turn loop:
2248
- * - Each turn is a single `generateText` call with tools exposed but **not auto-executed**
2249
- * (we control tool dispatch so that {@link AgentToolExecutionCoordinator} drives repair /
2250
- * connection-invocation recording / transient-error handling exactly like before).
2251
- * - When the model returns no tool calls the loop ends with the model's text as the final answer.
2252
- * - Respects `guardrails.maxTurns` and `guardrails.onTurnLimitReached`.
2253
- * - Strategy-owned tool calls (e.g. `find_tools`) are dispatched via the strategy, not the
2254
- * coordinator; their results are tracked so subsequent turns receive the discovered tools.
2255
- */
2256
2146
  async runTurnLoopUntilFinalAnswer(args) {
2257
2147
  const { prepared, itemInputsByPort, itemScopedTools, conversation } = args;
2258
2148
  const { ctx, guardrails, toolLoadingStrategy } = prepared;
@@ -2260,7 +2150,6 @@ let AIAgentNode = class AIAgentNode$1 {
2260
2150
  let toolCallCount = args.resumedToolCallCount ?? 0;
2261
2151
  let turnCount = 0;
2262
2152
  const repairAttemptsByToolName = /* @__PURE__ */ new Map();
2263
- /** Tool IDs surfaced by find_tools across all prior turns in this item run. */
2264
2153
  let previousFoundToolIds = [];
2265
2154
  for (let turn = 1; turn <= guardrails.maxTurns; turn++) {
2266
2155
  turnCount = turn;
@@ -2313,7 +2202,7 @@ let AIAgentNode = class AIAgentNode$1 {
2313
2202
  coordinatorExecutedCalls.push(...executed);
2314
2203
  }
2315
2204
  const allExecutedCalls = [...strategyExecutedCalls, ...coordinatorExecutedCalls];
2316
- this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, allExecutedCalls);
2205
+ this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, allExecutedCalls, ctx.config.passToolBinariesToModel !== false);
2317
2206
  }
2318
2207
  return {
2319
2208
  finalText,
@@ -2328,8 +2217,8 @@ let AIAgentNode = class AIAgentNode$1 {
2328
2217
  if (guardrails.onTurnLimitReached === "respondWithLastMessage") return;
2329
2218
  throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" reached maxTurns=${guardrails.maxTurns} before producing a final response.`);
2330
2219
  }
2331
- appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls) {
2332
- conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls));
2220
+ appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls, passToolBinariesToModel) {
2221
+ conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls, passToolBinariesToModel));
2333
2222
  }
2334
2223
  async resolveFinalOutputJson(prepared, itemInputsByPort, conversation, finalText, wasToolEnabledRun) {
2335
2224
  if (!prepared.ctx.config.outputSchema) return AgentOutputFactory.fromAgentContent(finalText);
@@ -2390,12 +2279,6 @@ let AIAgentNode = class AIAgentNode$1 {
2390
2279
  };
2391
2280
  });
2392
2281
  }
2393
- /**
2394
- * Resolves the HITL behavior for a tool binding, or `undefined` when it is not a HITL tool.
2395
- * A binding is HITL if either the backing node carries a `defineHumanApprovalNode` marker or the
2396
- * binding sets a per-binding `onRejected` via `asTool(..., { onRejected })`. The per-binding value
2397
- * wins over the node marker, so two tools backed by the same node can reject differently.
2398
- */
2399
2282
  resolveHumanApprovalBehavior(config) {
2400
2283
  if (!this.isNodeBackedToolConfig(config)) return void 0;
2401
2284
  const marker = config.node.humanApprovalToolBehavior;
@@ -2403,11 +2286,6 @@ let AIAgentNode = class AIAgentNode$1 {
2403
2286
  if (marker === void 0 && perBinding === void 0) return void 0;
2404
2287
  return { onRejected: perBinding ?? marker?.onRejected ?? "return" };
2405
2288
  }
2406
- /**
2407
- * Invoke a text turn using the merged tool set from item-scoped tools (coordinator-managed)
2408
- * and strategy tools (find_tools + discovered MCP tools).
2409
- * Strategy tools take precedence for names that overlap.
2410
- */
2411
2289
  async invokeTextTurnWithStrategyTools(prepared, itemInputsByPort, messages, itemScopedTools, strategyTools) {
2412
2290
  const itemToolSet = this.buildToolSet(itemScopedTools);
2413
2291
  const strategyHasTools = Object.keys(strategyTools).length > 0;
@@ -2418,10 +2296,6 @@ let AIAgentNode = class AIAgentNode$1 {
2418
2296
  } : void 0;
2419
2297
  return this.invokeTextTurnWithToolSet(prepared, itemInputsByPort, messages, mergedTools);
2420
2298
  }
2421
- /**
2422
- * Removes `execute` properties from ToolSet entries so the AI SDK does not
2423
- * auto-execute them within `generateText`. Codemation owns all tool dispatch.
2424
- */
2425
2299
  stripExecuteCallbacks(tools) {
2426
2300
  const stripped = {};
2427
2301
  for (const [name, def] of Object.entries(tools)) {
@@ -2430,12 +2304,6 @@ let AIAgentNode = class AIAgentNode$1 {
2430
2304
  }
2431
2305
  return stripped;
2432
2306
  }
2433
- /**
2434
- * Builds a ToolSet from resolved tools for strategy initialization.
2435
- * The strategy uses this for its "always-included" node-backed tool descriptions.
2436
- * HITL tools (detected via the `humanApprovalToolBehavior` field set by `defineHumanApprovalNode`) get the solo-constraint sentence
2437
- * appended to their description.
2438
- */
2439
2307
  buildToolSetFromResolved(resolvedTools) {
2440
2308
  if (resolvedTools.length === 0) return {};
2441
2309
  const toolSet = {};
@@ -2453,20 +2321,6 @@ let AIAgentNode = class AIAgentNode$1 {
2453
2321
  }
2454
2322
  return toolSet;
2455
2323
  }
2456
- /**
2457
- * Builds an AI SDK {@link ToolSet} where every tool ships a pre-converted JSON Schema (via
2458
- * {@link jsonSchema}) — not the raw Zod schema — and carries **no** `execute`. Two reasons:
2459
- *
2460
- * 1. Codemation owns tool dispatch + the per-tool repair loop (see {@link AgentToolExecutionCoordinator}),
2461
- * so the AI SDK must surface tool calls back to us instead of auto-running them.
2462
- * 2. The AI SDK's `asSchema` helper discriminates between Zod v3 / Zod v4 / Standard Schema via
2463
- * runtime feature-detection (`~standard`, `_zod`, etc.). Handing it a pre-built
2464
- * {@link jsonSchema} record — which is tagged with `Symbol.for('vercel.ai.schema')` — skips all
2465
- * of that detection and guarantees the provider receives a draft-07 JSON Schema with
2466
- * `additionalProperties: false` at every object depth (see {@link OpenAiStrictJsonSchemaFactory}
2467
- * for the same logic applied to structured-output schemas). Codemation still runs its own Zod
2468
- * validation on tool inputs before execute — the schema handed to the model is advisory.
2469
- */
2470
2324
  buildToolSet(itemScopedTools) {
2471
2325
  if (itemScopedTools.length === 0) return void 0;
2472
2326
  const toolSet = {};
@@ -2484,10 +2338,6 @@ let AIAgentNode = class AIAgentNode$1 {
2484
2338
  }
2485
2339
  return toolSet;
2486
2340
  }
2487
- /**
2488
- * One `generateText` turn (no auto tool execution) with Codemation-owned child-span telemetry
2489
- * and connection-invocation state recording. Accepts a pre-built ToolSet.
2490
- */
2491
2341
  async invokeTextTurnWithToolSet(prepared, itemInputsByPort, messages, tools) {
2492
2342
  const invocationId = __codemation_core.ConnectionInvocationIdFactory.create();
2493
2343
  const startedAt = /* @__PURE__ */ new Date();
@@ -2598,11 +2448,6 @@ let AIAgentNode = class AIAgentNode$1 {
2598
2448
  });
2599
2449
  }
2600
2450
  }
2601
- /**
2602
- * Structured-output turn: runs `generateText({ output: Output.object({ schema }) })` via the
2603
- * structured-output runner. We keep this as a separate helper because the runner needs the raw
2604
- * validated value (not just text) back, and must be able to retry on Zod failures.
2605
- */
2606
2451
  async invokeStructuredTurn(prepared, itemInputsByPort, schema, messages, structuredOptions) {
2607
2452
  const invocationId = __codemation_core.ConnectionInvocationIdFactory.create();
2608
2453
  const startedAt = /* @__PURE__ */ new Date();
@@ -2727,13 +2572,6 @@ let AIAgentNode = class AIAgentNode$1 {
2727
2572
  providerOptions: overrides?.providerOptions ?? defaults.providerOptions
2728
2573
  };
2729
2574
  }
2730
- /**
2731
- * Build a no-code-friendly output payload for an LLM round.
2732
- *
2733
- * Always includes `content` (matching the canvas snapshot shape used elsewhere) and adds a
2734
- * `toolCalls` array when the round produced tool calls so the execution inspector surfaces the
2735
- * planned calls instead of just an empty `""` for tool-only rounds.
2736
- */
2737
2575
  summarizeTurnOutput(turnResult) {
2738
2576
  if (turnResult.toolCalls.length === 0) return { content: turnResult.text };
2739
2577
  const toolCalls = turnResult.toolCalls.map((toolCall) => ({
@@ -2936,12 +2774,6 @@ let AIAgentNode = class AIAgentNode$1 {
2936
2774
  const binaries = await this.resolveInlineBinaries(attachments, ctx);
2937
2775
  return AgentBinaryContentFactory.withBinaries(promptMessages, binaries);
2938
2776
  }
2939
- /**
2940
- * Picks which attachments feed the passdown. When the author supplies `config.binaries`
2941
- * (a static array or a per-item function — e.g. to forward binaries from an earlier node),
2942
- * those replace the current item's attachments; otherwise the current item's `item.binary`
2943
- * is used.
2944
- */
2945
2777
  selectBinaryAttachments(item, itemIndex, items, ctx) {
2946
2778
  const manual = ctx.config.binaries;
2947
2779
  if (manual !== void 0) return typeof manual === "function" ? manual({
@@ -2952,14 +2784,6 @@ let AIAgentNode = class AIAgentNode$1 {
2952
2784
  }) : manual;
2953
2785
  return item.binary ? Object.values(item.binary) : [];
2954
2786
  }
2955
- /**
2956
- * Reads every attachment through `ctx.binary` (storage-backed, by reference — never base64 on
2957
- * `item.json`) and resolves it to inline base64 so the agent can pass it to the chat model as a
2958
- * native multimodal block. Images become image blocks; every other type (PDF, office docs, CSV,
2959
- * JSON, …) becomes a file block — we don't filter by media type, so any binary can be fed to the
2960
- * model. If the provider rejects an unsupported type the error surfaces at runtime, and the
2961
- * workflow can filter the binary upstream.
2962
- */
2963
2787
  async resolveInlineBinaries(attachments, ctx) {
2964
2788
  const resolved = [];
2965
2789
  for (const attachment of attachments) {
@@ -2972,12 +2796,6 @@ let AIAgentNode = class AIAgentNode$1 {
2972
2796
  }
2973
2797
  return resolved;
2974
2798
  }
2975
- /**
2976
- * When `item.json.__source` matches an entry in `config.untrustedSources`
2977
- * (default: `["gmail", "ocr", "webhook"]`), wraps every user-role message
2978
- * content with an untrusted-external-source preamble so the LLM treats the
2979
- * content as data, not instructions.
2980
- */
2981
2799
  wrapUntrustedSourceMessages(messages, item, config) {
2982
2800
  const source = item.json !== null && typeof item.json === "object" ? item.json.__source : void 0;
2983
2801
  if (typeof source !== "string") return messages;
@@ -3288,17 +3106,12 @@ HttpRequestNode = __decorate([(0, __codemation_core.node)({ packageName: "@codem
3288
3106
 
3289
3107
  //#endregion
3290
3108
  //#region ../core-nodes/src/nodes/httpRequest.ts
3291
- /**
3292
- * The built-in HTTP request credential type IDs accepted by the `HttpRequest` node.
3293
- * These match the four generic credential types shipped with `@codemation/core-nodes`.
3294
- */
3295
3109
  const HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES = [
3296
3110
  bearerTokenCredentialType.definition.typeId,
3297
3111
  apiKeyCredentialType.definition.typeId,
3298
3112
  basicAuthCredentialType.definition.typeId,
3299
3113
  oauth2ClientCredentialsType.definition.typeId
3300
3114
  ];
3301
- /** Default maximum response size for binary mode: 100 MiB. */
3302
3115
  const DEFAULT_RESPONSE_SIZE_CAP_BYTES = 100 * 1024 * 1024;
3303
3116
 
3304
3117
  //#endregion
@@ -3330,9 +3143,6 @@ FilterNode = __decorate([(0, __codemation_core.node)({ packageName: "@codemation
3330
3143
  function getOriginIndex(item) {
3331
3144
  return (0, __codemation_core.getOriginIndexFromItem)(item);
3332
3145
  }
3333
- /**
3334
- * Tags items routed to fan-in merge-by-origin (same contract as {@link IfNode} / {@link SwitchNode}).
3335
- */
3336
3146
  function tagItemForRouterFanIn(args) {
3337
3147
  const { item, itemIndex, nodeId, inputPortLabel = "$in" } = args;
3338
3148
  const metaBase = item.meta && typeof item.meta === "object" ? item.meta : {};
@@ -4215,7 +4025,6 @@ var ManualTrigger = class ManualTrigger {
4215
4025
  defaultItems;
4216
4026
  id;
4217
4027
  description;
4218
- /** Manual runs often emit an empty batch; still schedule downstream by default. */
4219
4028
  continueWhenEmptyOutput = true;
4220
4029
  constructor(name = "Manual trigger", defaultItemsOrId, idOrOptions) {
4221
4030
  this.name = name;
@@ -4433,6 +4242,39 @@ let WebhookTriggerNode = class WebhookTriggerNode$1 {
4433
4242
  };
4434
4243
  WebhookTriggerNode = __decorate([(0, __codemation_core.node)({ packageName: "@codemation/core-nodes" })], WebhookTriggerNode);
4435
4244
 
4245
+ //#endregion
4246
+ //#region ../core-nodes/src/nodes/schedulePollingTrigger.ts
4247
+ const schedulePollingTrigger = (0, __codemation_core.definePollingTrigger)({
4248
+ key: "schedule.interval",
4249
+ packageName: "@codemation/core-nodes",
4250
+ title: "Run on schedule",
4251
+ description: "Emit one tick item on every poll cycle.",
4252
+ icon: "lucide:clock",
4253
+ pollIntervalMs: 6e4,
4254
+ initialState() {
4255
+ return { tick: 0 };
4256
+ },
4257
+ poll({ state }) {
4258
+ const tick = (state ?? { tick: 0 }).tick + 1;
4259
+ return {
4260
+ items: [{ json: {
4261
+ firedAt: (/* @__PURE__ */ new Date()).toISOString(),
4262
+ tick
4263
+ } }],
4264
+ nextState: { tick }
4265
+ };
4266
+ },
4267
+ execute(items) {
4268
+ return { main: items };
4269
+ },
4270
+ testItems() {
4271
+ return [{ json: {
4272
+ firedAt: (/* @__PURE__ */ new Date()).toISOString(),
4273
+ tick: 0
4274
+ } }];
4275
+ }
4276
+ });
4277
+
4436
4278
  //#endregion
4437
4279
  //#region ../core-nodes/src/nodes/ConnectionCredentialNode.ts
4438
4280
  let ConnectionCredentialNode = class ConnectionCredentialNode$1 {
@@ -4620,16 +4462,6 @@ const collectionDeleteNode = (0, __codemation_core.defineNode)({
4620
4462
  function resolveSubjectField(field, item) {
4621
4463
  return typeof field === "function" ? field({ item }) : field;
4622
4464
  }
4623
- /**
4624
- * Auto-detecting inbox approval node.
4625
- *
4626
- * Uses `ctx.resolve(InboxChannelResolverToken)` to pick the right inbox channel
4627
- * at runtime:
4628
- * - In managed mode (PairingConfig present): routes to the control-plane inbox.
4629
- * - Otherwise: routes to the local inbox.
4630
- *
4631
- * Authors use this node directly; no extra wiring needed per deployment mode.
4632
- */
4633
4465
  const inboxApproval = (0, __codemation_core.defineHumanApprovalNode)({
4634
4466
  key: "inbox.approval",
4635
4467
  title: "Inbox Approval",