@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.
- package/CHANGELOG.md +9 -0
- package/dist/{analyzeInvoiceNode-BqZsN8iL.js → analyzeInvoiceNode-BQzww-XC.js} +1 -9
- package/dist/analyzeInvoiceNode-BQzww-XC.js.map +1 -0
- package/dist/{analyzeInvoiceNode-CmMsifbw.cjs → analyzeInvoiceNode-CW_SXNQf.cjs} +1 -9
- package/dist/analyzeInvoiceNode-CW_SXNQf.cjs.map +1 -0
- package/dist/codemation.plugin.cjs +178 -346
- package/dist/codemation.plugin.cjs.map +1 -1
- package/dist/codemation.plugin.d.cts +1 -73
- package/dist/codemation.plugin.d.ts +1 -58
- package/dist/codemation.plugin.js +179 -347
- package/dist/codemation.plugin.js.map +1 -1
- package/dist/{index-RNoZqCPr.d.ts → index-DdjAAXvy.d.ts} +9 -291
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +7 -46
- package/dist/index.d.ts +7 -29
- package/dist/index.js +1 -1
- package/dist/metadata.json +1 -1
- package/dist/{runtimeTypes-B6RO-Yki.d.cts → runtimeTypes-D_9j5Pat.d.cts} +9 -259
- package/package.json +2 -2
- package/src/lib/analyzeWithAzure.ts +0 -9
- package/src/nodes/analyzeDocumentNode.ts +0 -9
- package/src/nodes/analyzeImageNode.ts +0 -9
- package/src/nodes/analyzeInvoiceNode.ts +0 -4
- package/dist/analyzeInvoiceNode-BqZsN8iL.js.map +0 -1
- package/dist/analyzeInvoiceNode-CmMsifbw.cjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as analyzeImageNode, o as azureContentUnderstandingCredentialType, r as analyzeDocumentNode, t as analyzeInvoiceNode } from "./analyzeInvoiceNode-
|
|
2
|
-
import { AgentConfigInspector, AgentGuardrailDefaults, AgentMessageConfigNormalizer, CallableToolConfig, ChildExecutionScopeFactory, CodemationTelemetryAttributeNames, CodemationTelemetryMetricNames, ConnectionInvocationIdFactory, ConnectionNodeIdFactory, CoreTokens, GenAiTelemetryAttributeNames, InboxChannelResolverToken, ItemExprResolver, ItemsInputNormalizer, NodeBackedToolConfig, NodeOutputNormalizer, RunnableOutputBehaviorResolver, SuspensionRequest, chatModel, defineCredential, defineHumanApprovalNode, defineNode, emitPorts, getOriginIndexFromItem, inject, injectable, isPortsEmission, node } from "@codemation/core";
|
|
1
|
+
import { n as analyzeImageNode, o as azureContentUnderstandingCredentialType, r as analyzeDocumentNode, t as analyzeInvoiceNode } from "./analyzeInvoiceNode-BQzww-XC.js";
|
|
2
|
+
import { AgentConfigInspector, AgentGuardrailDefaults, AgentMessageConfigNormalizer, CallableToolConfig, ChildExecutionScopeFactory, CodemationTelemetryAttributeNames, CodemationTelemetryMetricNames, ConnectionInvocationIdFactory, ConnectionNodeIdFactory, CoreTokens, GenAiTelemetryAttributeNames, InboxChannelResolverToken, ItemExprResolver, ItemsInputNormalizer, NodeBackedToolConfig, NodeOutputNormalizer, RunnableOutputBehaviorResolver, SuspensionRequest, chatModel, defineCredential, defineHumanApprovalNode, defineNode, definePollingTrigger, emitPorts, getOriginIndexFromItem, inject, injectable, isPortsEmission, node } from "@codemation/core";
|
|
3
3
|
import dns from "node:dns/promises";
|
|
4
4
|
import { toJSONSchema } from "zod/v4/core";
|
|
5
5
|
import { CredentialResolverFactory } from "@codemation/core/bootstrap";
|
|
@@ -7,9 +7,6 @@ import { createHash, createHmac, randomBytes } from "node:crypto";
|
|
|
7
7
|
import { ZodError, z } from "zod";
|
|
8
8
|
|
|
9
9
|
//#region ../core-nodes/src/credentials/ApiKeyCredentialType.ts
|
|
10
|
-
/**
|
|
11
|
-
* API key credential that injects a key either as an HTTP header or a query parameter.
|
|
12
|
-
*/
|
|
13
10
|
const apiKeyCredentialType = defineCredential({
|
|
14
11
|
key: "core-nodes.api-key",
|
|
15
12
|
label: "API Key",
|
|
@@ -57,10 +54,6 @@ const apiKeyCredentialType = defineCredential({
|
|
|
57
54
|
|
|
58
55
|
//#endregion
|
|
59
56
|
//#region ../core-nodes/src/credentials/BasicAuthCredentialType.ts
|
|
60
|
-
/**
|
|
61
|
-
* HTTP Basic authentication credential.
|
|
62
|
-
* Session sets `Authorization: Basic <base64(username:password)>`.
|
|
63
|
-
*/
|
|
64
57
|
const basicAuthCredentialType = defineCredential({
|
|
65
58
|
key: "core-nodes.basic-auth",
|
|
66
59
|
label: "Basic Auth",
|
|
@@ -98,10 +91,6 @@ const basicAuthCredentialType = defineCredential({
|
|
|
98
91
|
|
|
99
92
|
//#endregion
|
|
100
93
|
//#region ../core-nodes/src/credentials/BearerTokenCredentialType.ts
|
|
101
|
-
/**
|
|
102
|
-
* Simple Bearer token credential.
|
|
103
|
-
* Session sets `Authorization: Bearer <token>` on every request.
|
|
104
|
-
*/
|
|
105
94
|
const bearerTokenCredentialType = defineCredential({
|
|
106
95
|
key: "core-nodes.bearer-token",
|
|
107
96
|
label: "Bearer Token",
|
|
@@ -160,24 +149,6 @@ var OAuth2TokenExchangeFactory = class {
|
|
|
160
149
|
|
|
161
150
|
//#endregion
|
|
162
151
|
//#region ../core-nodes/src/credentials/OAuth2ClientCredentialsTypeFactory.ts
|
|
163
|
-
/**
|
|
164
|
-
* OAuth2 client-credentials flow credential.
|
|
165
|
-
*
|
|
166
|
-
* This is a machine-to-machine flow: no user redirect occurs. The session
|
|
167
|
-
* POSTs to the configured `tokenUrl` with `client_credentials` grant, caches
|
|
168
|
-
* the resulting access token for the duration of the session, and injects it
|
|
169
|
-
* as `Authorization: Bearer <token>` on each request.
|
|
170
|
-
*
|
|
171
|
-
* Token caching is per-session only (one createSession call = one token fetch
|
|
172
|
-
* at most). Cross-session caching would require host-level state and is out of
|
|
173
|
-
* scope here. Because the engine creates a fresh session per execution, a new
|
|
174
|
-
* token is fetched once per node activation.
|
|
175
|
-
*
|
|
176
|
-
* NOTE: `auth` is intentionally omitted from the definition. The OAuth2
|
|
177
|
-
* `auth: { kind: "oauth2" }` shape signals an authorization-code / user-redirect
|
|
178
|
-
* flow; using it here would cause the host UI to render an OAuth consent button
|
|
179
|
-
* that goes nowhere. Client-credentials is a purely server-side flow.
|
|
180
|
-
*/
|
|
181
152
|
const oauth2ClientCredentialsType = defineCredential({
|
|
182
153
|
key: "core-nodes.oauth2-client-credentials",
|
|
183
154
|
label: "OAuth2 Client Credentials",
|
|
@@ -261,10 +232,6 @@ const oauth2ClientCredentialsType = defineCredential({
|
|
|
261
232
|
|
|
262
233
|
//#endregion
|
|
263
234
|
//#region ../core-nodes/src/http/SSRFBlockedError.ts
|
|
264
|
-
/**
|
|
265
|
-
* Thrown when an HTTP request target resolves to a private, link-local, or
|
|
266
|
-
* loopback address and `allowPrivateNetworkTargets` is not set.
|
|
267
|
-
*/
|
|
268
235
|
var SSRFBlockedError = class extends Error {
|
|
269
236
|
resolvedIp;
|
|
270
237
|
constructor(host, resolvedIp) {
|
|
@@ -276,26 +243,7 @@ var SSRFBlockedError = class extends Error {
|
|
|
276
243
|
|
|
277
244
|
//#endregion
|
|
278
245
|
//#region ../core-nodes/src/http/SsrfGuard.ts
|
|
279
|
-
/** Emitted once per process when NODE_ENV=production and no allowedOutboundHosts is set. */
|
|
280
246
|
let _productionNoAllowlistWarned = false;
|
|
281
|
-
/**
|
|
282
|
-
* Guards HTTP requests against Server-Side Request Forgery (SSRF) by
|
|
283
|
-
* DNS-resolving the target host and rejecting private/link-local/loopback
|
|
284
|
-
* addresses.
|
|
285
|
-
*
|
|
286
|
-
* Blocked ranges:
|
|
287
|
-
* - RFC-1918: 10/8, 172.16/12, 192.168/16
|
|
288
|
-
* - Link-local: 169.254/16
|
|
289
|
-
* - Loopback: 127/8, ::1
|
|
290
|
-
*
|
|
291
|
-
* When `allowedOutboundHosts` is set, every resolved DNS target must match
|
|
292
|
-
* at least one entry in the list (exact hostname or `*.example.com` wildcard).
|
|
293
|
-
* When unset, existing behaviour applies: private ranges blocked, public allowed.
|
|
294
|
-
*
|
|
295
|
-
* Call {@link check} before making any outbound HTTP request.
|
|
296
|
-
* Pass `allowPrivate: true` to bypass the private-network guard for trusted workflows
|
|
297
|
-
* (allowedOutboundHosts allowlist is still applied when set).
|
|
298
|
-
*/
|
|
299
247
|
var SsrfGuard = class {
|
|
300
248
|
constructor(allowedOutboundHosts) {
|
|
301
249
|
this.allowedOutboundHosts = allowedOutboundHosts;
|
|
@@ -304,15 +252,6 @@ var SsrfGuard = class {
|
|
|
304
252
|
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.");
|
|
305
253
|
}
|
|
306
254
|
}
|
|
307
|
-
/**
|
|
308
|
-
* Resolves the host of `url` via DNS and throws {@link SSRFBlockedError}
|
|
309
|
-
* if any resolved address falls in a blocked range, or if the host does not
|
|
310
|
-
* match the operator-configured allowlist (when set).
|
|
311
|
-
*
|
|
312
|
-
* @param url - Fully-qualified URL of the intended request target.
|
|
313
|
-
* @param allowPrivate - When `true`, the private-network check is skipped.
|
|
314
|
-
* The allowedOutboundHosts check is still applied when set.
|
|
315
|
-
*/
|
|
316
255
|
async check(url, allowPrivate) {
|
|
317
256
|
if (allowPrivate && !this.allowedOutboundHosts?.length) return;
|
|
318
257
|
let host;
|
|
@@ -336,10 +275,6 @@ var SsrfGuard = class {
|
|
|
336
275
|
}
|
|
337
276
|
for (const { address } of addresses) if (this.isPrivateAddress(address)) throw new SSRFBlockedError(host, address);
|
|
338
277
|
}
|
|
339
|
-
/**
|
|
340
|
-
* Returns true when `host` matches at least one entry in `allowedOutboundHosts`.
|
|
341
|
-
* Supports exact hostnames (`api.example.com`) and wildcard prefixes (`*.example.com`).
|
|
342
|
-
*/
|
|
343
278
|
isHostAllowed(host) {
|
|
344
279
|
for (const allowed of this.allowedOutboundHosts ?? []) if (allowed.startsWith("*.")) {
|
|
345
280
|
const suffix = allowed.slice(1);
|
|
@@ -373,21 +308,6 @@ var SsrfGuard = class {
|
|
|
373
308
|
|
|
374
309
|
//#endregion
|
|
375
310
|
//#region ../core-nodes/src/http/HttpRequestExecutor.ts
|
|
376
|
-
/**
|
|
377
|
-
* Executes a single HTTP request described by {@link HttpRequestSpec}.
|
|
378
|
-
*
|
|
379
|
-
* - Credential sessions provide header/query deltas via `applyToRequest`.
|
|
380
|
-
* - Body encoding is delegated to {@link HttpBodyBuilder}.
|
|
381
|
-
* - URL query merging is delegated to {@link HttpUrlBuilder}.
|
|
382
|
-
* - SSRF protection is delegated to {@link SsrfGuard} (injected).
|
|
383
|
-
* - Binary response bodies: when `download.mode` triggers binary attach, the
|
|
384
|
-
* `bodyBinaryName` field is set in the result but the body is NOT read here.
|
|
385
|
-
* Callers that need binary attachment should use `buildRequest` to get the
|
|
386
|
-
* resolved URL + init and make the fetch + binary attach themselves.
|
|
387
|
-
*
|
|
388
|
-
* Collaborators (`fetch`, body builder, url builder, ssrfGuard) are injected so
|
|
389
|
-
* callers own construction at composition roots and tests can supply deterministic stubs.
|
|
390
|
-
*/
|
|
391
311
|
var HttpRequestExecutor = class {
|
|
392
312
|
constructor(fetchFn, bodyBuilder, urlBuilder, ssrfGuard) {
|
|
393
313
|
this.fetchFn = fetchFn;
|
|
@@ -395,14 +315,6 @@ var HttpRequestExecutor = class {
|
|
|
395
315
|
this.urlBuilder = urlBuilder;
|
|
396
316
|
this.ssrfGuard = ssrfGuard;
|
|
397
317
|
}
|
|
398
|
-
/**
|
|
399
|
-
* Builds the fetch init (headers, query, body) from the spec + credential delta,
|
|
400
|
-
* returning both the resolved URL and the RequestInit so callers can make the
|
|
401
|
-
* actual fetch call themselves (useful for streaming / binary attach).
|
|
402
|
-
*
|
|
403
|
-
* Also performs SSRF protection via the injected {@link SsrfGuard} before
|
|
404
|
-
* returning — throws {@link SSRFBlockedError} if the target is a private address.
|
|
405
|
-
*/
|
|
406
318
|
async buildRequest(spec, item) {
|
|
407
319
|
await this.ssrfGuard.check(spec.url, spec.allowPrivateNetworkTargets ?? false);
|
|
408
320
|
const credentialDelta = spec.credential?.applyToRequest(spec) ?? {};
|
|
@@ -426,12 +338,6 @@ var HttpRequestExecutor = class {
|
|
|
426
338
|
}
|
|
427
339
|
};
|
|
428
340
|
}
|
|
429
|
-
/**
|
|
430
|
-
* Executes an HTTP request and returns parsed result.
|
|
431
|
-
* For binary downloads (when `shouldAttachBody` is true), the body is NOT consumed
|
|
432
|
-
* and callers must call `ctx.binary.attach` directly using the resolved URL + init
|
|
433
|
-
* (available via `buildRequest`).
|
|
434
|
-
*/
|
|
435
341
|
async execute(spec, item) {
|
|
436
342
|
const { url: resolvedUrl, init } = await this.buildRequest(spec, item);
|
|
437
343
|
const response = await this.fetchFn(resolvedUrl, init);
|
|
@@ -488,10 +394,6 @@ var HttpRequestExecutor = class {
|
|
|
488
394
|
|
|
489
395
|
//#endregion
|
|
490
396
|
//#region ../core-nodes/src/http/HttpBodyBuilder.ts
|
|
491
|
-
/**
|
|
492
|
-
* Builds a fetch-compatible `BodyInit` + Content-Type pair from an {@link HttpBodySpec}.
|
|
493
|
-
* Multipart binaries are read from `item.binary` via `ctx.binary.openReadStream`.
|
|
494
|
-
*/
|
|
495
397
|
var HttpBodyBuilder = class {
|
|
496
398
|
async build(spec, item, ctx) {
|
|
497
399
|
if (!spec || spec.kind === "none") return;
|
|
@@ -562,10 +464,6 @@ var HttpBodyBuilder = class {
|
|
|
562
464
|
|
|
563
465
|
//#endregion
|
|
564
466
|
//#region ../core-nodes/src/http/HttpUrlBuilder.ts
|
|
565
|
-
/**
|
|
566
|
-
* Merges query parameters into a base URL.
|
|
567
|
-
* Handles both scalar and array values, and preserves any existing params.
|
|
568
|
-
*/
|
|
569
467
|
var HttpUrlBuilder = class {
|
|
570
468
|
build(baseUrl, query) {
|
|
571
469
|
if (!query || Object.keys(query).length === 0) return baseUrl;
|
|
@@ -609,11 +507,6 @@ OpenAIChatModelFactory = __decorate([chatModel({ packageName: "@codemation/core-
|
|
|
609
507
|
|
|
610
508
|
//#endregion
|
|
611
509
|
//#region ../core-nodes/src/nodes/ConnectionCredentialExecutionContextFactory.ts
|
|
612
|
-
/**
|
|
613
|
-
* Builds a {@link NodeExecutionContext} whose identity for credential binding and `getCredential`
|
|
614
|
-
* is a **connection-owned** workflow node id (`ConnectionNodeIdFactory` in `@codemation/core`),
|
|
615
|
-
* not the executing parent node. Use for LLM slots, tool slots, or any connection-scoped owner.
|
|
616
|
-
*/
|
|
617
510
|
var ConnectionCredentialExecutionContextFactory = class {
|
|
618
511
|
credentialResolverFactory;
|
|
619
512
|
constructor(credentialSessions) {
|
|
@@ -636,15 +529,6 @@ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
|
|
|
636
529
|
createConnectionCredentialExecutionContextFactory(credentialSessions) {
|
|
637
530
|
return new ConnectionCredentialExecutionContextFactory(credentialSessions);
|
|
638
531
|
}
|
|
639
|
-
/**
|
|
640
|
-
* Produces a plain JSON Schema object (`draft-07`) from a Zod schema, as needed by
|
|
641
|
-
* OpenAI tool-parameter schemas and the structured-output repair prompt.
|
|
642
|
-
* - Prefers the schema's **instance** `toJSONSchema(...)` method so we stay inside the Zod
|
|
643
|
-
* instance that created the schema (works across consumer/framework tsx namespaces — see
|
|
644
|
-
* {@link ZodInstanceToJsonSchema}). Falls back to the framework-imported module function.
|
|
645
|
-
* - Strips root `$schema` (OpenAI ignores it).
|
|
646
|
-
* - Sanitizes `required` for cfworker json-schema compatibility (must be a string array or absent).
|
|
647
|
-
*/
|
|
648
532
|
createJsonSchemaRecord(inputSchema, options) {
|
|
649
533
|
const { $schema: _draftSchemaOmitted,...rest } = this.convertZodSchemaToJsonSchema(inputSchema, { target: "draft-07" });
|
|
650
534
|
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)}).`);
|
|
@@ -653,20 +537,11 @@ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
|
|
|
653
537
|
this.sanitizeJsonSchemaRequiredKeywordsForCfworker(rest);
|
|
654
538
|
return rest;
|
|
655
539
|
}
|
|
656
|
-
/**
|
|
657
|
-
* Runs Zod's `toJSONSchema` via the schema's own instance method when available, so consumer
|
|
658
|
-
* schemas loaded under a different tsx namespace still convert correctly. If the caller handed us
|
|
659
|
-
* a payload that lacks that method (e.g. a plain JSON Schema record or a Zod instance whose
|
|
660
|
-
* prototype was stripped), we fall back to the framework-bundled module function.
|
|
661
|
-
*/
|
|
662
540
|
convertZodSchemaToJsonSchema(inputSchema, params) {
|
|
663
541
|
const candidate = inputSchema.toJSONSchema;
|
|
664
542
|
if (typeof candidate === "function") return candidate.call(inputSchema, params);
|
|
665
543
|
return toJSONSchema(inputSchema, params);
|
|
666
544
|
}
|
|
667
|
-
/**
|
|
668
|
-
* `@cfworker/json-schema` iterates `schema.required` with `for...of`; it must be a string array or absent.
|
|
669
|
-
*/
|
|
670
545
|
sanitizeJsonSchemaRequiredKeywordsForCfworker(node$1) {
|
|
671
546
|
if (!node$1 || typeof node$1 !== "object" || Array.isArray(node$1)) return;
|
|
672
547
|
const o = node$1;
|
|
@@ -813,11 +688,6 @@ var OpenAIChatModelConfig = class {
|
|
|
813
688
|
|
|
814
689
|
//#endregion
|
|
815
690
|
//#region ../core-nodes/src/chatModels/OpenAiChatModelPresetsFactory.ts
|
|
816
|
-
/**
|
|
817
|
-
* Default OpenAI chat model configs for scaffolds and demos (icon + label match {@link OpenAIChatModelConfig} defaults).
|
|
818
|
-
* Prefer importing {@link openAiChatModelPresets} from here or from the consumer template re-export
|
|
819
|
-
* instead of repeating {@link OpenAIChatModelConfig} construction in app workflows.
|
|
820
|
-
*/
|
|
821
691
|
var OpenAiChatModelPresets = class {
|
|
822
692
|
demoGpt4oMini = new OpenAIChatModelConfig("OpenAI", "gpt-4o-mini");
|
|
823
693
|
demoGpt41 = new OpenAIChatModelConfig("OpenAI", "gpt-4.1");
|
|
@@ -826,19 +696,6 @@ const openAiChatModelPresets = new OpenAiChatModelPresets();
|
|
|
826
696
|
|
|
827
697
|
//#endregion
|
|
828
698
|
//#region ../core-nodes/src/chatModels/ManagedHmacSignerFactory.types.ts
|
|
829
|
-
/**
|
|
830
|
-
* Creates an HMAC-signing fetch wrapper that authenticates requests to
|
|
831
|
-
* Codemation managed services (LLM broker, doc-scanner) with the
|
|
832
|
-
* Codemation-Hmac v=1 scheme.
|
|
833
|
-
*
|
|
834
|
-
* Mirrors HmacRequestSigner from @codemation/host/pairing without importing
|
|
835
|
-
* that package (which would create a circular dependency since @codemation/host
|
|
836
|
-
* depends on @codemation/core-nodes).
|
|
837
|
-
*
|
|
838
|
-
* @param workspaceId - Workspace identifier injected by the CP provisioner.
|
|
839
|
-
* @param pairingSecret - Base64-encoded 32-byte HMAC key injected by the provisioner.
|
|
840
|
-
* @param options - Optional behaviour flags and test seams.
|
|
841
|
-
*/
|
|
842
699
|
function managedHmacFetchFactory(workspaceId, pairingSecret, options) {
|
|
843
700
|
const signBody = options?.signBody ?? true;
|
|
844
701
|
return async (input, init) => {
|
|
@@ -857,10 +714,6 @@ function managedHmacFetchFactory(workspaceId, pairingSecret, options) {
|
|
|
857
714
|
});
|
|
858
715
|
};
|
|
859
716
|
}
|
|
860
|
-
/**
|
|
861
|
-
* Produces a Codemation-Hmac v=1 Authorization header value.
|
|
862
|
-
* Algorithm must match HmacVerifier.computeSignature() in the control-plane.
|
|
863
|
-
*/
|
|
864
717
|
function buildHmacAuthHeader(workspaceId, pairingSecret, method, url, body, overrides) {
|
|
865
718
|
const ts = overrides?.now ? overrides.now() : Math.floor(Date.now() / 1e3);
|
|
866
719
|
const nonce = overrides?.nonce ? overrides.nonce() : randomBytes(16).toString("base64");
|
|
@@ -902,21 +755,137 @@ let CodemationChatModelFactory = class CodemationChatModelFactory$1 {
|
|
|
902
755
|
};
|
|
903
756
|
CodemationChatModelFactory = __decorate([chatModel({ packageName: "@codemation/core-nodes" })], CodemationChatModelFactory);
|
|
904
757
|
|
|
758
|
+
//#endregion
|
|
759
|
+
//#region ../core-nodes/src/nodes/AgentToolResultContentFactory.ts
|
|
760
|
+
const MAX_INLINE_BYTES = 8 * 1024 * 1024;
|
|
761
|
+
const KNOWN_MCP_BLOCK_TYPES = new Set([
|
|
762
|
+
"text",
|
|
763
|
+
"image",
|
|
764
|
+
"audio",
|
|
765
|
+
"resource",
|
|
766
|
+
"resource_link"
|
|
767
|
+
]);
|
|
768
|
+
var AgentToolResultContentFactory = class AgentToolResultContentFactory {
|
|
769
|
+
static tryMapToContentOutput(result) {
|
|
770
|
+
const blocks = AgentToolResultContentFactory.contentBlocks(result);
|
|
771
|
+
if (blocks === void 0) return void 0;
|
|
772
|
+
const parts = [];
|
|
773
|
+
let inlinedBytes = 0;
|
|
774
|
+
for (const block of blocks) {
|
|
775
|
+
const mapped = AgentToolResultContentFactory.mapBlock(block, inlinedBytes);
|
|
776
|
+
parts.push(mapped.part);
|
|
777
|
+
inlinedBytes += mapped.bytes;
|
|
778
|
+
}
|
|
779
|
+
return parts;
|
|
780
|
+
}
|
|
781
|
+
static contentBlocks(result) {
|
|
782
|
+
if (result === null || typeof result !== "object") return void 0;
|
|
783
|
+
const content = result.content;
|
|
784
|
+
if (!Array.isArray(content) || content.length === 0) return void 0;
|
|
785
|
+
if (!content.every((block) => block !== null && typeof block === "object" && typeof block.type === "string")) return void 0;
|
|
786
|
+
return content.some((block) => KNOWN_MCP_BLOCK_TYPES.has(block.type)) ? content : void 0;
|
|
787
|
+
}
|
|
788
|
+
static mapBlock(block, inlinedBytesSoFar) {
|
|
789
|
+
const type = block.type;
|
|
790
|
+
if (type === "text" && typeof block.text === "string") return {
|
|
791
|
+
part: {
|
|
792
|
+
type: "text",
|
|
793
|
+
text: block.text
|
|
794
|
+
},
|
|
795
|
+
bytes: 0
|
|
796
|
+
};
|
|
797
|
+
if (type === "image" && typeof block.data === "string" && typeof block.mimeType === "string") return AgentToolResultContentFactory.mapBinary({
|
|
798
|
+
base64: block.data,
|
|
799
|
+
mediaType: block.mimeType,
|
|
800
|
+
inlinedBytesSoFar
|
|
801
|
+
});
|
|
802
|
+
if (type === "resource" && block.resource) return AgentToolResultContentFactory.mapEmbeddedResource(block.resource, inlinedBytesSoFar);
|
|
803
|
+
if (type === "resource_link" && typeof block.uri === "string") {
|
|
804
|
+
const mime = typeof block.mimeType === "string" ? ` (${block.mimeType})` : "";
|
|
805
|
+
return {
|
|
806
|
+
part: {
|
|
807
|
+
type: "text",
|
|
808
|
+
text: `[linked resource: ${block.uri}${mime}]`
|
|
809
|
+
},
|
|
810
|
+
bytes: 0
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
return {
|
|
814
|
+
part: {
|
|
815
|
+
type: "text",
|
|
816
|
+
text: `[unsupported tool content block: ${String(type)}]`
|
|
817
|
+
},
|
|
818
|
+
bytes: 0
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
static mapEmbeddedResource(resource, inlinedBytesSoFar) {
|
|
822
|
+
if (typeof resource.text === "string") return {
|
|
823
|
+
part: {
|
|
824
|
+
type: "text",
|
|
825
|
+
text: resource.text
|
|
826
|
+
},
|
|
827
|
+
bytes: 0
|
|
828
|
+
};
|
|
829
|
+
if (typeof resource.blob === "string" && typeof resource.mimeType === "string") return AgentToolResultContentFactory.mapBinary({
|
|
830
|
+
base64: resource.blob,
|
|
831
|
+
mediaType: resource.mimeType,
|
|
832
|
+
filename: typeof resource.name === "string" ? resource.name : void 0,
|
|
833
|
+
inlinedBytesSoFar
|
|
834
|
+
});
|
|
835
|
+
return {
|
|
836
|
+
part: {
|
|
837
|
+
type: "text",
|
|
838
|
+
text: `[embedded resource: ${typeof resource.uri === "string" ? resource.uri : "unknown"}]`
|
|
839
|
+
},
|
|
840
|
+
bytes: 0
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
static mapBinary(args) {
|
|
844
|
+
const rawBytes = Math.floor(args.base64.length * 3 / 4);
|
|
845
|
+
if (args.inlinedBytesSoFar + rawBytes > MAX_INLINE_BYTES) {
|
|
846
|
+
const name = args.filename ? ` "${args.filename}"` : "";
|
|
847
|
+
const kb = Math.round(rawBytes / 1024);
|
|
848
|
+
return {
|
|
849
|
+
part: {
|
|
850
|
+
type: "text",
|
|
851
|
+
text: `[binary${name} (${args.mediaType}, ~${kb} KB) omitted: exceeds the per-tool-result inline limit]`
|
|
852
|
+
},
|
|
853
|
+
bytes: 0
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
if (args.mediaType.startsWith("image/")) return {
|
|
857
|
+
part: {
|
|
858
|
+
type: "image-data",
|
|
859
|
+
data: args.base64,
|
|
860
|
+
mediaType: args.mediaType
|
|
861
|
+
},
|
|
862
|
+
bytes: rawBytes
|
|
863
|
+
};
|
|
864
|
+
if (args.mediaType === "application/pdf") return {
|
|
865
|
+
part: {
|
|
866
|
+
type: "file-data",
|
|
867
|
+
data: args.base64,
|
|
868
|
+
mediaType: args.mediaType,
|
|
869
|
+
...args.filename ? { filename: args.filename } : {}
|
|
870
|
+
},
|
|
871
|
+
bytes: rawBytes
|
|
872
|
+
};
|
|
873
|
+
return {
|
|
874
|
+
part: {
|
|
875
|
+
type: "text",
|
|
876
|
+
text: `[binary${args.filename ? ` "${args.filename}"` : ""} (${args.mediaType}) not inlined: unsupported by the model]`
|
|
877
|
+
},
|
|
878
|
+
bytes: 0
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
|
|
905
883
|
//#endregion
|
|
906
884
|
//#region ../core-nodes/src/nodes/AgentMessageFactory.ts
|
|
907
|
-
/**
|
|
908
|
-
* AI-SDK-shaped message construction for the AIAgent stack. Emits plain `ModelMessage[]`
|
|
909
|
-
* ( `{ role: 'system' | 'user' | 'assistant' | 'tool', content: ... }` ) as consumed by
|
|
910
|
-
* `generateText({ messages })` from the `ai` package.
|
|
911
|
-
*/
|
|
912
885
|
var AgentMessageFactory = class AgentMessageFactory {
|
|
913
886
|
static createPromptMessages(messages) {
|
|
914
887
|
return messages.map((message) => this.createPromptMessage(message));
|
|
915
888
|
}
|
|
916
|
-
/**
|
|
917
|
-
* Builds the assistant message that contains optional text plus one or more tool-call parts,
|
|
918
|
-
* matching the shape AI SDK emits between steps.
|
|
919
|
-
*/
|
|
920
889
|
static createAssistantWithToolCalls(text, toolCalls) {
|
|
921
890
|
const content = [];
|
|
922
891
|
if (text && text.length > 0) content.push({
|
|
@@ -934,24 +903,30 @@ var AgentMessageFactory = class AgentMessageFactory {
|
|
|
934
903
|
content
|
|
935
904
|
};
|
|
936
905
|
}
|
|
937
|
-
|
|
938
|
-
* Builds the `{ role: "tool", content: [{ type: "tool-result", ... }, ...] }` message returned
|
|
939
|
-
* to the model after each tool round.
|
|
940
|
-
*/
|
|
941
|
-
static createToolResultsMessage(executedToolCalls) {
|
|
906
|
+
static createToolResultsMessage(executedToolCalls, passToolBinariesToModel = true) {
|
|
942
907
|
return {
|
|
943
908
|
role: "tool",
|
|
944
909
|
content: executedToolCalls.map((executed) => ({
|
|
945
910
|
type: "tool-result",
|
|
946
911
|
toolCallId: executed.toolCallId,
|
|
947
912
|
toolName: executed.toolName,
|
|
948
|
-
output:
|
|
949
|
-
type: "json",
|
|
950
|
-
value: AgentMessageFactory.toToolResultJson(executed.result)
|
|
951
|
-
}
|
|
913
|
+
output: AgentMessageFactory.toToolResultOutput(executed.result, passToolBinariesToModel)
|
|
952
914
|
}))
|
|
953
915
|
};
|
|
954
916
|
}
|
|
917
|
+
static toToolResultOutput(result, passToolBinariesToModel) {
|
|
918
|
+
if (passToolBinariesToModel) {
|
|
919
|
+
const content = AgentToolResultContentFactory.tryMapToContentOutput(result);
|
|
920
|
+
if (content !== void 0) return {
|
|
921
|
+
type: "content",
|
|
922
|
+
value: content
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
return {
|
|
926
|
+
type: "json",
|
|
927
|
+
value: AgentMessageFactory.toToolResultJson(result)
|
|
928
|
+
};
|
|
929
|
+
}
|
|
955
930
|
static toToolResultJson(value) {
|
|
956
931
|
if (value === void 0) return null;
|
|
957
932
|
try {
|
|
@@ -1094,10 +1069,6 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
|
|
|
1094
1069
|
}
|
|
1095
1070
|
throw new Error(`Structured output required for AIAgent "${args.agentName}" (${args.nodeId}) but validation still failed after ${_AgentStructuredOutputRunner.repairAttemptCount} repair attempts: ${failure.validationError}`);
|
|
1096
1071
|
}
|
|
1097
|
-
/**
|
|
1098
|
-
* Chooses strict mode for OpenAI chat-model configs, off otherwise. Extendable in future for
|
|
1099
|
-
* other providers that adopt the same "supply a JSON Schema record directly" contract.
|
|
1100
|
-
*/
|
|
1101
1072
|
resolveStructuredOutputOptions(chatModelConfig) {
|
|
1102
1073
|
if (chatModelConfig.type !== OpenAIChatModelFactory) return;
|
|
1103
1074
|
return {
|
|
@@ -1580,10 +1551,6 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
1580
1551
|
extractErrorDetails(error) {
|
|
1581
1552
|
return error.details;
|
|
1582
1553
|
}
|
|
1583
|
-
/**
|
|
1584
|
-
* Extracts the text content from the last assistant message in the conversation snapshot.
|
|
1585
|
-
* Used to populate `agentReasoning` in the HITL suspension metadata.
|
|
1586
|
-
*/
|
|
1587
1554
|
extractLastAssistantText(conversation) {
|
|
1588
1555
|
for (let i = conversation.length - 1; i >= 0; i--) {
|
|
1589
1556
|
const msg = conversation[i];
|
|
@@ -1618,16 +1585,6 @@ AgentToolExecutionCoordinator = __decorate([
|
|
|
1618
1585
|
|
|
1619
1586
|
//#endregion
|
|
1620
1587
|
//#region ../core-nodes/src/nodes/AgentBinaryContentFactory.ts
|
|
1621
|
-
/**
|
|
1622
|
-
* Turns resolved file binaries into native AI SDK multimodal content parts and merges them into the
|
|
1623
|
-
* agent prompt. Images (`image/*`) become {@link ImagePart}s; every other type (PDFs, office docs,
|
|
1624
|
-
* CSV, JSON, …) becomes a {@link FilePart}. The provider maps these to its wire-level `image` /
|
|
1625
|
-
* `document` blocks; an unsupported file type surfaces as a provider error at runtime.
|
|
1626
|
-
*
|
|
1627
|
-
* Parts are appended to the LAST user message so the binary travels alongside the author's prompt
|
|
1628
|
-
* text (preserving any untrusted-source preamble that already wrapped that text). When no user
|
|
1629
|
-
* message exists, a new user message carrying only the binaries is appended.
|
|
1630
|
-
*/
|
|
1631
1588
|
var AgentBinaryContentFactory = class AgentBinaryContentFactory {
|
|
1632
1589
|
static toContentPart(binary) {
|
|
1633
1590
|
if (binary.mediaType.startsWith("image/")) return {
|
|
@@ -1706,20 +1663,6 @@ let NodeBackedToolRuntime = class NodeBackedToolRuntime$1 {
|
|
|
1706
1663
|
outputs
|
|
1707
1664
|
});
|
|
1708
1665
|
}
|
|
1709
|
-
/**
|
|
1710
|
-
* Returns a re-rooted child ctx for nested-agent tools (so their LLM/tool connection ids derive
|
|
1711
|
-
* from the tool connection node, telemetry parents under the tool-call span, and connection
|
|
1712
|
-
* invocations carry `parentInvocationId`). Plain runnable tools (non-agent) keep the orchestrator
|
|
1713
|
-
* ctx with only `config` swapped — no nesting concern.
|
|
1714
|
-
*
|
|
1715
|
-
* The caller (`AIAgentNode.createItemScopedTools`) already wraps the orchestrator ctx via
|
|
1716
|
-
* `ConnectionCredentialExecutionContextFactory.forConnectionNode`, so `args.ctx.nodeId` is the
|
|
1717
|
-
* tool's own connection node id (e.g. `AIAgentNode:2__conn__tool__searchInMail`). We pass that
|
|
1718
|
-
* through as the sub-agent's `nodeId`; deriving another `toolConnectionNodeId(args.ctx.nodeId,
|
|
1719
|
-
* config.name)` here would prepend a duplicate `__conn__tool__<name>` segment and exponentially
|
|
1720
|
-
* deepen ids on each invocation, which also breaks credential resolution because user-provided
|
|
1721
|
-
* bindings sit on the single-level connection node id.
|
|
1722
|
-
*/
|
|
1723
1666
|
resolveNodeCtx(config, args) {
|
|
1724
1667
|
const isNestedAgent = AgentConfigInspector.isAgentNodeConfig(config.node);
|
|
1725
1668
|
const hooks = args.hooks;
|
|
@@ -1784,22 +1727,12 @@ NodeBackedToolRuntime = __decorate([
|
|
|
1784
1727
|
|
|
1785
1728
|
//#endregion
|
|
1786
1729
|
//#region ../core-nodes/src/nodes/BM25Index.ts
|
|
1787
|
-
/**
|
|
1788
|
-
* Minimal BM25 (Okapi BM25) implementation for indexing MCP tool descriptions.
|
|
1789
|
-
*
|
|
1790
|
-
* Parameters: k1=1.5, b=0.75 (standard defaults).
|
|
1791
|
-
* Tokenisation: lowercase, split on non-alphanumerics, filter empties.
|
|
1792
|
-
*/
|
|
1793
1730
|
var BM25Index = class {
|
|
1794
1731
|
k1 = 1.5;
|
|
1795
1732
|
b = .75;
|
|
1796
1733
|
tf = [];
|
|
1797
1734
|
df = /* @__PURE__ */ new Map();
|
|
1798
1735
|
avgDocLen = 0;
|
|
1799
|
-
/**
|
|
1800
|
-
* Add all documents at once. After calling this, search is available.
|
|
1801
|
-
* Documents are indexed in insertion order; search returns their indices.
|
|
1802
|
-
*/
|
|
1803
1736
|
add(docs) {
|
|
1804
1737
|
const docTerms = docs.map((d) => this.tokenize(d));
|
|
1805
1738
|
let totalLen = 0;
|
|
@@ -1812,10 +1745,6 @@ var BM25Index = class {
|
|
|
1812
1745
|
}
|
|
1813
1746
|
this.avgDocLen = docTerms.length > 0 ? totalLen / docTerms.length : 0;
|
|
1814
1747
|
}
|
|
1815
|
-
/**
|
|
1816
|
-
* Returns up to `limit` document indices ranked by BM25 score (highest first).
|
|
1817
|
-
* Returns an empty array if the index is empty or the query matches nothing.
|
|
1818
|
-
*/
|
|
1819
1748
|
search(query, limit) {
|
|
1820
1749
|
const n = this.tf.length;
|
|
1821
1750
|
if (n === 0) return [];
|
|
@@ -1856,26 +1785,12 @@ const PINNED_TOOLS_SOFT_LIMIT = 8;
|
|
|
1856
1785
|
const PINNED_TOOLS_HARD_LIMIT = 16;
|
|
1857
1786
|
const FIND_TOOLS_NAME = "find_tools";
|
|
1858
1787
|
const FIND_TOOLS_DEFAULT_LIMIT = 5;
|
|
1859
|
-
/**
|
|
1860
|
-
* Default tool-loading strategy: BM25-indexed MCP tool deferral via a `find_tools` meta-tool.
|
|
1861
|
-
*
|
|
1862
|
-
* - Node-backed tools and pinned MCP tools are always included in every turn.
|
|
1863
|
-
* - `find_tools(query, limit?)` is added to the tool set when MCP tools are indexed.
|
|
1864
|
-
* - Tools surfaced by `find_tools` are included in subsequent turns.
|
|
1865
|
-
*
|
|
1866
|
-
* Not DI-managed; instantiated per agent execution by DeferredMetaToolStrategyFactory.
|
|
1867
|
-
*/
|
|
1868
1788
|
var DeferredMetaToolStrategy = class {
|
|
1869
1789
|
nodeBackedTools = {};
|
|
1870
1790
|
pinnedTools = {};
|
|
1871
1791
|
mcpEntries = [];
|
|
1872
1792
|
toolsByServerId = /* @__PURE__ */ new Map();
|
|
1873
1793
|
foundToolIds = /* @__PURE__ */ new Set();
|
|
1874
|
-
/**
|
|
1875
|
-
* `jsonSchema` from the `ai` SDK, loaded lazily in {@link initialize} so the SDK
|
|
1876
|
-
* (~28MB RSS) stays off the boot path. `initialize` always runs before the sync
|
|
1877
|
-
* `getToolsForTurn` → `buildFindToolsDefinition` path, so this is set before use.
|
|
1878
|
-
*/
|
|
1879
1794
|
jsonSchema;
|
|
1880
1795
|
constructor(bm25, warnFn) {
|
|
1881
1796
|
this.bm25 = bm25;
|
|
@@ -2023,11 +1938,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2023
1938
|
inputSchema = z.unknown();
|
|
2024
1939
|
connectionCredentialExecutionContextFactory;
|
|
2025
1940
|
preparedByExecutionContext = /* @__PURE__ */ new WeakMap();
|
|
2026
|
-
/**
|
|
2027
|
-
* The `ai` SDK, loaded lazily in {@link execute} so the SDK (~28MB RSS) stays
|
|
2028
|
-
* off the boot path — non-AI workflows never load it. Every path runs through
|
|
2029
|
-
* `execute` → `ensureAiSdk` before any sync helper touches `this.aiSdk`.
|
|
2030
|
-
*/
|
|
2031
1941
|
aiSdk;
|
|
2032
1942
|
aiSdkPromise = null;
|
|
2033
1943
|
constructor(nodeResolver, credentialSessions, nodeBackedToolRuntime, executionHelpers, structuredOutputRunner, toolExecutionCoordinator, toolLoadingStrategyFactory, agentMcpIntegration) {
|
|
@@ -2051,15 +1961,9 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2051
1961
|
};
|
|
2052
1962
|
return (await this.runAgentForItem(prepared, itemWithMappedJson, args.itemIndex, args.items)).json;
|
|
2053
1963
|
}
|
|
2054
|
-
/** Load the `ai` SDK once per node instance (cached promise guards concurrent items). */
|
|
2055
1964
|
async ensureAiSdk() {
|
|
2056
1965
|
this.aiSdk = await (this.aiSdkPromise ??= import("./dist-DBYFkhSG.js"));
|
|
2057
1966
|
}
|
|
2058
|
-
/**
|
|
2059
|
-
* Resume path: re-enters the agent loop after a HITL suspension.
|
|
2060
|
-
* Reconstructs the conversation from the checkpoint, injects the human decision
|
|
2061
|
-
* as a tool_result, and continues the loop from where it suspended.
|
|
2062
|
-
*/
|
|
2063
1967
|
async executeResumed(args, resumeContext) {
|
|
2064
1968
|
const { ctx } = args;
|
|
2065
1969
|
const taskMetadata = resumeContext.task.metadata ?? {};
|
|
@@ -2086,7 +1990,7 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2086
1990
|
result: decision,
|
|
2087
1991
|
serialized: JSON.stringify(decision)
|
|
2088
1992
|
};
|
|
2089
|
-
const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry])];
|
|
1993
|
+
const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry], ctx.config.passToolBinariesToModel !== false)];
|
|
2090
1994
|
const loopResult = await this.runTurnLoopUntilFinalAnswer({
|
|
2091
1995
|
prepared,
|
|
2092
1996
|
itemInputsByPort,
|
|
@@ -2106,10 +2010,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2106
2010
|
const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
|
|
2107
2011
|
return this.buildOutputItem(item, outputJson).json;
|
|
2108
2012
|
}
|
|
2109
|
-
/**
|
|
2110
|
-
* Normalizes a {@link ResumeContext} decision into a flat JSON-serializable shape
|
|
2111
|
-
* suitable for injection as a tool_result content.
|
|
2112
|
-
*/
|
|
2113
2013
|
normalizeDecision(resumeContext) {
|
|
2114
2014
|
const { decision } = resumeContext;
|
|
2115
2015
|
if (decision.kind === "decided") {
|
|
@@ -2236,16 +2136,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2236
2136
|
const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
|
|
2237
2137
|
return this.buildOutputItem(item, outputJson);
|
|
2238
2138
|
}
|
|
2239
|
-
/**
|
|
2240
|
-
* Multi-turn loop:
|
|
2241
|
-
* - Each turn is a single `generateText` call with tools exposed but **not auto-executed**
|
|
2242
|
-
* (we control tool dispatch so that {@link AgentToolExecutionCoordinator} drives repair /
|
|
2243
|
-
* connection-invocation recording / transient-error handling exactly like before).
|
|
2244
|
-
* - When the model returns no tool calls the loop ends with the model's text as the final answer.
|
|
2245
|
-
* - Respects `guardrails.maxTurns` and `guardrails.onTurnLimitReached`.
|
|
2246
|
-
* - Strategy-owned tool calls (e.g. `find_tools`) are dispatched via the strategy, not the
|
|
2247
|
-
* coordinator; their results are tracked so subsequent turns receive the discovered tools.
|
|
2248
|
-
*/
|
|
2249
2139
|
async runTurnLoopUntilFinalAnswer(args) {
|
|
2250
2140
|
const { prepared, itemInputsByPort, itemScopedTools, conversation } = args;
|
|
2251
2141
|
const { ctx, guardrails, toolLoadingStrategy } = prepared;
|
|
@@ -2253,7 +2143,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2253
2143
|
let toolCallCount = args.resumedToolCallCount ?? 0;
|
|
2254
2144
|
let turnCount = 0;
|
|
2255
2145
|
const repairAttemptsByToolName = /* @__PURE__ */ new Map();
|
|
2256
|
-
/** Tool IDs surfaced by find_tools across all prior turns in this item run. */
|
|
2257
2146
|
let previousFoundToolIds = [];
|
|
2258
2147
|
for (let turn = 1; turn <= guardrails.maxTurns; turn++) {
|
|
2259
2148
|
turnCount = turn;
|
|
@@ -2306,7 +2195,7 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2306
2195
|
coordinatorExecutedCalls.push(...executed);
|
|
2307
2196
|
}
|
|
2308
2197
|
const allExecutedCalls = [...strategyExecutedCalls, ...coordinatorExecutedCalls];
|
|
2309
|
-
this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, allExecutedCalls);
|
|
2198
|
+
this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, allExecutedCalls, ctx.config.passToolBinariesToModel !== false);
|
|
2310
2199
|
}
|
|
2311
2200
|
return {
|
|
2312
2201
|
finalText,
|
|
@@ -2321,8 +2210,8 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2321
2210
|
if (guardrails.onTurnLimitReached === "respondWithLastMessage") return;
|
|
2322
2211
|
throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" reached maxTurns=${guardrails.maxTurns} before producing a final response.`);
|
|
2323
2212
|
}
|
|
2324
|
-
appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls) {
|
|
2325
|
-
conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls));
|
|
2213
|
+
appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls, passToolBinariesToModel) {
|
|
2214
|
+
conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls, passToolBinariesToModel));
|
|
2326
2215
|
}
|
|
2327
2216
|
async resolveFinalOutputJson(prepared, itemInputsByPort, conversation, finalText, wasToolEnabledRun) {
|
|
2328
2217
|
if (!prepared.ctx.config.outputSchema) return AgentOutputFactory.fromAgentContent(finalText);
|
|
@@ -2383,12 +2272,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2383
2272
|
};
|
|
2384
2273
|
});
|
|
2385
2274
|
}
|
|
2386
|
-
/**
|
|
2387
|
-
* Resolves the HITL behavior for a tool binding, or `undefined` when it is not a HITL tool.
|
|
2388
|
-
* A binding is HITL if either the backing node carries a `defineHumanApprovalNode` marker or the
|
|
2389
|
-
* binding sets a per-binding `onRejected` via `asTool(..., { onRejected })`. The per-binding value
|
|
2390
|
-
* wins over the node marker, so two tools backed by the same node can reject differently.
|
|
2391
|
-
*/
|
|
2392
2275
|
resolveHumanApprovalBehavior(config) {
|
|
2393
2276
|
if (!this.isNodeBackedToolConfig(config)) return void 0;
|
|
2394
2277
|
const marker = config.node.humanApprovalToolBehavior;
|
|
@@ -2396,11 +2279,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2396
2279
|
if (marker === void 0 && perBinding === void 0) return void 0;
|
|
2397
2280
|
return { onRejected: perBinding ?? marker?.onRejected ?? "return" };
|
|
2398
2281
|
}
|
|
2399
|
-
/**
|
|
2400
|
-
* Invoke a text turn using the merged tool set from item-scoped tools (coordinator-managed)
|
|
2401
|
-
* and strategy tools (find_tools + discovered MCP tools).
|
|
2402
|
-
* Strategy tools take precedence for names that overlap.
|
|
2403
|
-
*/
|
|
2404
2282
|
async invokeTextTurnWithStrategyTools(prepared, itemInputsByPort, messages, itemScopedTools, strategyTools) {
|
|
2405
2283
|
const itemToolSet = this.buildToolSet(itemScopedTools);
|
|
2406
2284
|
const strategyHasTools = Object.keys(strategyTools).length > 0;
|
|
@@ -2411,10 +2289,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2411
2289
|
} : void 0;
|
|
2412
2290
|
return this.invokeTextTurnWithToolSet(prepared, itemInputsByPort, messages, mergedTools);
|
|
2413
2291
|
}
|
|
2414
|
-
/**
|
|
2415
|
-
* Removes `execute` properties from ToolSet entries so the AI SDK does not
|
|
2416
|
-
* auto-execute them within `generateText`. Codemation owns all tool dispatch.
|
|
2417
|
-
*/
|
|
2418
2292
|
stripExecuteCallbacks(tools) {
|
|
2419
2293
|
const stripped = {};
|
|
2420
2294
|
for (const [name, def] of Object.entries(tools)) {
|
|
@@ -2423,12 +2297,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2423
2297
|
}
|
|
2424
2298
|
return stripped;
|
|
2425
2299
|
}
|
|
2426
|
-
/**
|
|
2427
|
-
* Builds a ToolSet from resolved tools for strategy initialization.
|
|
2428
|
-
* The strategy uses this for its "always-included" node-backed tool descriptions.
|
|
2429
|
-
* HITL tools (detected via the `humanApprovalToolBehavior` field set by `defineHumanApprovalNode`) get the solo-constraint sentence
|
|
2430
|
-
* appended to their description.
|
|
2431
|
-
*/
|
|
2432
2300
|
buildToolSetFromResolved(resolvedTools) {
|
|
2433
2301
|
if (resolvedTools.length === 0) return {};
|
|
2434
2302
|
const toolSet = {};
|
|
@@ -2446,20 +2314,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2446
2314
|
}
|
|
2447
2315
|
return toolSet;
|
|
2448
2316
|
}
|
|
2449
|
-
/**
|
|
2450
|
-
* Builds an AI SDK {@link ToolSet} where every tool ships a pre-converted JSON Schema (via
|
|
2451
|
-
* {@link jsonSchema}) — not the raw Zod schema — and carries **no** `execute`. Two reasons:
|
|
2452
|
-
*
|
|
2453
|
-
* 1. Codemation owns tool dispatch + the per-tool repair loop (see {@link AgentToolExecutionCoordinator}),
|
|
2454
|
-
* so the AI SDK must surface tool calls back to us instead of auto-running them.
|
|
2455
|
-
* 2. The AI SDK's `asSchema` helper discriminates between Zod v3 / Zod v4 / Standard Schema via
|
|
2456
|
-
* runtime feature-detection (`~standard`, `_zod`, etc.). Handing it a pre-built
|
|
2457
|
-
* {@link jsonSchema} record — which is tagged with `Symbol.for('vercel.ai.schema')` — skips all
|
|
2458
|
-
* of that detection and guarantees the provider receives a draft-07 JSON Schema with
|
|
2459
|
-
* `additionalProperties: false` at every object depth (see {@link OpenAiStrictJsonSchemaFactory}
|
|
2460
|
-
* for the same logic applied to structured-output schemas). Codemation still runs its own Zod
|
|
2461
|
-
* validation on tool inputs before execute — the schema handed to the model is advisory.
|
|
2462
|
-
*/
|
|
2463
2317
|
buildToolSet(itemScopedTools) {
|
|
2464
2318
|
if (itemScopedTools.length === 0) return void 0;
|
|
2465
2319
|
const toolSet = {};
|
|
@@ -2477,10 +2331,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2477
2331
|
}
|
|
2478
2332
|
return toolSet;
|
|
2479
2333
|
}
|
|
2480
|
-
/**
|
|
2481
|
-
* One `generateText` turn (no auto tool execution) with Codemation-owned child-span telemetry
|
|
2482
|
-
* and connection-invocation state recording. Accepts a pre-built ToolSet.
|
|
2483
|
-
*/
|
|
2484
2334
|
async invokeTextTurnWithToolSet(prepared, itemInputsByPort, messages, tools) {
|
|
2485
2335
|
const invocationId = ConnectionInvocationIdFactory.create();
|
|
2486
2336
|
const startedAt = /* @__PURE__ */ new Date();
|
|
@@ -2591,11 +2441,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2591
2441
|
});
|
|
2592
2442
|
}
|
|
2593
2443
|
}
|
|
2594
|
-
/**
|
|
2595
|
-
* Structured-output turn: runs `generateText({ output: Output.object({ schema }) })` via the
|
|
2596
|
-
* structured-output runner. We keep this as a separate helper because the runner needs the raw
|
|
2597
|
-
* validated value (not just text) back, and must be able to retry on Zod failures.
|
|
2598
|
-
*/
|
|
2599
2444
|
async invokeStructuredTurn(prepared, itemInputsByPort, schema, messages, structuredOptions) {
|
|
2600
2445
|
const invocationId = ConnectionInvocationIdFactory.create();
|
|
2601
2446
|
const startedAt = /* @__PURE__ */ new Date();
|
|
@@ -2720,13 +2565,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2720
2565
|
providerOptions: overrides?.providerOptions ?? defaults.providerOptions
|
|
2721
2566
|
};
|
|
2722
2567
|
}
|
|
2723
|
-
/**
|
|
2724
|
-
* Build a no-code-friendly output payload for an LLM round.
|
|
2725
|
-
*
|
|
2726
|
-
* Always includes `content` (matching the canvas snapshot shape used elsewhere) and adds a
|
|
2727
|
-
* `toolCalls` array when the round produced tool calls so the execution inspector surfaces the
|
|
2728
|
-
* planned calls instead of just an empty `""` for tool-only rounds.
|
|
2729
|
-
*/
|
|
2730
2568
|
summarizeTurnOutput(turnResult) {
|
|
2731
2569
|
if (turnResult.toolCalls.length === 0) return { content: turnResult.text };
|
|
2732
2570
|
const toolCalls = turnResult.toolCalls.map((toolCall) => ({
|
|
@@ -2929,12 +2767,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2929
2767
|
const binaries = await this.resolveInlineBinaries(attachments, ctx);
|
|
2930
2768
|
return AgentBinaryContentFactory.withBinaries(promptMessages, binaries);
|
|
2931
2769
|
}
|
|
2932
|
-
/**
|
|
2933
|
-
* Picks which attachments feed the passdown. When the author supplies `config.binaries`
|
|
2934
|
-
* (a static array or a per-item function — e.g. to forward binaries from an earlier node),
|
|
2935
|
-
* those replace the current item's attachments; otherwise the current item's `item.binary`
|
|
2936
|
-
* is used.
|
|
2937
|
-
*/
|
|
2938
2770
|
selectBinaryAttachments(item, itemIndex, items, ctx) {
|
|
2939
2771
|
const manual = ctx.config.binaries;
|
|
2940
2772
|
if (manual !== void 0) return typeof manual === "function" ? manual({
|
|
@@ -2945,14 +2777,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2945
2777
|
}) : manual;
|
|
2946
2778
|
return item.binary ? Object.values(item.binary) : [];
|
|
2947
2779
|
}
|
|
2948
|
-
/**
|
|
2949
|
-
* Reads every attachment through `ctx.binary` (storage-backed, by reference — never base64 on
|
|
2950
|
-
* `item.json`) and resolves it to inline base64 so the agent can pass it to the chat model as a
|
|
2951
|
-
* native multimodal block. Images become image blocks; every other type (PDF, office docs, CSV,
|
|
2952
|
-
* JSON, …) becomes a file block — we don't filter by media type, so any binary can be fed to the
|
|
2953
|
-
* model. If the provider rejects an unsupported type the error surfaces at runtime, and the
|
|
2954
|
-
* workflow can filter the binary upstream.
|
|
2955
|
-
*/
|
|
2956
2780
|
async resolveInlineBinaries(attachments, ctx) {
|
|
2957
2781
|
const resolved = [];
|
|
2958
2782
|
for (const attachment of attachments) {
|
|
@@ -2965,12 +2789,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
2965
2789
|
}
|
|
2966
2790
|
return resolved;
|
|
2967
2791
|
}
|
|
2968
|
-
/**
|
|
2969
|
-
* When `item.json.__source` matches an entry in `config.untrustedSources`
|
|
2970
|
-
* (default: `["gmail", "ocr", "webhook"]`), wraps every user-role message
|
|
2971
|
-
* content with an untrusted-external-source preamble so the LLM treats the
|
|
2972
|
-
* content as data, not instructions.
|
|
2973
|
-
*/
|
|
2974
2792
|
wrapUntrustedSourceMessages(messages, item, config) {
|
|
2975
2793
|
const source = item.json !== null && typeof item.json === "object" ? item.json.__source : void 0;
|
|
2976
2794
|
if (typeof source !== "string") return messages;
|
|
@@ -3281,17 +3099,12 @@ HttpRequestNode = __decorate([node({ packageName: "@codemation/core-nodes" })],
|
|
|
3281
3099
|
|
|
3282
3100
|
//#endregion
|
|
3283
3101
|
//#region ../core-nodes/src/nodes/httpRequest.ts
|
|
3284
|
-
/**
|
|
3285
|
-
* The built-in HTTP request credential type IDs accepted by the `HttpRequest` node.
|
|
3286
|
-
* These match the four generic credential types shipped with `@codemation/core-nodes`.
|
|
3287
|
-
*/
|
|
3288
3102
|
const HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES = [
|
|
3289
3103
|
bearerTokenCredentialType.definition.typeId,
|
|
3290
3104
|
apiKeyCredentialType.definition.typeId,
|
|
3291
3105
|
basicAuthCredentialType.definition.typeId,
|
|
3292
3106
|
oauth2ClientCredentialsType.definition.typeId
|
|
3293
3107
|
];
|
|
3294
|
-
/** Default maximum response size for binary mode: 100 MiB. */
|
|
3295
3108
|
const DEFAULT_RESPONSE_SIZE_CAP_BYTES = 100 * 1024 * 1024;
|
|
3296
3109
|
|
|
3297
3110
|
//#endregion
|
|
@@ -3323,9 +3136,6 @@ FilterNode = __decorate([node({ packageName: "@codemation/core-nodes" })], Filte
|
|
|
3323
3136
|
function getOriginIndex(item) {
|
|
3324
3137
|
return getOriginIndexFromItem(item);
|
|
3325
3138
|
}
|
|
3326
|
-
/**
|
|
3327
|
-
* Tags items routed to fan-in merge-by-origin (same contract as {@link IfNode} / {@link SwitchNode}).
|
|
3328
|
-
*/
|
|
3329
3139
|
function tagItemForRouterFanIn(args) {
|
|
3330
3140
|
const { item, itemIndex, nodeId, inputPortLabel = "$in" } = args;
|
|
3331
3141
|
const metaBase = item.meta && typeof item.meta === "object" ? item.meta : {};
|
|
@@ -4208,7 +4018,6 @@ var ManualTrigger = class ManualTrigger {
|
|
|
4208
4018
|
defaultItems;
|
|
4209
4019
|
id;
|
|
4210
4020
|
description;
|
|
4211
|
-
/** Manual runs often emit an empty batch; still schedule downstream by default. */
|
|
4212
4021
|
continueWhenEmptyOutput = true;
|
|
4213
4022
|
constructor(name = "Manual trigger", defaultItemsOrId, idOrOptions) {
|
|
4214
4023
|
this.name = name;
|
|
@@ -4426,6 +4235,39 @@ let WebhookTriggerNode = class WebhookTriggerNode$1 {
|
|
|
4426
4235
|
};
|
|
4427
4236
|
WebhookTriggerNode = __decorate([node({ packageName: "@codemation/core-nodes" })], WebhookTriggerNode);
|
|
4428
4237
|
|
|
4238
|
+
//#endregion
|
|
4239
|
+
//#region ../core-nodes/src/nodes/schedulePollingTrigger.ts
|
|
4240
|
+
const schedulePollingTrigger = definePollingTrigger({
|
|
4241
|
+
key: "schedule.interval",
|
|
4242
|
+
packageName: "@codemation/core-nodes",
|
|
4243
|
+
title: "Run on schedule",
|
|
4244
|
+
description: "Emit one tick item on every poll cycle.",
|
|
4245
|
+
icon: "lucide:clock",
|
|
4246
|
+
pollIntervalMs: 6e4,
|
|
4247
|
+
initialState() {
|
|
4248
|
+
return { tick: 0 };
|
|
4249
|
+
},
|
|
4250
|
+
poll({ state }) {
|
|
4251
|
+
const tick = (state ?? { tick: 0 }).tick + 1;
|
|
4252
|
+
return {
|
|
4253
|
+
items: [{ json: {
|
|
4254
|
+
firedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4255
|
+
tick
|
|
4256
|
+
} }],
|
|
4257
|
+
nextState: { tick }
|
|
4258
|
+
};
|
|
4259
|
+
},
|
|
4260
|
+
execute(items) {
|
|
4261
|
+
return { main: items };
|
|
4262
|
+
},
|
|
4263
|
+
testItems() {
|
|
4264
|
+
return [{ json: {
|
|
4265
|
+
firedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4266
|
+
tick: 0
|
|
4267
|
+
} }];
|
|
4268
|
+
}
|
|
4269
|
+
});
|
|
4270
|
+
|
|
4429
4271
|
//#endregion
|
|
4430
4272
|
//#region ../core-nodes/src/nodes/ConnectionCredentialNode.ts
|
|
4431
4273
|
let ConnectionCredentialNode = class ConnectionCredentialNode$1 {
|
|
@@ -4613,16 +4455,6 @@ const collectionDeleteNode = defineNode({
|
|
|
4613
4455
|
function resolveSubjectField(field, item) {
|
|
4614
4456
|
return typeof field === "function" ? field({ item }) : field;
|
|
4615
4457
|
}
|
|
4616
|
-
/**
|
|
4617
|
-
* Auto-detecting inbox approval node.
|
|
4618
|
-
*
|
|
4619
|
-
* Uses `ctx.resolve(InboxChannelResolverToken)` to pick the right inbox channel
|
|
4620
|
-
* at runtime:
|
|
4621
|
-
* - In managed mode (PairingConfig present): routes to the control-plane inbox.
|
|
4622
|
-
* - Otherwise: routes to the local inbox.
|
|
4623
|
-
*
|
|
4624
|
-
* Authors use this node directly; no extra wiring needed per deployment mode.
|
|
4625
|
-
*/
|
|
4626
4458
|
const inboxApproval = defineHumanApprovalNode({
|
|
4627
4459
|
key: "inbox.approval",
|
|
4628
4460
|
title: "Inbox Approval",
|