@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
1
|
const require_chunk = require('./chunk-BaqVhFee.cjs');
|
|
2
|
-
const require_analyzeInvoiceNode = require('./analyzeInvoiceNode-
|
|
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",
|