@fevex/core 0.1.0-alpha.7 → 0.1.0-alpha.9
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/README.md +49 -0
- package/dist/agents/index.d.mts +2 -2
- package/dist/artifacts/index.d.mts +1 -1
- package/dist/channels/index.d.mts +1 -1
- package/dist/evals/index.d.mts +4 -4
- package/dist/{fevex-CVMLnZsk.d.mts → fevex-WBvRWSpy.d.mts} +20 -10
- package/dist/http/index.d.mts +4 -4
- package/dist/{index-CzAi2Y_Z.d.mts → index-3bFNzCiY.d.mts} +154 -4
- package/dist/{index-CbACw6xZ.d.mts → index-BLgiXQK4.d.mts} +2 -2
- package/dist/{index-5p3q2yIp.d.mts → index-BbwT5XxQ.d.mts} +94 -4
- package/dist/{index-Df84VWmN.d.mts → index-CL7mu_x9.d.mts} +1 -1
- package/dist/{index-BTD1c4Ys.d.mts → index-CmFBB4Px.d.mts} +3 -3
- package/dist/{index-B0AdFbgr.d.mts → index-D4mWV2zG.d.mts} +2 -2
- package/dist/{index-CpLDt6Rw.d.mts → index-D7FoZ7aQ.d.mts} +3 -3
- package/dist/{index-BgKFd9yw.d.mts → index-D7r0puBi.d.mts} +15 -2
- package/dist/{index-1cHr7KB_.d.mts → index-D8lCFkc7.d.mts} +4 -4
- package/dist/{index-CseuTR-F.d.mts → index-DgSxJZo4.d.mts} +3 -3
- package/dist/index.d.mts +13 -13
- package/dist/index.mjs +655 -78
- package/dist/knowledge/index.d.mts +1 -1
- package/dist/models/index.d.mts +2 -2
- package/dist/observability/index.d.mts +1 -1
- package/dist/policies/index.d.mts +1 -1
- package/dist/runtime/index.d.mts +2 -2
- package/dist/runtime/index.mjs +172 -1
- package/dist/sandbox/index.d.mts +1 -1
- package/dist/teams/index.d.mts +1 -1
- package/dist/testing/index.d.mts +6 -6
- package/dist/tools/index.d.mts +3 -3
- package/dist/tools/index.mjs +221 -1
- package/dist/{types-B8laW1hM.d.mts → types-CckDlhYo.d.mts} +15 -1
- package/dist/workflows/index.d.mts +1 -1
- package/package.json +1 -1
- package/dist/exact-definition-NHBBFlFR.d.mts +0 -4
package/dist/index.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import { n as RunPausedError, t as FevexRunError } from "./run-error-Cr17wPGH.mj
|
|
|
4
4
|
import { ChannelError, deliverChannelRun, handleChannelInput } from "./channels/index.mjs";
|
|
5
5
|
import { InMemoryMemoryStore, createAgentsMdContextProvider, createMemoryContextProvider, defineContextProvider, defineSkill, isProgressiveSkillProvider, loadSkillFile, loadSkillFiles } from "./knowledge/index.mjs";
|
|
6
6
|
import { n as teamAsWorkflow, t as defineTeam } from "./teams-Ctz676G4.mjs";
|
|
7
|
-
import { InMemoryRunStore, isDurableRunStore, isRetainableRunStore, validateListEventsOptions, validatePurgeRunsOptions } from "./runtime/index.mjs";
|
|
8
|
-
import { FEVEX_JSON_SCHEMA_PROFILE_VERSION, IntegrationError, compileFevexJsonSchema, defineConnection, defineTool, toToolSpec, validateFevexJsonSchemaProfile } from "./tools/index.mjs";
|
|
7
|
+
import { InMemoryRunStore, createDigestContextCompactor, createModelContextCompactor, estimateContextTokens, isDurableRunStore, isRetainableRunStore, resolveContextBudget, validateListEventsOptions, validatePurgeRunsOptions } from "./runtime/index.mjs";
|
|
8
|
+
import { FEVEX_JSON_SCHEMA_PROFILE_VERSION, InMemoryToolEmbeddingCache, IntegrationError, buildToolEmbeddingIndex, compileFevexJsonSchema, cosineSimilarity, defineConnection, defineTool, schemaKeywords, searchToolEmbeddingIndex, toToolSpec, toolEmbeddingKey, toolEmbeddingText, validateFevexJsonSchemaProfile } from "./tools/index.mjs";
|
|
9
9
|
import { _ as serializeValue, a as assertTokenBudget, c as eventUsage, d as remainingDeadlineMs, f as remainingOutputTokens, g as serializeJsonValue, h as toErrorMessage, i as assertContinuationBudget, l as isTimeoutError, m as runSignal, n as abortable, o as cancellationReason, p as resumeDeadlineSignal, r as addUsage, s as deadlineAtFromMs, u as minDeadline, v as toJsonValue } from "./run-support-CmZ6Xo6H.mjs";
|
|
10
10
|
import { t as readModelStream } from "./model-stream-JQdHDDcj.mjs";
|
|
11
11
|
import "./models/index.mjs";
|
|
@@ -67,6 +67,139 @@ async function validateSchema(schema, value, errorMessage) {
|
|
|
67
67
|
return result.value;
|
|
68
68
|
}
|
|
69
69
|
//#endregion
|
|
70
|
+
//#region src/internal/tool-search.ts
|
|
71
|
+
function resolveToolSearchOptions(options, label, defaultCache) {
|
|
72
|
+
if (options !== void 0 && (typeof options !== "object" || options === null || Array.isArray(options))) throw new TypeError(`${label} must be an object`);
|
|
73
|
+
const mode = options?.mode ?? "auto";
|
|
74
|
+
if (mode !== "auto" && mode !== "on" && mode !== "off") throw new TypeError(`${label}.mode must be auto, on or off`);
|
|
75
|
+
const threshold = options?.threshold ?? 30;
|
|
76
|
+
if (!Number.isSafeInteger(threshold) || threshold < 1) throw new TypeError(`${label}.threshold must be a positive safe integer`);
|
|
77
|
+
const tokenThreshold = options?.tokenThreshold;
|
|
78
|
+
if (tokenThreshold !== void 0 && (!Number.isSafeInteger(tokenThreshold) || tokenThreshold < 1)) throw new TypeError(`${label}.tokenThreshold must be a positive safe integer`);
|
|
79
|
+
const limit = options?.limit ?? 5;
|
|
80
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 20) throw new TypeError(`${label}.limit must be a safe integer between 1 and 20`);
|
|
81
|
+
const pinned = options?.pinned ?? [];
|
|
82
|
+
if (!Array.isArray(pinned) || pinned.some((name) => typeof name !== "string" || !name.trim())) throw new TypeError(`${label}.pinned must contain non-empty tool names`);
|
|
83
|
+
const normalizedPinned = [...new Set(pinned.map((name) => name.trim()))];
|
|
84
|
+
if (normalizedPinned.length !== pinned.length) throw new TypeError(`${label}.pinned must contain unique tool names`);
|
|
85
|
+
const semantic = options?.semantic;
|
|
86
|
+
return {
|
|
87
|
+
mode,
|
|
88
|
+
threshold,
|
|
89
|
+
...tokenThreshold === void 0 ? {} : { tokenThreshold },
|
|
90
|
+
limit,
|
|
91
|
+
pinned: normalizedPinned,
|
|
92
|
+
...semantic === void 0 ? {} : { semantic: resolveSemanticOptions(semantic, `${label}.semantic`, defaultCache) }
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function serializeToolSearchOptions(options) {
|
|
96
|
+
return {
|
|
97
|
+
mode: options.mode,
|
|
98
|
+
threshold: options.threshold,
|
|
99
|
+
...options.tokenThreshold === void 0 ? {} : { tokenThreshold: options.tokenThreshold },
|
|
100
|
+
limit: options.limit,
|
|
101
|
+
pinned: [...options.pinned]
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function reciprocalRankFusionCandidates(lexical, semantic, limit, rankConstant = 60) {
|
|
105
|
+
if (!Number.isSafeInteger(limit) || limit < 1) throw new TypeError("Tool search fusion limit must be a positive safe integer");
|
|
106
|
+
if (!Number.isSafeInteger(rankConstant) || rankConstant < 1) throw new TypeError("Tool search fusion rankConstant must be a positive safe integer");
|
|
107
|
+
const scores = /* @__PURE__ */ new Map();
|
|
108
|
+
const add = (tools) => {
|
|
109
|
+
for (let index = 0; index < tools.length; index += 1) {
|
|
110
|
+
const tool = tools[index];
|
|
111
|
+
const entry = scores.get(tool.name) ?? {
|
|
112
|
+
tool,
|
|
113
|
+
score: 0
|
|
114
|
+
};
|
|
115
|
+
entry.score += 1 / (rankConstant + index + 1);
|
|
116
|
+
scores.set(tool.name, entry);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
add(lexical);
|
|
120
|
+
add(semantic);
|
|
121
|
+
return [...scores.values()].sort((left, right) => right.score - left.score || left.tool.name.localeCompare(right.tool.name)).slice(0, limit);
|
|
122
|
+
}
|
|
123
|
+
function resolveSemanticOptions(semantic, label, defaultCache) {
|
|
124
|
+
if (typeof semantic !== "object" || semantic === null || Array.isArray(semantic)) throw new TypeError(`${label} must be an object`);
|
|
125
|
+
const embeddings = semantic.embeddings;
|
|
126
|
+
if (typeof embeddings !== "object" || embeddings === null || typeof embeddings.id !== "string" || !embeddings.id.trim() || typeof embeddings.embed !== "function") throw new TypeError(`${label}.embeddings must implement EmbeddingModel`);
|
|
127
|
+
if (embeddings.dimensions !== void 0 && (!Number.isSafeInteger(embeddings.dimensions) || embeddings.dimensions < 1)) throw new TypeError(`${label}.embeddings.dimensions must be a positive safe integer`);
|
|
128
|
+
const cache = semantic.cache ?? defaultCache;
|
|
129
|
+
if (cache === void 0 || typeof cache.get !== "function" || typeof cache.set !== "function") throw new TypeError(`${label}.cache must implement get and set`);
|
|
130
|
+
const topK = semantic.topK ?? 20;
|
|
131
|
+
if (!Number.isSafeInteger(topK) || topK < 1) throw new TypeError(`${label}.topK must be a positive safe integer`);
|
|
132
|
+
const minScore = semantic.minScore ?? 0;
|
|
133
|
+
if (typeof minScore !== "number" || !Number.isFinite(minScore) || minScore < -1 || minScore > 1) throw new TypeError(`${label}.minScore must be a finite number between -1 and 1`);
|
|
134
|
+
const reranker = semantic.reranker;
|
|
135
|
+
if (reranker !== void 0 && (typeof reranker !== "object" || reranker === null || typeof reranker.rerank !== "function")) throw new TypeError(`${label}.reranker must implement rerank`);
|
|
136
|
+
return {
|
|
137
|
+
embeddings,
|
|
138
|
+
cache,
|
|
139
|
+
topK,
|
|
140
|
+
minScore,
|
|
141
|
+
...reranker === void 0 ? {} : { reranker }
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function shouldUseToolSearch(options, tools) {
|
|
145
|
+
if (options.mode === "off" || tools.length === 0) return false;
|
|
146
|
+
if (options.mode === "on") return true;
|
|
147
|
+
if (tools.length > options.threshold) return true;
|
|
148
|
+
if (options.tokenThreshold === void 0) return false;
|
|
149
|
+
return Math.ceil(JSON.stringify(tools).length / 4) > options.tokenThreshold;
|
|
150
|
+
}
|
|
151
|
+
const searchIndex = /* @__PURE__ */ new WeakMap();
|
|
152
|
+
function indexedTools(tools) {
|
|
153
|
+
const cached = searchIndex.get(tools);
|
|
154
|
+
if (cached !== void 0) return cached;
|
|
155
|
+
const index = tools.map((tool) => {
|
|
156
|
+
const name = tool.name.toLowerCase();
|
|
157
|
+
const description = (tool.description ?? "").toLowerCase();
|
|
158
|
+
return {
|
|
159
|
+
tool,
|
|
160
|
+
name,
|
|
161
|
+
nameTerms: new Set(name.split(/[^a-z0-9]+/).filter(Boolean)),
|
|
162
|
+
description,
|
|
163
|
+
descriptionTerms: new Set(description.split(/[^a-z0-9]+/).filter(Boolean)),
|
|
164
|
+
schema: JSON.stringify(tool.inputSchema ?? {}).toLowerCase()
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
searchIndex.set(tools, index);
|
|
168
|
+
return index;
|
|
169
|
+
}
|
|
170
|
+
function searchToolSpecs(tools, query, limit) {
|
|
171
|
+
const terms = [...new Set(query.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 1))];
|
|
172
|
+
if (terms.length === 0) return [];
|
|
173
|
+
return indexedTools(tools).map(({ tool, name, nameTerms, description, descriptionTerms, schema }) => {
|
|
174
|
+
let score = 0;
|
|
175
|
+
for (const term of terms) {
|
|
176
|
+
if (name === term) score += 100;
|
|
177
|
+
else if (nameTerms.has(term)) score += 40;
|
|
178
|
+
else if (name.startsWith(term)) score += 30;
|
|
179
|
+
else if (name.includes(term)) score += 20;
|
|
180
|
+
if (descriptionTerms.has(term)) score += 8;
|
|
181
|
+
else if (description.includes(term)) score += 5;
|
|
182
|
+
if (schema.includes(term)) score += 1;
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
tool,
|
|
186
|
+
score
|
|
187
|
+
};
|
|
188
|
+
}).filter(({ score }) => score > 0).sort((left, right) => right.score - left.score || left.tool.name.localeCompare(right.tool.name)).slice(0, limit).map(({ tool }) => tool);
|
|
189
|
+
}
|
|
190
|
+
function readToolSearchInput(input) {
|
|
191
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) throw new TypeError("Tool search input must be an object");
|
|
192
|
+
const record = input;
|
|
193
|
+
const query = record.query;
|
|
194
|
+
const limit = record.limit;
|
|
195
|
+
if (typeof query !== "string" || !query.trim()) throw new TypeError("Tool search query must be a non-empty string");
|
|
196
|
+
if (limit !== void 0 && (typeof limit !== "number" || !Number.isSafeInteger(limit) || limit < 1)) throw new TypeError("Tool search limit must be a positive safe integer");
|
|
197
|
+
return {
|
|
198
|
+
query: query.trim(),
|
|
199
|
+
...limit === void 0 ? {} : { limit }
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
70
203
|
//#region src/internal/configuration.ts
|
|
71
204
|
const REASONING_EFFORTS = /* @__PURE__ */ new Set([
|
|
72
205
|
"provider-default",
|
|
@@ -190,6 +323,7 @@ function expandConnection(connection) {
|
|
|
190
323
|
};
|
|
191
324
|
assertConfiguration(policy?.inputSchema === void 0 || isJsonObject(policy.inputSchema), "INVALID_TOOL", `Input schema for connection tool "${remoteName}" must be a JSON object`);
|
|
192
325
|
assertConfiguration(policy?.outputSchema === void 0 || isJsonObject(policy.outputSchema), "INVALID_TOOL", `Output schema for connection tool "${remoteName}" must be a JSON object`);
|
|
326
|
+
assertConfiguration(policy?.maxResultBytes === void 0 || policy.maxResultBytes === false || Number.isSafeInteger(policy.maxResultBytes) && policy.maxResultBytes > 0, "INVALID_TOOL", `maxResultBytes for connection tool "${remoteName}" must be a positive safe integer or false`);
|
|
193
327
|
return {
|
|
194
328
|
name: namespacedToolName(connection.name, remoteName),
|
|
195
329
|
...policy?.label === void 0 ? {} : { label: policy.label },
|
|
@@ -201,6 +335,7 @@ function expandConnection(connection) {
|
|
|
201
335
|
...policy?.idempotency === void 0 ? {} : { idempotency: policy.idempotency },
|
|
202
336
|
...policy?.retry === void 0 ? {} : { retry: policy.retry },
|
|
203
337
|
...policy?.credentials === void 0 ? {} : { credentials: policy.credentials },
|
|
338
|
+
...policy?.maxResultBytes === void 0 ? {} : { maxResultBytes: policy.maxResultBytes },
|
|
204
339
|
source,
|
|
205
340
|
async resolve(context) {
|
|
206
341
|
try {
|
|
@@ -300,6 +435,24 @@ function createComposition(config) {
|
|
|
300
435
|
assertConfiguration(config.tools === void 0 || Array.isArray(config.tools), "INVALID_CONFIG", "Fevex config \"tools\" must be an array");
|
|
301
436
|
assertConfiguration(config.connections === void 0 || Array.isArray(config.connections), "INVALID_CONFIG", "Fevex config \"connections\" must be an array");
|
|
302
437
|
assertConfiguration(config.contextProviders === void 0 || Array.isArray(config.contextProviders), "INVALID_CONFIG", "Fevex config \"contextProviders\" must be an array");
|
|
438
|
+
if (config.contextBudget !== void 0) try {
|
|
439
|
+
resolveContextBudget(config.contextBudget, "Fevex config \"contextBudget\"");
|
|
440
|
+
} catch (error) {
|
|
441
|
+
throw configurationError("INVALID_CONFIG", error.message);
|
|
442
|
+
}
|
|
443
|
+
assertConfiguration(config.toolSearchCache === void 0 || isRecord(config.toolSearchCache) && typeof config.toolSearchCache.get === "function" && typeof config.toolSearchCache.set === "function", "INVALID_CONFIG", "Fevex config \"toolSearchCache\" must implement get and set");
|
|
444
|
+
const defaultToolSearchCache = config.toolSearchCache ?? new InMemoryToolEmbeddingCache();
|
|
445
|
+
if (config.toolSearch !== void 0) try {
|
|
446
|
+
resolveToolSearchOptions(config.toolSearch, "Fevex config \"toolSearch\"", defaultToolSearchCache);
|
|
447
|
+
} catch (error) {
|
|
448
|
+
throw configurationError("INVALID_CONFIG", error.message);
|
|
449
|
+
}
|
|
450
|
+
if (config.compaction !== void 0) {
|
|
451
|
+
assertConfiguration(typeof config.compaction === "object" && config.compaction !== null && !Array.isArray(config.compaction), "INVALID_CONFIG", "Fevex config \"compaction\" must be an object");
|
|
452
|
+
assertConfiguration(config.compaction.compactor === void 0 || isRecord(config.compaction.compactor) && typeof config.compaction.compactor.compact === "function", "INVALID_CONFIG", "Fevex config \"compaction.compactor\" must implement compact");
|
|
453
|
+
assertConfiguration(config.compaction.estimateTokens === void 0 || typeof config.compaction.estimateTokens === "function", "INVALID_CONFIG", "Fevex config \"compaction.estimateTokens\" must be a function");
|
|
454
|
+
assertConfiguration(config.compaction.redactSummary === void 0 || typeof config.compaction.redactSummary === "function", "INVALID_CONFIG", "Fevex config \"compaction.redactSummary\" must be a function");
|
|
455
|
+
}
|
|
303
456
|
assertConfiguration(config.memoryStore === void 0 || isMemoryStore(config.memoryStore), "INVALID_CONFIG", "Fevex config \"memoryStore\" must implement MemoryStore");
|
|
304
457
|
assertConfiguration(config.onEvent === void 0 || typeof config.onEvent === "function", "INVALID_CONFIG", "Fevex config \"onEvent\" must be a function");
|
|
305
458
|
assertConfiguration(config.runStore === void 0 || isRunStore(config.runStore), "INVALID_CONFIG", "Fevex config \"runStore\" must implement RunStore");
|
|
@@ -385,6 +538,7 @@ function createComposition(config) {
|
|
|
385
538
|
assertConfiguration(definition.credentials === void 0 || Array.isArray(definition.credentials) && definition.credentials.every((value) => typeof value === "string" && value.trim()), "INVALID_TOOL", `Tool "${name}" credentials must be non-empty strings`);
|
|
386
539
|
assertSandboxCapabilities(definition.sandbox, name);
|
|
387
540
|
assertConfiguration(definition.sandbox === void 0 || config.sandbox !== void 0, "INVALID_TOOL", `Tool "${name}" requires Fevex config "sandbox"`);
|
|
541
|
+
assertConfiguration(definition.maxResultBytes === void 0 || definition.maxResultBytes === false || Number.isSafeInteger(definition.maxResultBytes) && definition.maxResultBytes > 0, "INVALID_TOOL", `Tool "${name}" maxResultBytes must be a positive safe integer or false`);
|
|
388
542
|
if (definition.retry !== void 0) assertConfiguration(isRecord(definition.retry) && Number.isInteger(definition.retry.maxAttempts) && definition.retry.maxAttempts >= 1 && Number.isInteger(definition.retry.backoffMs) && definition.retry.backoffMs >= 0 && (definition.retry.maxBackoffMs === void 0 || Number.isInteger(definition.retry.maxBackoffMs) && definition.retry.maxBackoffMs >= 0), "INVALID_TOOL", `Tool "${name}" retry is invalid`);
|
|
389
543
|
if (definition.inputSchema !== void 0) assertConfiguration(isStandardSchema(definition.inputSchema), "INVALID_TOOL", `Input schema for tool "${name}" must implement Standard Schema`);
|
|
390
544
|
if (definition.inputSchema !== void 0) assertConfiguration(isStandardSchema(definition.inputSchema), "INVALID_AGENT", `Input schema for agent "${name}" must implement Standard Schema`);
|
|
@@ -404,6 +558,7 @@ function createComposition(config) {
|
|
|
404
558
|
...definition.idempotency === void 0 ? {} : { idempotency: definition.idempotency },
|
|
405
559
|
...definition.retry === void 0 ? {} : { retry: { ...definition.retry } },
|
|
406
560
|
...definition.credentials === void 0 ? {} : { credentials: [...definition.credentials] },
|
|
561
|
+
...definition.maxResultBytes === void 0 ? {} : { maxResultBytes: definition.maxResultBytes },
|
|
407
562
|
...definition.sandbox === void 0 ? {} : { sandbox: copySandboxCapabilities(definition.sandbox) },
|
|
408
563
|
...definition.source === void 0 ? {} : { source: { ...definition.source } },
|
|
409
564
|
...definition.resolve === void 0 ? {} : { resolve: definition.resolve },
|
|
@@ -475,12 +630,23 @@ function createComposition(config) {
|
|
|
475
630
|
assertConfiguration(definition.approvalMode === void 0 || definition.approvalMode === "pause" || definition.approvalMode === "deny", "INVALID_AGENT", `Agent "${name}" approvalMode is invalid`);
|
|
476
631
|
assertConfiguration(definition.toolChoice === void 0 || definition.toolChoice === "auto" || definition.toolChoice === "required" || definition.toolChoice === "none" || isRecord(definition.toolChoice) && typeof definition.toolChoice.name === "string" && Boolean(definition.toolChoice.name.trim()), "INVALID_AGENT", `Agent "${name}" toolChoice is invalid`);
|
|
477
632
|
assertConfiguration(definition.limits === void 0 || isRecord(definition.limits), "INVALID_AGENT", `Agent "${name}" limits must be an object`);
|
|
633
|
+
if (definition.contextBudget !== void 0) try {
|
|
634
|
+
resolveContextBudget(definition.contextBudget, `Agent "${name}" contextBudget`);
|
|
635
|
+
} catch (error) {
|
|
636
|
+
throw configurationError("INVALID_AGENT", error.message);
|
|
637
|
+
}
|
|
638
|
+
if (definition.toolSearch !== void 0) try {
|
|
639
|
+
resolveToolSearchOptions(definition.toolSearch, `Agent "${name}" toolSearch`, defaultToolSearchCache);
|
|
640
|
+
} catch (error) {
|
|
641
|
+
throw configurationError("INVALID_AGENT", error.message);
|
|
642
|
+
}
|
|
478
643
|
if (definition.outputSchema !== void 0) assertConfiguration(isStandardSchema(definition.outputSchema), "INVALID_AGENT", `Output schema for agent "${name}" must implement Standard Schema`);
|
|
479
644
|
const rawLimits = definition.limits;
|
|
480
645
|
assertLimit(rawLimits?.maxSteps, "maxSteps", 1, name);
|
|
481
646
|
assertLimit(rawLimits?.maxToolCalls, "maxToolCalls", 0, name);
|
|
482
647
|
assertLimit(rawLimits?.maxInputTokens, "maxInputTokens", 1, name, true);
|
|
483
648
|
assertLimit(rawLimits?.maxOutputTokens, "maxOutputTokens", 1, name, true);
|
|
649
|
+
assertLimit(rawLimits?.maxToolResultBytes, "maxToolResultBytes", 1, name, true);
|
|
484
650
|
agents.set(name, {
|
|
485
651
|
name,
|
|
486
652
|
instructions: definition.instructions,
|
|
@@ -498,13 +664,19 @@ function createComposition(config) {
|
|
|
498
664
|
...definition.toolChoice === void 0 ? {} : { toolChoice: typeof definition.toolChoice === "string" ? definition.toolChoice : { name: definition.toolChoice.name } },
|
|
499
665
|
...definition.elicitation === void 0 ? {} : { elicitation: definition.elicitation },
|
|
500
666
|
...definition.approvalMode === void 0 ? {} : { approvalMode: definition.approvalMode },
|
|
667
|
+
...definition.contextBudget === void 0 ? {} : { contextBudget: { ...definition.contextBudget } },
|
|
668
|
+
...definition.toolSearch === void 0 ? {} : { toolSearch: {
|
|
669
|
+
...definition.toolSearch,
|
|
670
|
+
...definition.toolSearch.pinned === void 0 ? {} : { pinned: [...definition.toolSearch.pinned] }
|
|
671
|
+
} },
|
|
501
672
|
...definition.inputSchema === void 0 ? {} : { inputSchema: definition.inputSchema },
|
|
502
673
|
...definition.outputSchema === void 0 ? {} : { outputSchema: definition.outputSchema },
|
|
503
674
|
...rawLimits === void 0 ? {} : { limits: {
|
|
504
675
|
...rawLimits.maxSteps === void 0 ? {} : { maxSteps: rawLimits.maxSteps },
|
|
505
676
|
...rawLimits.maxToolCalls === void 0 ? {} : { maxToolCalls: rawLimits.maxToolCalls },
|
|
506
677
|
...rawLimits.maxInputTokens === void 0 ? {} : { maxInputTokens: rawLimits.maxInputTokens },
|
|
507
|
-
...rawLimits.maxOutputTokens === void 0 ? {} : { maxOutputTokens: rawLimits.maxOutputTokens }
|
|
678
|
+
...rawLimits.maxOutputTokens === void 0 ? {} : { maxOutputTokens: rawLimits.maxOutputTokens },
|
|
679
|
+
...rawLimits.maxToolResultBytes === void 0 ? {} : { maxToolResultBytes: rawLimits.maxToolResultBytes }
|
|
508
680
|
} }
|
|
509
681
|
});
|
|
510
682
|
}
|
|
@@ -541,6 +713,7 @@ function createComposition(config) {
|
|
|
541
713
|
assertLimit(rawLimits?.maxToolCalls, "maxToolCalls", 0, name, false, "Workflow", "INVALID_WORKFLOW");
|
|
542
714
|
assertLimit(rawLimits?.maxInputTokens, "maxInputTokens", 1, name, true, "Workflow", "INVALID_WORKFLOW");
|
|
543
715
|
assertLimit(rawLimits?.maxOutputTokens, "maxOutputTokens", 1, name, true, "Workflow", "INVALID_WORKFLOW");
|
|
716
|
+
assertLimit(rawLimits?.maxToolResultBytes, "maxToolResultBytes", 1, name, false, "Workflow", "INVALID_WORKFLOW");
|
|
544
717
|
assertConfiguration(!workflows.has(name), "DUPLICATE_WORKFLOW", `Workflow "${name}" is duplicated`);
|
|
545
718
|
workflows.set(name, {
|
|
546
719
|
name,
|
|
@@ -552,7 +725,8 @@ function createComposition(config) {
|
|
|
552
725
|
...rawLimits.maxSteps === void 0 ? {} : { maxSteps: rawLimits.maxSteps },
|
|
553
726
|
...rawLimits.maxToolCalls === void 0 ? {} : { maxToolCalls: rawLimits.maxToolCalls },
|
|
554
727
|
...rawLimits.maxInputTokens === void 0 ? {} : { maxInputTokens: rawLimits.maxInputTokens },
|
|
555
|
-
...rawLimits.maxOutputTokens === void 0 ? {} : { maxOutputTokens: rawLimits.maxOutputTokens }
|
|
728
|
+
...rawLimits.maxOutputTokens === void 0 ? {} : { maxOutputTokens: rawLimits.maxOutputTokens },
|
|
729
|
+
...rawLimits.maxToolResultBytes === void 0 ? {} : { maxToolResultBytes: rawLimits.maxToolResultBytes }
|
|
556
730
|
} },
|
|
557
731
|
run: definition.run
|
|
558
732
|
});
|
|
@@ -591,6 +765,7 @@ function createComposition(config) {
|
|
|
591
765
|
assertLimit(rawLimits?.maxToolCalls, "maxToolCalls", 0, name, false, "Team", "INVALID_TEAM");
|
|
592
766
|
assertLimit(rawLimits?.maxInputTokens, "maxInputTokens", 1, name, true, "Team", "INVALID_TEAM");
|
|
593
767
|
assertLimit(rawLimits?.maxOutputTokens, "maxOutputTokens", 1, name, true, "Team", "INVALID_TEAM");
|
|
768
|
+
assertLimit(rawLimits?.maxToolResultBytes, "maxToolResultBytes", 1, name, false, "Team", "INVALID_TEAM");
|
|
594
769
|
assertConfiguration(!teams.has(name), "DUPLICATE_TEAM", `Team "${name}" is duplicated`);
|
|
595
770
|
assertConfiguration(!workflows.has(name), "INVALID_TEAM", `Team "${name}" conflicts with a workflow of the same name`);
|
|
596
771
|
const team = {
|
|
@@ -606,7 +781,8 @@ function createComposition(config) {
|
|
|
606
781
|
...rawLimits.maxSteps === void 0 ? {} : { maxSteps: rawLimits.maxSteps },
|
|
607
782
|
...rawLimits.maxToolCalls === void 0 ? {} : { maxToolCalls: rawLimits.maxToolCalls },
|
|
608
783
|
...rawLimits.maxInputTokens === void 0 ? {} : { maxInputTokens: rawLimits.maxInputTokens },
|
|
609
|
-
...rawLimits.maxOutputTokens === void 0 ? {} : { maxOutputTokens: rawLimits.maxOutputTokens }
|
|
784
|
+
...rawLimits.maxOutputTokens === void 0 ? {} : { maxOutputTokens: rawLimits.maxOutputTokens },
|
|
785
|
+
...rawLimits.maxToolResultBytes === void 0 ? {} : { maxToolResultBytes: rawLimits.maxToolResultBytes }
|
|
610
786
|
} },
|
|
611
787
|
run: definition.run
|
|
612
788
|
};
|
|
@@ -621,6 +797,17 @@ function createComposition(config) {
|
|
|
621
797
|
teams,
|
|
622
798
|
tools,
|
|
623
799
|
contextProviders,
|
|
800
|
+
contextBudget: config.contextBudget === void 0 ? void 0 : { ...config.contextBudget },
|
|
801
|
+
toolSearch: config.toolSearch === void 0 ? void 0 : {
|
|
802
|
+
...config.toolSearch,
|
|
803
|
+
...config.toolSearch.pinned === void 0 ? {} : { pinned: [...config.toolSearch.pinned] }
|
|
804
|
+
},
|
|
805
|
+
toolSearchCache: defaultToolSearchCache,
|
|
806
|
+
compaction: {
|
|
807
|
+
compactor: config.compaction?.compactor ?? createDigestContextCompactor(),
|
|
808
|
+
estimateTokens: config.compaction?.estimateTokens ?? estimateContextTokens,
|
|
809
|
+
...config.compaction?.redactSummary === void 0 ? {} : { redactSummary: config.compaction.redactSummary }
|
|
810
|
+
},
|
|
624
811
|
memoryStore: config.memoryStore ?? (usesMemory ? new InMemoryMemoryStore() : void 0),
|
|
625
812
|
runStore: config.runStore ?? new InMemoryRunStore(),
|
|
626
813
|
credentialStore: config.credentialStore,
|
|
@@ -651,6 +838,10 @@ const LEASE_RENEW_MS = 1e4;
|
|
|
651
838
|
const ELICIT_TOOL_NAME = "fevex__elicit";
|
|
652
839
|
/** Reserved name of the internal progressive skill loader exposed to the model. */
|
|
653
840
|
const LOAD_SKILL_TOOL_NAME = "fevex__load_skill";
|
|
841
|
+
/** Reserved name of the internal on-demand tool search exposed to the model. */
|
|
842
|
+
const SEARCH_TOOLS_TOOL_NAME = "fevex__search_tools";
|
|
843
|
+
/** Default safety cap for one serialized tool result. */
|
|
844
|
+
const DEFAULT_MAX_TOOL_RESULT_BYTES = 1048576;
|
|
654
845
|
//#endregion
|
|
655
846
|
//#region src/internal/run-helpers.ts
|
|
656
847
|
function redact(message, secrets) {
|
|
@@ -778,6 +969,208 @@ const runErrorPayload = (error) => {
|
|
|
778
969
|
};
|
|
779
970
|
};
|
|
780
971
|
//#endregion
|
|
972
|
+
//#region src/internal/agent-internal-tools.ts
|
|
973
|
+
async function executeInternalTool(input) {
|
|
974
|
+
if (input.call.name === "fevex__search_tools") {
|
|
975
|
+
if (!input.searchEnabled) return void 0;
|
|
976
|
+
const search = readToolSearchInput(input.input);
|
|
977
|
+
const limit = Math.min(search.limit ?? input.toolSearch.limit, input.toolSearch.limit);
|
|
978
|
+
const semantic = input.toolSearch.semantic;
|
|
979
|
+
const candidateLimit = Math.max(limit, semantic?.topK ?? limit);
|
|
980
|
+
const lexical = searchToolSpecs(input.tools, search.query, candidateLimit);
|
|
981
|
+
let matches = lexical;
|
|
982
|
+
if (semantic !== void 0) try {
|
|
983
|
+
if (input.semanticState.index === void 0 && !input.semanticState.attempted) {
|
|
984
|
+
input.semanticState.attempted = true;
|
|
985
|
+
input.semanticState.index = await buildToolEmbeddingIndex({
|
|
986
|
+
tools: input.tools,
|
|
987
|
+
embeddings: semantic.embeddings,
|
|
988
|
+
cache: semantic.cache,
|
|
989
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
if (input.semanticState.index !== void 0) {
|
|
993
|
+
const queryVector = (await semantic.embeddings.embed({
|
|
994
|
+
texts: [search.query],
|
|
995
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
996
|
+
}))[0];
|
|
997
|
+
if (!queryVector) throw new Error("Embedding model returned no query vector");
|
|
998
|
+
const semanticMatches = searchToolEmbeddingIndex(input.semanticState.index, queryVector, semantic.topK, semantic.minScore);
|
|
999
|
+
if (semanticMatches.length > 0) {
|
|
1000
|
+
const fusionLimit = semantic.reranker === void 0 ? limit : Math.max(limit, semantic.topK);
|
|
1001
|
+
const fused = reciprocalRankFusionCandidates(lexical, semanticMatches.map((match) => match.tool), fusionLimit);
|
|
1002
|
+
if (semantic.reranker !== void 0 && fused.length > 1) matches = (await rerankToolCandidates(semantic.reranker, search.query, fused, limit, input.signal) ?? fused).map((candidate) => candidate.tool);
|
|
1003
|
+
else matches = fused.map((candidate) => candidate.tool);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
if (input.signal?.aborted) throw error;
|
|
1008
|
+
matches = lexical;
|
|
1009
|
+
}
|
|
1010
|
+
const selected = matches.slice(0, limit);
|
|
1011
|
+
for (const match of selected) input.loadedTools.add(match.name);
|
|
1012
|
+
return { content: serializeValue({
|
|
1013
|
+
tools: selected.map((tool) => ({
|
|
1014
|
+
name: tool.name,
|
|
1015
|
+
...tool.description === void 0 ? {} : { description: tool.description }
|
|
1016
|
+
})),
|
|
1017
|
+
...selected.length === 0 ? { message: "No tools matched; try a different query." } : {}
|
|
1018
|
+
}, "Tool search result must be serializable") };
|
|
1019
|
+
}
|
|
1020
|
+
if (input.call.name === "fevex__load_skill") {
|
|
1021
|
+
if (input.progressiveSkills.length === 0) return void 0;
|
|
1022
|
+
const skillInput = readSkillInput(input.input);
|
|
1023
|
+
const skill = input.progressiveSkills.find((item) => item.name === skillInput.name);
|
|
1024
|
+
if (!skill) throw new Error(`Skill "${skillInput.name}" is not available`);
|
|
1025
|
+
try {
|
|
1026
|
+
return { content: (await abortable(() => skill.load(input.knowledgeContext, skillInput.resource), input.signal)).filter((block) => block.content.trim()).map((block) => `[Skill: ${skill.name}/${block.id}]\n${block.content}`).join("\n\n") };
|
|
1027
|
+
} catch (error) {
|
|
1028
|
+
throw new Error(`Skill provider "${skill.name}" failed: ${toErrorMessage(error)}`, { cause: error });
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
async function rerankToolCandidates(reranker, query, candidates, limit, signal) {
|
|
1033
|
+
try {
|
|
1034
|
+
const result = await reranker.rerank({
|
|
1035
|
+
query,
|
|
1036
|
+
tools: candidates,
|
|
1037
|
+
limit,
|
|
1038
|
+
...signal === void 0 ? {} : { signal }
|
|
1039
|
+
});
|
|
1040
|
+
if (!Array.isArray(result)) return void 0;
|
|
1041
|
+
const byName = new Map(candidates.map((candidate) => [candidate.tool.name, candidate]));
|
|
1042
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1043
|
+
const ordered = [];
|
|
1044
|
+
for (const candidate of result) {
|
|
1045
|
+
const name = candidate?.tool?.name;
|
|
1046
|
+
const original = typeof name === "string" ? byName.get(name) : void 0;
|
|
1047
|
+
if (!original || seen.has(name)) return void 0;
|
|
1048
|
+
seen.add(name);
|
|
1049
|
+
ordered.push(original);
|
|
1050
|
+
}
|
|
1051
|
+
return ordered.slice(0, limit);
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
if (signal?.aborted) throw error;
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
function readSkillInput(input) {
|
|
1058
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) throw new TypeError(`${LOAD_SKILL_TOOL_NAME} input must be an object`);
|
|
1059
|
+
const record = input;
|
|
1060
|
+
const name = record.name;
|
|
1061
|
+
const resource = record.resource;
|
|
1062
|
+
if (typeof name !== "string" || !name.trim()) throw new TypeError(`${LOAD_SKILL_TOOL_NAME} name must be a non-empty string`);
|
|
1063
|
+
if (resource !== void 0 && (typeof resource !== "string" || !resource.trim())) throw new TypeError(`${LOAD_SKILL_TOOL_NAME} resource must be a non-empty string`);
|
|
1064
|
+
return {
|
|
1065
|
+
name: name.trim(),
|
|
1066
|
+
...resource === void 0 ? {} : { resource: resource.trim() }
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
//#endregion
|
|
1070
|
+
//#region src/internal/context-compaction.ts
|
|
1071
|
+
/** Maximum summary characters copied into the context.compacted event. */
|
|
1072
|
+
const MAX_COMPACTION_EVENT_SUMMARY_CHARS = 4e3;
|
|
1073
|
+
/** Keeps the newest summary segments instead of cutting mid-sentence. */
|
|
1074
|
+
function capSummary(summary, maxChars) {
|
|
1075
|
+
if (summary.length <= maxChars) return summary;
|
|
1076
|
+
const tail = summary.slice(summary.length - maxChars + 1);
|
|
1077
|
+
const boundary = tail.indexOf("\n\n");
|
|
1078
|
+
return `…${boundary === -1 ? tail : tail.slice(boundary + 2)}`;
|
|
1079
|
+
}
|
|
1080
|
+
async function compactContext(input) {
|
|
1081
|
+
const estimateWith = (messages, includeTools) => {
|
|
1082
|
+
const value = input.estimateTokens({
|
|
1083
|
+
messages,
|
|
1084
|
+
...includeTools ? { tools: input.tools } : {}
|
|
1085
|
+
});
|
|
1086
|
+
if (!Number.isFinite(value) || value < 0) throw new TypeError("Context token estimator must return a non-negative finite number");
|
|
1087
|
+
return Math.ceil(value * input.contextBudget.safetyFactor);
|
|
1088
|
+
};
|
|
1089
|
+
const estimate = (messages) => estimateWith(messages, true);
|
|
1090
|
+
const estimated = estimate(input.messages);
|
|
1091
|
+
const usable = input.contextBudget.contextWindow - input.contextBudget.reserveOutputTokens;
|
|
1092
|
+
const threshold = Math.floor(usable * input.contextBudget.compactAt);
|
|
1093
|
+
if (input.allowCompaction === false || estimated <= threshold) {
|
|
1094
|
+
if (estimated > usable) throw new FevexRunError("CONTEXT_BUDGET_EXCEEDED", `Agent "${input.agentName}" context budget exceeded: ${estimated} tokens > ${usable} usable${input.allowCompaction === false ? " after compaction" : ""}.`, input.runId);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
const split = splitContextForCompaction(input.messages);
|
|
1098
|
+
if (split === void 0 || split.compactable.length === 0) {
|
|
1099
|
+
if (estimated > usable) throw new FevexRunError("CONTEXT_BUDGET_EXCEEDED", `Agent "${input.agentName}" context budget exceeded: ${estimated} tokens > ${usable} usable. The preserved system prefix and current turn cannot be compacted further.`, input.runId);
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
const preservedTokens = estimate([...split.prefix, ...split.currentTurn]);
|
|
1103
|
+
const available = Math.max(1, Math.min(input.contextBudget.targetTokens, usable - 1) - preservedTokens);
|
|
1104
|
+
const compactedMessages = validateCompactedMessages(await abortable(() => input.compactor.compact({
|
|
1105
|
+
agentName: input.agentName,
|
|
1106
|
+
sessionId: input.sessionId,
|
|
1107
|
+
messages: split.compactable,
|
|
1108
|
+
tools: input.tools,
|
|
1109
|
+
estimatedTokens: estimated,
|
|
1110
|
+
targetTokens: available,
|
|
1111
|
+
contextWindow: input.contextBudget.contextWindow,
|
|
1112
|
+
step: input.step,
|
|
1113
|
+
...input.context === void 0 ? {} : { context: input.context },
|
|
1114
|
+
signal: input.signal,
|
|
1115
|
+
estimateTokens: (messages) => estimateWith(messages, false)
|
|
1116
|
+
}), input.signal));
|
|
1117
|
+
const messages = [
|
|
1118
|
+
...split.prefix,
|
|
1119
|
+
...compactedMessages,
|
|
1120
|
+
...split.currentTurn
|
|
1121
|
+
];
|
|
1122
|
+
const compactedTokens = estimate(messages);
|
|
1123
|
+
if (compactedTokens > usable) throw new FevexRunError("CONTEXT_BUDGET_EXCEEDED", `Agent "${input.agentName}" context budget exceeded after compaction: ${compactedTokens} tokens > ${usable} usable. The preserved system prefix and current turn cannot be compacted further.`, input.runId);
|
|
1124
|
+
return {
|
|
1125
|
+
messages,
|
|
1126
|
+
removedMessages: split.compactable.length,
|
|
1127
|
+
estimatedTokens: estimated,
|
|
1128
|
+
compactedTokens,
|
|
1129
|
+
summary: compactedMessages.map((message) => message.content).join("\n\n")
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
function splitContextForCompaction(messages) {
|
|
1133
|
+
let lastUser = -1;
|
|
1134
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) if (messages[index].role === "user") {
|
|
1135
|
+
lastUser = index;
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
if (lastUser < 0) return void 0;
|
|
1139
|
+
let lastToolBatch = -1;
|
|
1140
|
+
for (let index = messages.length - 1; index > lastUser; index -= 1) {
|
|
1141
|
+
const message = messages[index];
|
|
1142
|
+
if (message.role === "assistant" && (message.toolCalls?.length ?? 0) > 0) {
|
|
1143
|
+
lastToolBatch = index;
|
|
1144
|
+
break;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
const prefix = messages.filter((message) => message.role === "system");
|
|
1148
|
+
const currentTurn = (lastToolBatch === -1 ? messages.slice(lastUser) : [messages[lastUser], ...messages.slice(lastToolBatch)]).filter((message) => message.role !== "system");
|
|
1149
|
+
const preserved = /* @__PURE__ */ new Set([...prefix, ...currentTurn]);
|
|
1150
|
+
return {
|
|
1151
|
+
prefix,
|
|
1152
|
+
currentTurn,
|
|
1153
|
+
compactable: messages.filter((message) => message.role !== "system" && !preserved.has(message))
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
function validateCompactedMessages(value) {
|
|
1157
|
+
if (!Array.isArray(value) || value.length === 0) throw new TypeError("Context compactor must return at least one message");
|
|
1158
|
+
const roles = /* @__PURE__ */ new Set([
|
|
1159
|
+
"system",
|
|
1160
|
+
"user",
|
|
1161
|
+
"assistant",
|
|
1162
|
+
"tool"
|
|
1163
|
+
]);
|
|
1164
|
+
for (const [index, message] of value.entries()) {
|
|
1165
|
+
if (typeof message !== "object" || message === null || Array.isArray(message)) throw new TypeError(`Context compactor message at index ${index} must be an object`);
|
|
1166
|
+
const record = message;
|
|
1167
|
+
if (typeof record.role !== "string" || !roles.has(record.role)) throw new TypeError(`Context compactor message at index ${index} has an invalid role`);
|
|
1168
|
+
if (typeof record.content !== "string") throw new TypeError(`Context compactor message at index ${index} must have string content`);
|
|
1169
|
+
if (record.toolCalls !== void 0 && !Array.isArray(record.toolCalls)) throw new TypeError(`Context compactor message at index ${index} has invalid toolCalls`);
|
|
1170
|
+
}
|
|
1171
|
+
return value;
|
|
1172
|
+
}
|
|
1173
|
+
//#endregion
|
|
781
1174
|
//#region src/internal/definition-hash.ts
|
|
782
1175
|
function canonicalize(value) {
|
|
783
1176
|
if (Array.isArray(value)) return value.map(canonicalize);
|
|
@@ -796,6 +1189,7 @@ async function transportableTool(tool) {
|
|
|
796
1189
|
retry: tool.retry,
|
|
797
1190
|
credentials: tool.credentials ?? [],
|
|
798
1191
|
sandbox: tool.sandbox,
|
|
1192
|
+
maxResultBytes: tool.maxResultBytes,
|
|
799
1193
|
source: tool.source
|
|
800
1194
|
};
|
|
801
1195
|
}
|
|
@@ -804,7 +1198,7 @@ async function sha256(value) {
|
|
|
804
1198
|
const digest = await crypto.subtle.digest("SHA-256", encoded);
|
|
805
1199
|
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
806
1200
|
}
|
|
807
|
-
async function definitionHash(name, modelName, agent, tools) {
|
|
1201
|
+
async function definitionHash(name, modelName, agent, tools, contextBudget) {
|
|
808
1202
|
const toolDefinitions = await Promise.all((agent.tools ?? []).map((toolName) => transportableTool(tools.get(toolName))));
|
|
809
1203
|
return sha256({
|
|
810
1204
|
name,
|
|
@@ -820,6 +1214,7 @@ async function definitionHash(name, modelName, agent, tools) {
|
|
|
820
1214
|
elicitation: agent.elicitation,
|
|
821
1215
|
approvalMode: agent.approvalMode,
|
|
822
1216
|
limits: agent.limits,
|
|
1217
|
+
contextBudget,
|
|
823
1218
|
inputSchema: agent.inputSchema === void 0 ? void 0 : toTransportableSchema(agent.inputSchema, "input", `Input schema for agent "${name}" is not transportable`),
|
|
824
1219
|
outputSchema: agent.outputSchema === void 0 ? void 0 : toTransportableSchema(agent.outputSchema, "output", `Output schema for agent "${name}" is not transportable`)
|
|
825
1220
|
});
|
|
@@ -864,7 +1259,8 @@ function combineLimits(first, second) {
|
|
|
864
1259
|
...minLimit(first.maxSteps, second.maxSteps) === void 0 ? {} : { maxSteps: minLimit(first.maxSteps, second.maxSteps) },
|
|
865
1260
|
...minLimit(first.maxToolCalls, second.maxToolCalls) === void 0 ? {} : { maxToolCalls: minLimit(first.maxToolCalls, second.maxToolCalls) },
|
|
866
1261
|
...minLimit(first.maxInputTokens, second.maxInputTokens) === void 0 ? {} : { maxInputTokens: minLimit(first.maxInputTokens, second.maxInputTokens) },
|
|
867
|
-
...minLimit(first.maxOutputTokens, second.maxOutputTokens) === void 0 ? {} : { maxOutputTokens: minLimit(first.maxOutputTokens, second.maxOutputTokens) }
|
|
1262
|
+
...minLimit(first.maxOutputTokens, second.maxOutputTokens) === void 0 ? {} : { maxOutputTokens: minLimit(first.maxOutputTokens, second.maxOutputTokens) },
|
|
1263
|
+
...minLimit(first.maxToolResultBytes, second.maxToolResultBytes) === void 0 ? {} : { maxToolResultBytes: minLimit(first.maxToolResultBytes, second.maxToolResultBytes) }
|
|
868
1264
|
};
|
|
869
1265
|
}
|
|
870
1266
|
function subtractLimit(limit, used) {
|
|
@@ -877,7 +1273,8 @@ function remainingWorkflowLimits(limits, budget) {
|
|
|
877
1273
|
...limits.maxSteps === void 0 ? {} : { maxSteps: subtractLimit(limits.maxSteps, budget?.steps) },
|
|
878
1274
|
...limits.maxToolCalls === void 0 ? {} : { maxToolCalls: subtractLimit(limits.maxToolCalls, budget?.toolCalls) },
|
|
879
1275
|
...limits.maxInputTokens === void 0 ? {} : { maxInputTokens: subtractLimit(limits.maxInputTokens, budget?.usage?.inputTokens) },
|
|
880
|
-
...limits.maxOutputTokens === void 0 ? {} : { maxOutputTokens: subtractLimit(limits.maxOutputTokens, budget?.usage?.outputTokens) }
|
|
1276
|
+
...limits.maxOutputTokens === void 0 ? {} : { maxOutputTokens: subtractLimit(limits.maxOutputTokens, budget?.usage?.outputTokens) },
|
|
1277
|
+
...limits.maxToolResultBytes === void 0 ? {} : { maxToolResultBytes: limits.maxToolResultBytes }
|
|
881
1278
|
};
|
|
882
1279
|
}
|
|
883
1280
|
function assertWorkflowBudget(workflowName, limits, budget) {
|
|
@@ -904,12 +1301,24 @@ function addWorkflowBudget(first, second) {
|
|
|
904
1301
|
* engine — that direction is what keeps the two separable.
|
|
905
1302
|
*/
|
|
906
1303
|
function createAgentEngine(composition, core) {
|
|
907
|
-
const { models, agents, tools, contextProviders, memoryStore, runStore, credentialStore, sandbox, artifacts, policies } = composition;
|
|
1304
|
+
const { models, agents, tools, contextProviders, contextBudget: defaultContextBudget, compaction, toolSearch: defaultToolSearch, toolSearchCache: defaultToolSearchCache, memoryStore, runStore, credentialStore, sandbox, artifacts, policies } = composition;
|
|
1305
|
+
const compactionStats = {
|
|
1306
|
+
compactions: 0,
|
|
1307
|
+
removedMessages: 0,
|
|
1308
|
+
estimatedTokens: 0,
|
|
1309
|
+
compactedTokens: 0,
|
|
1310
|
+
savedTokens: 0
|
|
1311
|
+
};
|
|
908
1312
|
const { activeRuns, activeSessions, runtimeOwner, isClosed, admitStart, markInternalRun, resolveModel, resolveCheckpointModel, notifyObserver, createEvent, emitEvent, releaseExecution, commit, startLease, cancelExecution, authorize } = core;
|
|
909
1313
|
const prepareExecutionBody = async (name, request, internal = false, correlation = {}) => {
|
|
910
1314
|
if (!internal && isClosed()) throw new FevexRunError("RUNTIME_CLOSED", "Runtime is not accepting new runs");
|
|
911
1315
|
const agent = agents.get(name);
|
|
912
1316
|
if (!agent) throw new FevexRunError("AGENT_NOT_FOUND", `Agent "${name}" is not registered`);
|
|
1317
|
+
const contextBudget = resolveContextBudget(request.contextBudget ?? agent.contextBudget ?? defaultContextBudget, `Agent "${name}" contextBudget`);
|
|
1318
|
+
const toolSearch = resolveToolSearchOptions(request.toolSearch ?? agent.toolSearch ?? defaultToolSearch, `Agent "${name}" toolSearch`, defaultToolSearchCache);
|
|
1319
|
+
const toolChoice = effectiveToolChoice(agent, request);
|
|
1320
|
+
const initialLoadedTools = new Set(toolSearch.pinned.filter((toolName) => agent.tools?.includes(toolName)));
|
|
1321
|
+
if (typeof toolChoice === "object" && agent.tools?.includes(toolChoice.name)) initialLoadedTools.add(toolChoice.name);
|
|
913
1322
|
const { modelName, model } = resolveModel(agent, request.model);
|
|
914
1323
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
915
1324
|
let session;
|
|
@@ -967,7 +1376,7 @@ function createAgentEngine(composition, core) {
|
|
|
967
1376
|
content: agent.instructions
|
|
968
1377
|
},
|
|
969
1378
|
...knowledge.skills,
|
|
970
|
-
...session
|
|
1379
|
+
...sessionHistoryMessages(session),
|
|
971
1380
|
...knowledge.context,
|
|
972
1381
|
...knowledge.memory,
|
|
973
1382
|
{
|
|
@@ -980,8 +1389,11 @@ function createAgentEngine(composition, core) {
|
|
|
980
1389
|
runId: run.id,
|
|
981
1390
|
...request.model !== void 0 || typeof agent.model === "string" ? { modelName } : {},
|
|
982
1391
|
...request.reasoning === void 0 ? {} : { reasoning: request.reasoning },
|
|
983
|
-
definitionHash: await definitionHash(name, modelName, agent, tools),
|
|
1392
|
+
definitionHash: await definitionHash(name, modelName, agent, tools, contextBudget),
|
|
984
1393
|
limits: combineLimits(agent.limits, request.limits),
|
|
1394
|
+
...contextBudget === void 0 ? {} : { contextBudget },
|
|
1395
|
+
toolSearch: serializeToolSearchOptions(toolSearch),
|
|
1396
|
+
loadedTools: [...initialLoadedTools],
|
|
985
1397
|
messages,
|
|
986
1398
|
inputContent,
|
|
987
1399
|
context: request.context,
|
|
@@ -992,7 +1404,7 @@ function createAgentEngine(composition, core) {
|
|
|
992
1404
|
pendingIndex: 0,
|
|
993
1405
|
effectiveElicitationMode: effectiveElicitationMode(agent, request),
|
|
994
1406
|
effectiveApprovalMode: effectiveApprovalMode(agent, request),
|
|
995
|
-
effectiveToolChoice:
|
|
1407
|
+
effectiveToolChoice: toolChoice
|
|
996
1408
|
};
|
|
997
1409
|
const ownerId = `${runtimeOwner}:${crypto.randomUUID()}`;
|
|
998
1410
|
const lease = {
|
|
@@ -1039,6 +1451,8 @@ function createAgentEngine(composition, core) {
|
|
|
1039
1451
|
const durableCheckpoint = async (state, model, modelName, messages, inputContent, usage, providerState, step, toolCallCount, seenToolCallIds, pendingTools, pendingIndex) => {
|
|
1040
1452
|
const agent = agents.get(state.run.agentName);
|
|
1041
1453
|
if (agent.model !== void 0 && typeof agent.model !== "string") throw new FevexRunError("RUN_NOT_RESUMABLE", "Durable continuation requires a named model", state.run.id);
|
|
1454
|
+
const contextBudget = resolveContextBudget(state.checkpoint?.contextBudget ?? state.request.contextBudget ?? agent.contextBudget ?? defaultContextBudget, `Agent "${state.run.agentName}" contextBudget`);
|
|
1455
|
+
const toolSearch = state.toolSearch ?? resolveToolSearchOptions(state.checkpoint?.toolSearch ?? state.request.toolSearch ?? agent.toolSearch ?? defaultToolSearch, `Agent "${state.run.agentName}" toolSearch`, defaultToolSearchCache);
|
|
1042
1456
|
let serializedState;
|
|
1043
1457
|
if (providerState !== void 0) {
|
|
1044
1458
|
if (!model.stateCodec) throw new FevexRunError("RUN_NOT_RESUMABLE", "ModelGateway must provide stateCodec for durable continuation", state.run.id);
|
|
@@ -1049,8 +1463,11 @@ function createAgentEngine(composition, core) {
|
|
|
1049
1463
|
runId: state.run.id,
|
|
1050
1464
|
modelName,
|
|
1051
1465
|
...state.request.reasoning === void 0 ? {} : { reasoning: state.request.reasoning },
|
|
1052
|
-
definitionHash: await definitionHash(state.run.agentName, modelName, agent, tools),
|
|
1466
|
+
definitionHash: await definitionHash(state.run.agentName, modelName, agent, tools, contextBudget),
|
|
1053
1467
|
limits: state.checkpoint?.limits ?? combineLimits(agent.limits, state.request.limits),
|
|
1468
|
+
...contextBudget === void 0 ? {} : { contextBudget },
|
|
1469
|
+
toolSearch: serializeToolSearchOptions(toolSearch),
|
|
1470
|
+
loadedTools: [...state.loadedTools ?? []],
|
|
1054
1471
|
messages: structuredClone(messages),
|
|
1055
1472
|
inputContent,
|
|
1056
1473
|
context: state.request.context,
|
|
@@ -1109,6 +1526,15 @@ function createAgentEngine(composition, core) {
|
|
|
1109
1526
|
const { request } = state;
|
|
1110
1527
|
const agent = agents.get(name);
|
|
1111
1528
|
const { modelName, model } = resolveModel(agent, state.checkpoint?.modelName ?? request.model);
|
|
1529
|
+
const contextBudget = resolveContextBudget(state.checkpoint?.contextBudget ?? request.contextBudget ?? agent.contextBudget ?? defaultContextBudget, `Agent "${name}" contextBudget`);
|
|
1530
|
+
const semantic = request.toolSearch?.semantic ?? agent.toolSearch?.semantic ?? defaultToolSearch?.semantic;
|
|
1531
|
+
const toolSearch = resolveToolSearchOptions(state.checkpoint === void 0 ? request.toolSearch ?? agent.toolSearch ?? defaultToolSearch : state.checkpoint.toolSearch === void 0 ? {
|
|
1532
|
+
mode: "off",
|
|
1533
|
+
...semantic === void 0 ? {} : { semantic }
|
|
1534
|
+
} : {
|
|
1535
|
+
...state.checkpoint.toolSearch,
|
|
1536
|
+
...semantic === void 0 ? {} : { semantic }
|
|
1537
|
+
}, `Agent "${name}" toolSearch`, defaultToolSearchCache);
|
|
1112
1538
|
const events = [...state.initialEvents ?? []];
|
|
1113
1539
|
const firstEvents = state.initialEvents ?? [];
|
|
1114
1540
|
state.initialEvents = void 0;
|
|
@@ -1126,12 +1552,14 @@ function createAgentEngine(composition, core) {
|
|
|
1126
1552
|
let seenToolCallIds;
|
|
1127
1553
|
let pendingTools;
|
|
1128
1554
|
let pendingIndex;
|
|
1555
|
+
let compactionApplied = false;
|
|
1556
|
+
const semanticState = { attempted: false };
|
|
1129
1557
|
try {
|
|
1130
1558
|
for (const event of firstEvents) yield event;
|
|
1131
1559
|
if (state.checkpoint) {
|
|
1132
1560
|
const checkpoint = state.checkpoint;
|
|
1133
1561
|
if (checkpoint.version !== 2) throw new FevexRunError("CHECKPOINT_UNSUPPORTED", `Checkpoint for run "${state.run.id}" is unsupported`, state.run.id);
|
|
1134
|
-
const hash = await definitionHash(name, modelName, agent, tools);
|
|
1562
|
+
const hash = await definitionHash(name, modelName, agent, tools, contextBudget);
|
|
1135
1563
|
if (checkpoint.definitionHash !== hash) throw new FevexRunError("RUN_DEFINITION_CHANGED", `Definition for agent "${name}" changed`, state.run.id);
|
|
1136
1564
|
inputContent = checkpoint.inputContent;
|
|
1137
1565
|
messages = checkpoint.messages;
|
|
@@ -1153,7 +1581,7 @@ function createAgentEngine(composition, core) {
|
|
|
1153
1581
|
content: agent.instructions
|
|
1154
1582
|
},
|
|
1155
1583
|
...knowledge.skills,
|
|
1156
|
-
...state.session
|
|
1584
|
+
...sessionHistoryMessages(state.session),
|
|
1157
1585
|
...knowledge.context,
|
|
1158
1586
|
...knowledge.memory,
|
|
1159
1587
|
{
|
|
@@ -1201,30 +1629,86 @@ function createAgentEngine(composition, core) {
|
|
|
1201
1629
|
additionalProperties: false
|
|
1202
1630
|
}
|
|
1203
1631
|
};
|
|
1632
|
+
const searchEnabled = shouldUseToolSearch(toolSearch, agentTools);
|
|
1633
|
+
const searchTool = searchEnabled ? {
|
|
1634
|
+
name: SEARCH_TOOLS_TOOL_NAME,
|
|
1635
|
+
description: `Search ${agentTools.length} tools available to this agent by capability. Use it when you need a tool that is not currently exposed. Returns matching tools and makes their schemas available on the next turn.`,
|
|
1636
|
+
inputSchema: {
|
|
1637
|
+
type: "object",
|
|
1638
|
+
properties: {
|
|
1639
|
+
query: {
|
|
1640
|
+
type: "string",
|
|
1641
|
+
description: "Capability to search for, such as \"send email\" or \"query database\"."
|
|
1642
|
+
},
|
|
1643
|
+
limit: {
|
|
1644
|
+
type: "integer",
|
|
1645
|
+
minimum: 1,
|
|
1646
|
+
maximum: toolSearch.limit
|
|
1647
|
+
}
|
|
1648
|
+
},
|
|
1649
|
+
required: ["query"],
|
|
1650
|
+
additionalProperties: false
|
|
1651
|
+
}
|
|
1652
|
+
} : void 0;
|
|
1653
|
+
const elicitationTool = currentElicitationMode === "pause" ? {
|
|
1654
|
+
name: ELICIT_TOOL_NAME,
|
|
1655
|
+
description: "Request external information required to continue this run. Use prompt for explanatory text shown outside the form. Put short field labels in responseSchema property titles and optional field help in property descriptions.",
|
|
1656
|
+
inputSchema: {
|
|
1657
|
+
type: "object",
|
|
1658
|
+
properties: {
|
|
1659
|
+
prompt: {
|
|
1660
|
+
type: "string",
|
|
1661
|
+
description: "Explanatory text for the user; do not duplicate field labels here."
|
|
1662
|
+
},
|
|
1663
|
+
responseSchema: {
|
|
1664
|
+
type: "object",
|
|
1665
|
+
description: "JSON Schema for the expected response. Each property should include a short title for the form label and may include a description for field help."
|
|
1666
|
+
},
|
|
1667
|
+
expiresAt: { type: "string" }
|
|
1668
|
+
},
|
|
1669
|
+
required: ["prompt", "responseSchema"],
|
|
1670
|
+
additionalProperties: false
|
|
1671
|
+
}
|
|
1672
|
+
} : void 0;
|
|
1204
1673
|
const allTools = [
|
|
1205
1674
|
...agentTools,
|
|
1206
1675
|
...skillTool === void 0 ? [] : [skillTool],
|
|
1207
|
-
...
|
|
1208
|
-
|
|
1209
|
-
description: "Request external information required to continue this run. Use prompt for explanatory text shown outside the form. Put short field labels in responseSchema property titles and optional field help in property descriptions.",
|
|
1210
|
-
inputSchema: {
|
|
1211
|
-
type: "object",
|
|
1212
|
-
properties: {
|
|
1213
|
-
prompt: {
|
|
1214
|
-
type: "string",
|
|
1215
|
-
description: "Explanatory text for the user; do not duplicate field labels here."
|
|
1216
|
-
},
|
|
1217
|
-
responseSchema: {
|
|
1218
|
-
type: "object",
|
|
1219
|
-
description: "JSON Schema for the expected response. Each property should include a short title for the form label and may include a description for field help."
|
|
1220
|
-
},
|
|
1221
|
-
expiresAt: { type: "string" }
|
|
1222
|
-
},
|
|
1223
|
-
required: ["prompt", "responseSchema"],
|
|
1224
|
-
additionalProperties: false
|
|
1225
|
-
}
|
|
1226
|
-
}] : []
|
|
1676
|
+
...searchTool === void 0 ? [] : [searchTool],
|
|
1677
|
+
...elicitationTool === void 0 ? [] : [elicitationTool]
|
|
1227
1678
|
];
|
|
1679
|
+
const loadedTools = new Set(state.loadedTools ?? state.checkpoint?.loadedTools ?? []);
|
|
1680
|
+
for (const toolName of toolSearch.pinned) if (agent.tools?.includes(toolName)) loadedTools.add(toolName);
|
|
1681
|
+
if (typeof currentToolChoice === "object" && agent.tools?.includes(currentToolChoice.name)) loadedTools.add(currentToolChoice.name);
|
|
1682
|
+
state.loadedTools = loadedTools;
|
|
1683
|
+
state.toolSearch = toolSearch;
|
|
1684
|
+
let loadedToolsVersion = 0;
|
|
1685
|
+
let cachedModelTools;
|
|
1686
|
+
let cachedModelToolsVersion = -1;
|
|
1687
|
+
const modelTools = () => {
|
|
1688
|
+
if (!searchEnabled) return allTools;
|
|
1689
|
+
if (cachedModelTools !== void 0 && cachedModelToolsVersion === loadedToolsVersion) return cachedModelTools;
|
|
1690
|
+
cachedModelTools = [
|
|
1691
|
+
...agentTools.filter((tool) => loadedTools.has(tool.name)),
|
|
1692
|
+
...skillTool === void 0 ? [] : [skillTool],
|
|
1693
|
+
...searchTool === void 0 ? [] : [searchTool],
|
|
1694
|
+
...elicitationTool === void 0 ? [] : [elicitationTool]
|
|
1695
|
+
];
|
|
1696
|
+
cachedModelToolsVersion = loadedToolsVersion;
|
|
1697
|
+
return cachedModelTools;
|
|
1698
|
+
};
|
|
1699
|
+
const toolsTokenCache = /* @__PURE__ */ new WeakMap();
|
|
1700
|
+
const estimateContext = (input) => {
|
|
1701
|
+
if (!input.tools?.length) return compaction.estimateTokens(input);
|
|
1702
|
+
let toolTokens = toolsTokenCache.get(input.tools);
|
|
1703
|
+
if (toolTokens === void 0) {
|
|
1704
|
+
toolTokens = compaction.estimateTokens({
|
|
1705
|
+
messages: [],
|
|
1706
|
+
tools: input.tools
|
|
1707
|
+
});
|
|
1708
|
+
toolsTokenCache.set(input.tools, toolTokens);
|
|
1709
|
+
}
|
|
1710
|
+
return compaction.estimateTokens({ messages: input.messages }) + toolTokens;
|
|
1711
|
+
};
|
|
1228
1712
|
const effectiveLimits = state.checkpoint?.limits ?? combineLimits(agent.limits, request.limits);
|
|
1229
1713
|
const maxSteps = effectiveLimits?.maxSteps ?? 8;
|
|
1230
1714
|
const maxToolCalls = effectiveLimits?.maxToolCalls ?? 16;
|
|
@@ -1232,12 +1716,73 @@ function createAgentEngine(composition, core) {
|
|
|
1232
1716
|
while (step <= maxSteps) {
|
|
1233
1717
|
request.signal.throwIfAborted();
|
|
1234
1718
|
if (pendingTools.length === 0) {
|
|
1719
|
+
const currentTools = modelTools();
|
|
1720
|
+
if (contextBudget !== void 0) {
|
|
1721
|
+
const compacted = await compactContext({
|
|
1722
|
+
runId: state.run.id,
|
|
1723
|
+
agentName: name,
|
|
1724
|
+
sessionId: state.session.id,
|
|
1725
|
+
messages,
|
|
1726
|
+
tools: currentTools,
|
|
1727
|
+
contextBudget,
|
|
1728
|
+
compactor: compaction.compactor,
|
|
1729
|
+
estimateTokens: estimateContext,
|
|
1730
|
+
step,
|
|
1731
|
+
...request.context === void 0 ? {} : { context: request.context },
|
|
1732
|
+
signal: request.signal,
|
|
1733
|
+
allowCompaction: !compactionApplied
|
|
1734
|
+
});
|
|
1735
|
+
if (compacted !== void 0) {
|
|
1736
|
+
messages = compacted.messages;
|
|
1737
|
+
compactionApplied = true;
|
|
1738
|
+
compactionStats.compactions += 1;
|
|
1739
|
+
compactionStats.removedMessages += compacted.removedMessages;
|
|
1740
|
+
compactionStats.estimatedTokens += compacted.estimatedTokens;
|
|
1741
|
+
compactionStats.compactedTokens += compacted.compactedTokens;
|
|
1742
|
+
compactionStats.savedTokens += Math.max(0, compacted.estimatedTokens - compacted.compactedTokens);
|
|
1743
|
+
const previous = state.session.compaction;
|
|
1744
|
+
const combined = previous === void 0 ? compacted.summary : `${previous.summary}\n\n${compacted.summary}`;
|
|
1745
|
+
const maxSummaryChars = Math.max(200, contextBudget.targetTokens * 4);
|
|
1746
|
+
state.session.compaction = {
|
|
1747
|
+
summary: capSummary(combined, maxSummaryChars),
|
|
1748
|
+
coveredMessages: state.session.history.length,
|
|
1749
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1750
|
+
};
|
|
1751
|
+
const redactedSummary = compaction.redactSummary?.(compacted.summary) ?? compacted.summary;
|
|
1752
|
+
if (typeof redactedSummary !== "string") throw new TypeError("compaction.redactSummary must return a string");
|
|
1753
|
+
const summaryTruncated = redactedSummary.length > MAX_COMPACTION_EVENT_SUMMARY_CHARS;
|
|
1754
|
+
const event = createEvent(state, "context.compacted", {
|
|
1755
|
+
step,
|
|
1756
|
+
removedMessages: compacted.removedMessages,
|
|
1757
|
+
estimatedTokens: compacted.estimatedTokens,
|
|
1758
|
+
compactedTokens: compacted.compactedTokens,
|
|
1759
|
+
targetTokens: contextBudget.targetTokens,
|
|
1760
|
+
contextWindow: contextBudget.contextWindow,
|
|
1761
|
+
summary: summaryTruncated ? capSummary(redactedSummary, MAX_COMPACTION_EVENT_SUMMARY_CHARS) : redactedSummary,
|
|
1762
|
+
...summaryTruncated ? { summaryTruncated: true } : {}
|
|
1763
|
+
});
|
|
1764
|
+
if (isDurableRunStore(runStore)) {
|
|
1765
|
+
const checkpoint = await durableCheckpoint(state, model, modelName, messages, inputContent, usage, providerState, step, toolCallCount, seenToolCallIds, pendingTools, pendingIndex);
|
|
1766
|
+
await commit(state, {
|
|
1767
|
+
checkpoint,
|
|
1768
|
+
session: state.session,
|
|
1769
|
+
events: [event]
|
|
1770
|
+
});
|
|
1771
|
+
state.checkpoint = checkpoint;
|
|
1772
|
+
} else {
|
|
1773
|
+
await runStore.appendEvent(event);
|
|
1774
|
+
notifyObserver(event);
|
|
1775
|
+
}
|
|
1776
|
+
events.push(event);
|
|
1777
|
+
yield event;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1235
1780
|
const hasToolBudget = step < maxSteps && toolCallCount < maxToolCalls;
|
|
1236
|
-
const modelToolChoice = hasToolBudget &&
|
|
1781
|
+
const modelToolChoice = hasToolBudget && currentTools.length ? currentToolChoice === "auto" ? void 0 : currentToolChoice : void 0;
|
|
1237
1782
|
yield await emit("model.started", { step });
|
|
1238
1783
|
const modelStream = readModelStream(model, {
|
|
1239
1784
|
messages: [...messages],
|
|
1240
|
-
tools: hasToolBudget &&
|
|
1785
|
+
tools: hasToolBudget && currentTools.length ? currentTools : void 0,
|
|
1241
1786
|
...modelToolChoice === void 0 ? {} : { toolChoice: modelToolChoice },
|
|
1242
1787
|
reasoning: state.request.reasoning ?? agent.reasoning,
|
|
1243
1788
|
modelOptions: agent.modelOptions,
|
|
@@ -1313,52 +1858,63 @@ function createAgentEngine(composition, core) {
|
|
|
1313
1858
|
assertContinuationBudget(name, "maxInputTokens", "inputTokens", effectiveLimits?.maxInputTokens, usage);
|
|
1314
1859
|
assertContinuationBudget(name, "maxOutputTokens", "outputTokens", effectiveLimits?.maxOutputTokens, usage);
|
|
1315
1860
|
if (result.toolCalls.length > maxToolCalls - toolCallCount) throw new Error(`Agent "${name}" exceeded maxToolCalls limit of ${maxToolCalls}`);
|
|
1316
|
-
const
|
|
1317
|
-
if (
|
|
1318
|
-
if (result.toolCalls.length !== 1) throw new Error(`${
|
|
1319
|
-
if (!
|
|
1320
|
-
if (seenToolCallIds.has(
|
|
1861
|
+
const internalCall = result.toolCalls.find((call) => searchEnabled && call.name === "fevex__search_tools" || progressiveSkills.length > 0 && call.name === "fevex__load_skill");
|
|
1862
|
+
if (internalCall) {
|
|
1863
|
+
if (result.toolCalls.length !== 1) throw new Error(`${internalCall.name} must be the only tool call in a model turn`);
|
|
1864
|
+
if (!internalCall.id?.trim()) throw new Error("Tool call id cannot be empty");
|
|
1865
|
+
if (seenToolCallIds.has(internalCall.id)) throw new Error(`Tool call id "${internalCall.id}" is duplicated in run "${state.run.id}"`);
|
|
1321
1866
|
if (toolCallCount + 1 > maxToolCalls) throw new Error(`Agent "${name}" exceeded maxToolCalls limit of ${maxToolCalls}`);
|
|
1322
|
-
const rawInput = toJsonValue(
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
if (!skill) throw new Error(`Skill "${skillInput.name}" is not available to agent "${name}"`);
|
|
1326
|
-
seenToolCallIds.add(loadSkillCall.id);
|
|
1867
|
+
const rawInput = toJsonValue(internalCall.input, `Input for tool "${internalCall.name}" must be JSON-serializable`);
|
|
1868
|
+
const loadedBefore = loadedTools.size;
|
|
1869
|
+
seenToolCallIds.add(internalCall.id);
|
|
1327
1870
|
messages.push({
|
|
1328
1871
|
role: "assistant",
|
|
1329
1872
|
content: result.output === void 0 ? "" : serializeValue(result.output, `Model output for agent "${name}" must be serializable`),
|
|
1330
1873
|
toolCalls: [{
|
|
1331
|
-
...
|
|
1874
|
+
...internalCall,
|
|
1332
1875
|
input: rawInput
|
|
1333
1876
|
}]
|
|
1334
1877
|
});
|
|
1335
1878
|
yield await emit("tool.started", {
|
|
1336
1879
|
step,
|
|
1337
|
-
toolCallId:
|
|
1338
|
-
toolName:
|
|
1880
|
+
toolCallId: internalCall.id,
|
|
1881
|
+
toolName: internalCall.name
|
|
1339
1882
|
});
|
|
1340
|
-
let
|
|
1883
|
+
let execution;
|
|
1341
1884
|
try {
|
|
1342
|
-
|
|
1885
|
+
execution = await executeInternalTool({
|
|
1886
|
+
call: internalCall,
|
|
1887
|
+
input: rawInput,
|
|
1888
|
+
tools: agentTools,
|
|
1889
|
+
searchEnabled,
|
|
1890
|
+
toolSearch,
|
|
1891
|
+
loadedTools,
|
|
1892
|
+
progressiveSkills,
|
|
1893
|
+
knowledgeContext: knowledgeContext(state, inputContent),
|
|
1894
|
+
semanticState,
|
|
1895
|
+
signal: request.signal
|
|
1896
|
+
});
|
|
1343
1897
|
} catch (error) {
|
|
1344
1898
|
yield await emit("tool.failed", {
|
|
1345
1899
|
step,
|
|
1346
|
-
toolCallId:
|
|
1347
|
-
toolName:
|
|
1900
|
+
toolCallId: internalCall.id,
|
|
1901
|
+
toolName: internalCall.name,
|
|
1348
1902
|
error: toErrorMessage(error)
|
|
1349
1903
|
});
|
|
1350
|
-
throw
|
|
1904
|
+
throw error;
|
|
1351
1905
|
}
|
|
1906
|
+
if (!execution) throw new Error(`Tool "${internalCall.name}" is not available to agent "${name}"`);
|
|
1907
|
+
if (loadedTools.size > loadedBefore) loadedToolsVersion += 1;
|
|
1352
1908
|
messages.push({
|
|
1353
1909
|
role: "tool",
|
|
1354
|
-
name:
|
|
1355
|
-
toolCallId:
|
|
1356
|
-
content:
|
|
1910
|
+
name: internalCall.name,
|
|
1911
|
+
toolCallId: internalCall.id,
|
|
1912
|
+
content: execution.content
|
|
1357
1913
|
});
|
|
1358
1914
|
yield await emit("tool.completed", {
|
|
1359
1915
|
step,
|
|
1360
|
-
toolCallId:
|
|
1361
|
-
toolName:
|
|
1916
|
+
toolCallId: internalCall.id,
|
|
1917
|
+
toolName: internalCall.name
|
|
1362
1918
|
});
|
|
1363
1919
|
toolCallCount += 1;
|
|
1364
1920
|
step += 1;
|
|
@@ -1420,6 +1976,7 @@ function createAgentEngine(composition, core) {
|
|
|
1420
1976
|
batch.add(call.id);
|
|
1421
1977
|
const tool = agent.tools?.includes(call.name) ? tools.get(call.name) : void 0;
|
|
1422
1978
|
if (!tool) throw new Error(`Tool "${call.name}" is not available to agent "${name}"`);
|
|
1979
|
+
if (searchEnabled && !loadedTools.has(call.name)) throw new Error(`Tool "${call.name}" is not loaded; use ${SEARCH_TOOLS_TOOL_NAME} first`);
|
|
1423
1980
|
const rawInput = toJsonValue(call.input, `Input for tool "${call.name}" must be JSON-serializable`);
|
|
1424
1981
|
let validated;
|
|
1425
1982
|
try {
|
|
@@ -1676,7 +2233,13 @@ function createAgentEngine(composition, core) {
|
|
|
1676
2233
|
signal: request.signal
|
|
1677
2234
|
}), request.signal);
|
|
1678
2235
|
for (const event of createdArtifacts.splice(0)) yield event;
|
|
1679
|
-
|
|
2236
|
+
const validated = tool.outputSchema === void 0 ? raw : await validateSchema(tool.outputSchema, raw, `Output from tool "${tool.name}" does not match outputSchema`);
|
|
2237
|
+
const serialized = serializeValue(validated, `Output from tool "${tool.name}" must be JSON-serializable`);
|
|
2238
|
+
const configuredMaxResultBytes = tool.maxResultBytes !== void 0 ? tool.maxResultBytes : effectiveLimits?.maxToolResultBytes;
|
|
2239
|
+
const maxResultBytes = configuredMaxResultBytes === void 0 ? DEFAULT_MAX_TOOL_RESULT_BYTES : configuredMaxResultBytes;
|
|
2240
|
+
const resultBytes = new TextEncoder().encode(serialized).byteLength;
|
|
2241
|
+
if (maxResultBytes !== false && resultBytes > maxResultBytes) throw new FevexRunError("TOOL_RESULT_TOO_LARGE", `Tool "${tool.name}" returned ${resultBytes} bytes; limit is ${maxResultBytes}. Store large outputs as artifacts and return an artifact descriptor.`, state.run.id);
|
|
2242
|
+
output = toJsonValue(validated, `Output from tool "${tool.name}" must be JSON-serializable`);
|
|
1680
2243
|
if (containsSecret(output, secrets)) throw new Error(`Output from tool "${tool.name}" contains a credential`);
|
|
1681
2244
|
record = {
|
|
1682
2245
|
...record,
|
|
@@ -1713,7 +2276,7 @@ function createAgentEngine(composition, core) {
|
|
|
1713
2276
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1714
2277
|
};
|
|
1715
2278
|
if (durable) await commit(state, { toolExecution: record });
|
|
1716
|
-
if (attempt >= maxAttempts || tool.idempotency !== "keyed") {
|
|
2279
|
+
if (attempt >= maxAttempts || tool.idempotency !== "keyed" || error instanceof FevexRunError && error.code === "TOOL_RESULT_TOO_LARGE") {
|
|
1717
2280
|
yield await emit("tool.failed", {
|
|
1718
2281
|
step,
|
|
1719
2282
|
toolCallId: pending.call.id,
|
|
@@ -2110,20 +2673,19 @@ function createAgentEngine(composition, core) {
|
|
|
2110
2673
|
nextEvent,
|
|
2111
2674
|
drainExecution,
|
|
2112
2675
|
resumeAgent,
|
|
2113
|
-
cancelStoredAgent
|
|
2676
|
+
cancelStoredAgent,
|
|
2677
|
+
getCompactionStats() {
|
|
2678
|
+
return { ...compactionStats };
|
|
2679
|
+
}
|
|
2114
2680
|
};
|
|
2115
2681
|
}
|
|
2116
|
-
function
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
return {
|
|
2124
|
-
name: name.trim(),
|
|
2125
|
-
...resource === void 0 ? {} : { resource: resource.trim() }
|
|
2126
|
-
};
|
|
2682
|
+
function sessionHistoryMessages(session) {
|
|
2683
|
+
const compaction = session.compaction;
|
|
2684
|
+
if (compaction === void 0 || compaction.coveredMessages <= 0 || compaction.coveredMessages > session.history.length) return session.history;
|
|
2685
|
+
return [{
|
|
2686
|
+
role: "system",
|
|
2687
|
+
content: `[Compacted conversation]\n${compaction.summary}`
|
|
2688
|
+
}, ...session.history.slice(compaction.coveredMessages)];
|
|
2127
2689
|
}
|
|
2128
2690
|
function scopedArtifacts(store, options) {
|
|
2129
2691
|
const scopedContext = (value) => {
|
|
@@ -2284,6 +2846,17 @@ function annotation(event) {
|
|
|
2284
2846
|
delayMs: event.payload.delayMs
|
|
2285
2847
|
}
|
|
2286
2848
|
};
|
|
2849
|
+
case "context.compacted": return {
|
|
2850
|
+
name: event.type,
|
|
2851
|
+
timestamp: event.timestamp,
|
|
2852
|
+
attributes: {
|
|
2853
|
+
removedMessages: event.payload.removedMessages,
|
|
2854
|
+
estimatedTokens: event.payload.estimatedTokens,
|
|
2855
|
+
compactedTokens: event.payload.compactedTokens,
|
|
2856
|
+
targetTokens: event.payload.targetTokens,
|
|
2857
|
+
contextWindow: event.payload.contextWindow
|
|
2858
|
+
}
|
|
2859
|
+
};
|
|
2287
2860
|
case "tool.execution_unknown": return {
|
|
2288
2861
|
name: event.type,
|
|
2289
2862
|
timestamp: event.timestamp,
|
|
@@ -2882,12 +3455,12 @@ function createRunCore({ models, agents, tools, runStore, policies, onEvent, obs
|
|
|
2882
3455
|
};
|
|
2883
3456
|
const resolveCheckpointModel = async (agentName, agent, checkpoint) => {
|
|
2884
3457
|
const preferred = resolveModel(agent, checkpoint.modelName);
|
|
2885
|
-
if (checkpoint.definitionHash === await definitionHash(agentName, preferred.modelName, agent, tools)) return preferred;
|
|
3458
|
+
if (checkpoint.definitionHash === await definitionHash(agentName, preferred.modelName, agent, tools, checkpoint.contextBudget)) return preferred;
|
|
2886
3459
|
if (checkpoint.modelName !== void 0) return void 0;
|
|
2887
3460
|
for (const candidateName of models.keys()) {
|
|
2888
3461
|
if (candidateName === preferred.modelName) continue;
|
|
2889
3462
|
const candidate = resolveModel(agent, candidateName);
|
|
2890
|
-
if (checkpoint.definitionHash === await definitionHash(agentName, candidate.modelName, agent, tools)) return candidate;
|
|
3463
|
+
if (checkpoint.definitionHash === await definitionHash(agentName, candidate.modelName, agent, tools, checkpoint.contextBudget)) return candidate;
|
|
2891
3464
|
}
|
|
2892
3465
|
};
|
|
2893
3466
|
const scheduleTraceExport = (event) => {
|
|
@@ -4218,7 +4791,7 @@ function createRuntime(composition) {
|
|
|
4218
4791
|
const core = createRunCore(composition);
|
|
4219
4792
|
const { activeRuns, activeWorkflowRuns, activeSessions, isIdle, waitForIdle, closeAdmission, isClosed, traceExports, runtimeOwner, createEvent, createCoordinatorEvent, notifyObserver, commit, startLease, cancelExecution, releaseWorkflowExecution, commitWorkflow } = core;
|
|
4220
4793
|
const agentEngine = createAgentEngine(composition, core);
|
|
4221
|
-
const { prepareExecution, executeAgent, nextEvent, drainExecution, resumeAgent, cancelStoredAgent } = agentEngine;
|
|
4794
|
+
const { prepareExecution, executeAgent, nextEvent, drainExecution, resumeAgent, cancelStoredAgent, getCompactionStats } = agentEngine;
|
|
4222
4795
|
const { prepareWorkflowExecution, executeWorkflow, nextWorkflowEvent, drainWorkflowExecution, resumeWorkflow, cancelWorkflowExecution, cancelStoredWorkflow } = createWorkflowEngine(composition, core, agentEngine);
|
|
4223
4796
|
const resumeRun = async (runId, resolution) => {
|
|
4224
4797
|
if (isClosed()) throw new FevexRunError("RUNTIME_CLOSED", "Runtime is not accepting new runs", runId);
|
|
@@ -4408,6 +4981,7 @@ function createRuntime(composition) {
|
|
|
4408
4981
|
role: "system",
|
|
4409
4982
|
content: summary
|
|
4410
4983
|
}];
|
|
4984
|
+
session.compaction = void 0;
|
|
4411
4985
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4412
4986
|
await runStore.saveSession(session);
|
|
4413
4987
|
return structuredClone(session);
|
|
@@ -4418,6 +4992,9 @@ function createRuntime(composition) {
|
|
|
4418
4992
|
getObservabilityStats() {
|
|
4419
4993
|
return traceExports.stats();
|
|
4420
4994
|
},
|
|
4995
|
+
getCompactionStats() {
|
|
4996
|
+
return getCompactionStats();
|
|
4997
|
+
},
|
|
4421
4998
|
shutdown
|
|
4422
4999
|
};
|
|
4423
5000
|
}
|
|
@@ -4433,4 +5010,4 @@ function createFevex(config) {
|
|
|
4433
5010
|
return createRuntime(createComposition(config));
|
|
4434
5011
|
}
|
|
4435
5012
|
//#endregion
|
|
4436
|
-
export { ArtifactError, CatalogedArtifactStore, ChannelError, FEVEX_JSON_SCHEMA_PROFILE_VERSION, FevexConfigurationError, FevexRunError, FileSystemArtifactStore, InMemoryArtifactCatalog, InMemoryArtifactStore, InMemoryMemoryStore, InMemoryRunStore, IntegrationError, PROVIDER_REASONING_UNSUPPORTED, PROVIDER_SCHEMA_UNSUPPORTED, RunPausedError, S3ArtifactStore, SCHEMA_NOT_TRANSPORTABLE, SandboxError, artifactTenantId, assertArtifactId, compileFevexJsonSchema, createAgentsMdContextProvider, createFevex, createLocalSandbox, createMemoryContextProvider, defineAgent, defineConnection, defineContextProvider, definePolicy, defineSkill, defineTeam, defineTool, defineWorkflow, deliverChannelRun, handleChannelInput, isDurableRunStore, isProgressiveSkillProvider, isRetainableRunStore, loadSkillFile, loadSkillFiles, sandboxResultToJson, toToolSpec, validateFevexJsonSchemaProfile, validateListEventsOptions, validatePurgeRunsOptions };
|
|
5013
|
+
export { ArtifactError, CatalogedArtifactStore, ChannelError, FEVEX_JSON_SCHEMA_PROFILE_VERSION, FevexConfigurationError, FevexRunError, FileSystemArtifactStore, InMemoryArtifactCatalog, InMemoryArtifactStore, InMemoryMemoryStore, InMemoryRunStore, InMemoryToolEmbeddingCache, IntegrationError, PROVIDER_REASONING_UNSUPPORTED, PROVIDER_SCHEMA_UNSUPPORTED, RunPausedError, S3ArtifactStore, SCHEMA_NOT_TRANSPORTABLE, SandboxError, artifactTenantId, assertArtifactId, buildToolEmbeddingIndex, compileFevexJsonSchema, cosineSimilarity, createAgentsMdContextProvider, createDigestContextCompactor, createFevex, createLocalSandbox, createMemoryContextProvider, createModelContextCompactor, defineAgent, defineConnection, defineContextProvider, definePolicy, defineSkill, defineTeam, defineTool, defineWorkflow, deliverChannelRun, estimateContextTokens, handleChannelInput, isDurableRunStore, isProgressiveSkillProvider, isRetainableRunStore, loadSkillFile, loadSkillFiles, resolveContextBudget, sandboxResultToJson, schemaKeywords, searchToolEmbeddingIndex, toToolSpec, toolEmbeddingKey, toolEmbeddingText, validateFevexJsonSchemaProfile, validateListEventsOptions, validatePurgeRunsOptions };
|