@fevex/core 0.1.0-alpha.6 → 0.1.0-alpha.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.
Files changed (34) hide show
  1. package/README.md +47 -0
  2. package/dist/agents/index.d.mts +2 -2
  3. package/dist/artifacts/index.d.mts +1 -1
  4. package/dist/channels/index.d.mts +1 -1
  5. package/dist/evals/index.d.mts +4 -4
  6. package/dist/{fevex-B4QY5Z6e.d.mts → fevex-DB0-k1fb.d.mts} +14 -10
  7. package/dist/http/index.d.mts +4 -4
  8. package/dist/{index-CzAi2Y_Z.d.mts → index-B8molngu.d.mts} +92 -3
  9. package/dist/{index-BgKFd9yw.d.mts → index-BfYzemR8.d.mts} +1 -1
  10. package/dist/{index-CbACw6xZ.d.mts → index-Bl0hR7EC.d.mts} +2 -2
  11. package/dist/index-CAyAoOW0.d.mts +116 -0
  12. package/dist/{index-Df84VWmN.d.mts → index-CL7mu_x9.d.mts} +1 -1
  13. package/dist/{index-1cHr7KB_.d.mts → index-CODYPevS.d.mts} +3 -3
  14. package/dist/{index-CpLDt6Rw.d.mts → index-CecXoEbs.d.mts} +3 -3
  15. package/dist/{index-DVw0zOc_.d.mts → index-CuaetTLQ.d.mts} +3 -3
  16. package/dist/{index-5p3q2yIp.d.mts → index-DtMxzCqj.d.mts} +2 -2
  17. package/dist/{index-CseuTR-F.d.mts → index-RKE3-CKs.d.mts} +2 -2
  18. package/dist/index.d.mts +13 -13
  19. package/dist/index.mjs +311 -24
  20. package/dist/knowledge/index.d.mts +2 -2
  21. package/dist/knowledge/index.mjs +236 -36
  22. package/dist/models/index.d.mts +1 -1
  23. package/dist/observability/index.d.mts +1 -1
  24. package/dist/policies/index.d.mts +1 -1
  25. package/dist/runtime/index.d.mts +2 -2
  26. package/dist/runtime/index.mjs +105 -1
  27. package/dist/sandbox/index.d.mts +1 -1
  28. package/dist/teams/index.d.mts +1 -1
  29. package/dist/testing/index.d.mts +6 -6
  30. package/dist/tools/index.d.mts +2 -2
  31. package/dist/{types-B8laW1hM.d.mts → types-CckDlhYo.d.mts} +15 -1
  32. package/dist/workflows/index.d.mts +1 -1
  33. package/package.json +1 -1
  34. package/dist/index-BJktlD4E.d.mts +0 -70
package/dist/index.mjs CHANGED
@@ -2,9 +2,9 @@ import { defineAgent } from "./agents/index.mjs";
2
2
  import { a as CatalogedArtifactStore, c as assertArtifactId, i as FileSystemArtifactStore, n as InMemoryArtifactStore, o as ArtifactError, r as InMemoryArtifactCatalog, s as artifactTenantId, t as S3ArtifactStore } from "./artifacts-gUzemET7.mjs";
3
3
  import { n as RunPausedError, t as FevexRunError } from "./run-error-Cr17wPGH.mjs";
4
4
  import { ChannelError, deliverChannelRun, handleChannelInput } from "./channels/index.mjs";
5
- import { InMemoryMemoryStore, createMemoryContextProvider, defineContextProvider, defineSkill } from "./knowledge/index.mjs";
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";
7
+ import { InMemoryRunStore, createDigestContextCompactor, estimateContextTokens, isDurableRunStore, isRetainableRunStore, resolveContextBudget, validateListEventsOptions, validatePurgeRunsOptions } from "./runtime/index.mjs";
8
8
  import { FEVEX_JSON_SCHEMA_PROFILE_VERSION, IntegrationError, compileFevexJsonSchema, defineConnection, defineTool, toToolSpec, 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";
@@ -300,6 +300,16 @@ function createComposition(config) {
300
300
  assertConfiguration(config.tools === void 0 || Array.isArray(config.tools), "INVALID_CONFIG", "Fevex config \"tools\" must be an array");
301
301
  assertConfiguration(config.connections === void 0 || Array.isArray(config.connections), "INVALID_CONFIG", "Fevex config \"connections\" must be an array");
302
302
  assertConfiguration(config.contextProviders === void 0 || Array.isArray(config.contextProviders), "INVALID_CONFIG", "Fevex config \"contextProviders\" must be an array");
303
+ if (config.contextBudget !== void 0) try {
304
+ resolveContextBudget(config.contextBudget, "Fevex config \"contextBudget\"");
305
+ } catch (error) {
306
+ throw configurationError("INVALID_CONFIG", error.message);
307
+ }
308
+ if (config.compaction !== void 0) {
309
+ assertConfiguration(typeof config.compaction === "object" && config.compaction !== null && !Array.isArray(config.compaction), "INVALID_CONFIG", "Fevex config \"compaction\" must be an object");
310
+ 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");
311
+ assertConfiguration(config.compaction.estimateTokens === void 0 || typeof config.compaction.estimateTokens === "function", "INVALID_CONFIG", "Fevex config \"compaction.estimateTokens\" must be a function");
312
+ }
303
313
  assertConfiguration(config.memoryStore === void 0 || isMemoryStore(config.memoryStore), "INVALID_CONFIG", "Fevex config \"memoryStore\" must implement MemoryStore");
304
314
  assertConfiguration(config.onEvent === void 0 || typeof config.onEvent === "function", "INVALID_CONFIG", "Fevex config \"onEvent\" must be a function");
305
315
  assertConfiguration(config.runStore === void 0 || isRunStore(config.runStore), "INVALID_CONFIG", "Fevex config \"runStore\" must implement RunStore");
@@ -475,6 +485,11 @@ function createComposition(config) {
475
485
  assertConfiguration(definition.approvalMode === void 0 || definition.approvalMode === "pause" || definition.approvalMode === "deny", "INVALID_AGENT", `Agent "${name}" approvalMode is invalid`);
476
486
  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
487
  assertConfiguration(definition.limits === void 0 || isRecord(definition.limits), "INVALID_AGENT", `Agent "${name}" limits must be an object`);
488
+ if (definition.contextBudget !== void 0) try {
489
+ resolveContextBudget(definition.contextBudget, `Agent "${name}" contextBudget`);
490
+ } catch (error) {
491
+ throw configurationError("INVALID_AGENT", error.message);
492
+ }
478
493
  if (definition.outputSchema !== void 0) assertConfiguration(isStandardSchema(definition.outputSchema), "INVALID_AGENT", `Output schema for agent "${name}" must implement Standard Schema`);
479
494
  const rawLimits = definition.limits;
480
495
  assertLimit(rawLimits?.maxSteps, "maxSteps", 1, name);
@@ -498,6 +513,7 @@ function createComposition(config) {
498
513
  ...definition.toolChoice === void 0 ? {} : { toolChoice: typeof definition.toolChoice === "string" ? definition.toolChoice : { name: definition.toolChoice.name } },
499
514
  ...definition.elicitation === void 0 ? {} : { elicitation: definition.elicitation },
500
515
  ...definition.approvalMode === void 0 ? {} : { approvalMode: definition.approvalMode },
516
+ ...definition.contextBudget === void 0 ? {} : { contextBudget: { ...definition.contextBudget } },
501
517
  ...definition.inputSchema === void 0 ? {} : { inputSchema: definition.inputSchema },
502
518
  ...definition.outputSchema === void 0 ? {} : { outputSchema: definition.outputSchema },
503
519
  ...rawLimits === void 0 ? {} : { limits: {
@@ -621,6 +637,11 @@ function createComposition(config) {
621
637
  teams,
622
638
  tools,
623
639
  contextProviders,
640
+ contextBudget: config.contextBudget === void 0 ? void 0 : { ...config.contextBudget },
641
+ compaction: {
642
+ compactor: config.compaction?.compactor ?? createDigestContextCompactor(),
643
+ estimateTokens: config.compaction?.estimateTokens ?? estimateContextTokens
644
+ },
624
645
  memoryStore: config.memoryStore ?? (usesMemory ? new InMemoryMemoryStore() : void 0),
625
646
  runStore: config.runStore ?? new InMemoryRunStore(),
626
647
  credentialStore: config.credentialStore,
@@ -649,6 +670,8 @@ const LEASE_MS = 3e4;
649
670
  const LEASE_RENEW_MS = 1e4;
650
671
  /** Reserved name of the internal elicitation tool exposed to the model. */
651
672
  const ELICIT_TOOL_NAME = "fevex__elicit";
673
+ /** Reserved name of the internal progressive skill loader exposed to the model. */
674
+ const LOAD_SKILL_TOOL_NAME = "fevex__load_skill";
652
675
  //#endregion
653
676
  //#region src/internal/run-helpers.ts
654
677
  function redact(message, secrets) {
@@ -776,6 +799,103 @@ const runErrorPayload = (error) => {
776
799
  };
777
800
  };
778
801
  //#endregion
802
+ //#region src/internal/context-compaction.ts
803
+ /** Maximum summary characters copied into the context.compacted event. */
804
+ const MAX_COMPACTION_EVENT_SUMMARY_CHARS = 4e3;
805
+ async function compactContext(input) {
806
+ const estimateWith = (messages, includeTools) => {
807
+ const value = input.estimateTokens({
808
+ messages,
809
+ ...includeTools ? { tools: input.tools } : {}
810
+ });
811
+ if (!Number.isFinite(value) || value < 0) throw new TypeError("Context token estimator must return a non-negative finite number");
812
+ return Math.ceil(value * input.contextBudget.safetyFactor);
813
+ };
814
+ const estimate = (messages) => estimateWith(messages, true);
815
+ const estimated = estimate(input.messages);
816
+ const usable = input.contextBudget.contextWindow - input.contextBudget.reserveOutputTokens;
817
+ const threshold = Math.floor(usable * input.contextBudget.compactAt);
818
+ if (input.allowCompaction === false || estimated <= threshold) {
819
+ 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);
820
+ return;
821
+ }
822
+ const split = splitContextForCompaction(input.messages);
823
+ if (split === void 0 || split.compactable.length === 0) {
824
+ 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);
825
+ return;
826
+ }
827
+ const preservedTokens = estimate([...split.prefix, ...split.currentTurn]);
828
+ const available = Math.max(1, Math.min(input.contextBudget.targetTokens, usable - 1) - preservedTokens);
829
+ const compactedMessages = validateCompactedMessages(await abortable(() => input.compactor.compact({
830
+ agentName: input.agentName,
831
+ sessionId: input.sessionId,
832
+ messages: split.compactable,
833
+ tools: input.tools,
834
+ estimatedTokens: estimated,
835
+ targetTokens: available,
836
+ contextWindow: input.contextBudget.contextWindow,
837
+ step: input.step,
838
+ ...input.context === void 0 ? {} : { context: input.context },
839
+ signal: input.signal,
840
+ estimateTokens: (messages) => estimateWith(messages, false)
841
+ }), input.signal));
842
+ const messages = [
843
+ ...split.prefix,
844
+ ...compactedMessages,
845
+ ...split.currentTurn
846
+ ];
847
+ const compactedTokens = estimate(messages);
848
+ 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);
849
+ return {
850
+ messages,
851
+ removedMessages: split.compactable.length,
852
+ estimatedTokens: estimated,
853
+ compactedTokens,
854
+ summary: compactedMessages.map((message) => message.content).join("\n\n")
855
+ };
856
+ }
857
+ function splitContextForCompaction(messages) {
858
+ let lastUser = -1;
859
+ for (let index = messages.length - 1; index >= 0; index -= 1) if (messages[index].role === "user") {
860
+ lastUser = index;
861
+ break;
862
+ }
863
+ if (lastUser < 0) return void 0;
864
+ let lastToolBatch = -1;
865
+ for (let index = messages.length - 1; index > lastUser; index -= 1) {
866
+ const message = messages[index];
867
+ if (message.role === "assistant" && (message.toolCalls?.length ?? 0) > 0) {
868
+ lastToolBatch = index;
869
+ break;
870
+ }
871
+ }
872
+ const prefix = messages.filter((message) => message.role === "system");
873
+ const currentTurn = (lastToolBatch === -1 ? messages.slice(lastUser) : [messages[lastUser], ...messages.slice(lastToolBatch)]).filter((message) => message.role !== "system");
874
+ const preserved = /* @__PURE__ */ new Set([...prefix, ...currentTurn]);
875
+ return {
876
+ prefix,
877
+ currentTurn,
878
+ compactable: messages.filter((message) => message.role !== "system" && !preserved.has(message))
879
+ };
880
+ }
881
+ function validateCompactedMessages(value) {
882
+ if (!Array.isArray(value) || value.length === 0) throw new TypeError("Context compactor must return at least one message");
883
+ const roles = /* @__PURE__ */ new Set([
884
+ "system",
885
+ "user",
886
+ "assistant",
887
+ "tool"
888
+ ]);
889
+ for (const [index, message] of value.entries()) {
890
+ if (typeof message !== "object" || message === null || Array.isArray(message)) throw new TypeError(`Context compactor message at index ${index} must be an object`);
891
+ const record = message;
892
+ if (typeof record.role !== "string" || !roles.has(record.role)) throw new TypeError(`Context compactor message at index ${index} has an invalid role`);
893
+ if (typeof record.content !== "string") throw new TypeError(`Context compactor message at index ${index} must have string content`);
894
+ if (record.toolCalls !== void 0 && !Array.isArray(record.toolCalls)) throw new TypeError(`Context compactor message at index ${index} has invalid toolCalls`);
895
+ }
896
+ return value;
897
+ }
898
+ //#endregion
779
899
  //#region src/internal/definition-hash.ts
780
900
  function canonicalize(value) {
781
901
  if (Array.isArray(value)) return value.map(canonicalize);
@@ -802,7 +922,7 @@ async function sha256(value) {
802
922
  const digest = await crypto.subtle.digest("SHA-256", encoded);
803
923
  return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
804
924
  }
805
- async function definitionHash(name, modelName, agent, tools) {
925
+ async function definitionHash(name, modelName, agent, tools, contextBudget) {
806
926
  const toolDefinitions = await Promise.all((agent.tools ?? []).map((toolName) => transportableTool(tools.get(toolName))));
807
927
  return sha256({
808
928
  name,
@@ -818,6 +938,7 @@ async function definitionHash(name, modelName, agent, tools) {
818
938
  elicitation: agent.elicitation,
819
939
  approvalMode: agent.approvalMode,
820
940
  limits: agent.limits,
941
+ contextBudget,
821
942
  inputSchema: agent.inputSchema === void 0 ? void 0 : toTransportableSchema(agent.inputSchema, "input", `Input schema for agent "${name}" is not transportable`),
822
943
  outputSchema: agent.outputSchema === void 0 ? void 0 : toTransportableSchema(agent.outputSchema, "output", `Output schema for agent "${name}" is not transportable`)
823
944
  });
@@ -902,12 +1023,13 @@ function addWorkflowBudget(first, second) {
902
1023
  * engine — that direction is what keeps the two separable.
903
1024
  */
904
1025
  function createAgentEngine(composition, core) {
905
- const { models, agents, tools, contextProviders, memoryStore, runStore, credentialStore, sandbox, artifacts, policies } = composition;
1026
+ const { models, agents, tools, contextProviders, contextBudget: defaultContextBudget, compaction, memoryStore, runStore, credentialStore, sandbox, artifacts, policies } = composition;
906
1027
  const { activeRuns, activeSessions, runtimeOwner, isClosed, admitStart, markInternalRun, resolveModel, resolveCheckpointModel, notifyObserver, createEvent, emitEvent, releaseExecution, commit, startLease, cancelExecution, authorize } = core;
907
1028
  const prepareExecutionBody = async (name, request, internal = false, correlation = {}) => {
908
1029
  if (!internal && isClosed()) throw new FevexRunError("RUNTIME_CLOSED", "Runtime is not accepting new runs");
909
1030
  const agent = agents.get(name);
910
1031
  if (!agent) throw new FevexRunError("AGENT_NOT_FOUND", `Agent "${name}" is not registered`);
1032
+ const contextBudget = resolveContextBudget(request.contextBudget ?? agent.contextBudget ?? defaultContextBudget, `Agent "${name}" contextBudget`);
911
1033
  const { modelName, model } = resolveModel(agent, request.model);
912
1034
  const now = (/* @__PURE__ */ new Date()).toISOString();
913
1035
  let session;
@@ -965,7 +1087,7 @@ function createAgentEngine(composition, core) {
965
1087
  content: agent.instructions
966
1088
  },
967
1089
  ...knowledge.skills,
968
- ...session.history,
1090
+ ...sessionHistoryMessages(session),
969
1091
  ...knowledge.context,
970
1092
  ...knowledge.memory,
971
1093
  {
@@ -978,8 +1100,9 @@ function createAgentEngine(composition, core) {
978
1100
  runId: run.id,
979
1101
  ...request.model !== void 0 || typeof agent.model === "string" ? { modelName } : {},
980
1102
  ...request.reasoning === void 0 ? {} : { reasoning: request.reasoning },
981
- definitionHash: await definitionHash(name, modelName, agent, tools),
1103
+ definitionHash: await definitionHash(name, modelName, agent, tools, contextBudget),
982
1104
  limits: combineLimits(agent.limits, request.limits),
1105
+ ...contextBudget === void 0 ? {} : { contextBudget },
983
1106
  messages,
984
1107
  inputContent,
985
1108
  context: request.context,
@@ -1037,6 +1160,7 @@ function createAgentEngine(composition, core) {
1037
1160
  const durableCheckpoint = async (state, model, modelName, messages, inputContent, usage, providerState, step, toolCallCount, seenToolCallIds, pendingTools, pendingIndex) => {
1038
1161
  const agent = agents.get(state.run.agentName);
1039
1162
  if (agent.model !== void 0 && typeof agent.model !== "string") throw new FevexRunError("RUN_NOT_RESUMABLE", "Durable continuation requires a named model", state.run.id);
1163
+ const contextBudget = resolveContextBudget(state.checkpoint?.contextBudget ?? state.request.contextBudget ?? agent.contextBudget ?? defaultContextBudget, `Agent "${state.run.agentName}" contextBudget`);
1040
1164
  let serializedState;
1041
1165
  if (providerState !== void 0) {
1042
1166
  if (!model.stateCodec) throw new FevexRunError("RUN_NOT_RESUMABLE", "ModelGateway must provide stateCodec for durable continuation", state.run.id);
@@ -1047,8 +1171,9 @@ function createAgentEngine(composition, core) {
1047
1171
  runId: state.run.id,
1048
1172
  modelName,
1049
1173
  ...state.request.reasoning === void 0 ? {} : { reasoning: state.request.reasoning },
1050
- definitionHash: await definitionHash(state.run.agentName, modelName, agent, tools),
1174
+ definitionHash: await definitionHash(state.run.agentName, modelName, agent, tools, contextBudget),
1051
1175
  limits: state.checkpoint?.limits ?? combineLimits(agent.limits, state.request.limits),
1176
+ ...contextBudget === void 0 ? {} : { contextBudget },
1052
1177
  messages: structuredClone(messages),
1053
1178
  inputContent,
1054
1179
  context: state.request.context,
@@ -1107,6 +1232,7 @@ function createAgentEngine(composition, core) {
1107
1232
  const { request } = state;
1108
1233
  const agent = agents.get(name);
1109
1234
  const { modelName, model } = resolveModel(agent, state.checkpoint?.modelName ?? request.model);
1235
+ const contextBudget = resolveContextBudget(state.checkpoint?.contextBudget ?? request.contextBudget ?? agent.contextBudget ?? defaultContextBudget, `Agent "${name}" contextBudget`);
1110
1236
  const events = [...state.initialEvents ?? []];
1111
1237
  const firstEvents = state.initialEvents ?? [];
1112
1238
  state.initialEvents = void 0;
@@ -1124,12 +1250,13 @@ function createAgentEngine(composition, core) {
1124
1250
  let seenToolCallIds;
1125
1251
  let pendingTools;
1126
1252
  let pendingIndex;
1253
+ let compactionApplied = false;
1127
1254
  try {
1128
1255
  for (const event of firstEvents) yield event;
1129
1256
  if (state.checkpoint) {
1130
1257
  const checkpoint = state.checkpoint;
1131
1258
  if (checkpoint.version !== 2) throw new FevexRunError("CHECKPOINT_UNSUPPORTED", `Checkpoint for run "${state.run.id}" is unsupported`, state.run.id);
1132
- const hash = await definitionHash(name, modelName, agent, tools);
1259
+ const hash = await definitionHash(name, modelName, agent, tools, contextBudget);
1133
1260
  if (checkpoint.definitionHash !== hash) throw new FevexRunError("RUN_DEFINITION_CHANGED", `Definition for agent "${name}" changed`, state.run.id);
1134
1261
  inputContent = checkpoint.inputContent;
1135
1262
  messages = checkpoint.messages;
@@ -1151,7 +1278,7 @@ function createAgentEngine(composition, core) {
1151
1278
  content: agent.instructions
1152
1279
  },
1153
1280
  ...knowledge.skills,
1154
- ...state.session.history,
1281
+ ...sessionHistoryMessages(state.session),
1155
1282
  ...knowledge.context,
1156
1283
  ...knowledge.memory,
1157
1284
  {
@@ -1179,26 +1306,50 @@ function createAgentEngine(composition, core) {
1179
1306
  description: remote.description
1180
1307
  } : tool, inputSchema);
1181
1308
  }));
1182
- const allTools = currentElicitationMode === "pause" ? [...agentTools, {
1183
- name: ELICIT_TOOL_NAME,
1184
- 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.",
1309
+ const progressiveSkills = (agent.skills ?? []).map((skillName) => contextProviders.get(skillName)).filter((provider) => provider !== void 0 && isProgressiveSkillProvider(provider));
1310
+ const skillTool = progressiveSkills.length === 0 ? void 0 : {
1311
+ name: LOAD_SKILL_TOOL_NAME,
1312
+ description: `Load the full instructions for one of this agent's skills. Available skills: ${progressiveSkills.map((skill) => skill.name).join(", ")}. Pass a resource path from the loaded skill to read one resource.`,
1185
1313
  inputSchema: {
1186
1314
  type: "object",
1187
1315
  properties: {
1188
- prompt: {
1316
+ name: {
1189
1317
  type: "string",
1190
- description: "Explanatory text for the user; do not duplicate field labels here."
1191
- },
1192
- responseSchema: {
1193
- type: "object",
1194
- 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."
1318
+ enum: progressiveSkills.map((skill) => skill.name)
1195
1319
  },
1196
- expiresAt: { type: "string" }
1320
+ resource: {
1321
+ type: "string",
1322
+ description: "Optional resource path returned by a previous load."
1323
+ }
1197
1324
  },
1198
- required: ["prompt", "responseSchema"],
1325
+ required: ["name"],
1199
1326
  additionalProperties: false
1200
1327
  }
1201
- }] : agentTools;
1328
+ };
1329
+ const allTools = [
1330
+ ...agentTools,
1331
+ ...skillTool === void 0 ? [] : [skillTool],
1332
+ ...currentElicitationMode === "pause" ? [{
1333
+ name: ELICIT_TOOL_NAME,
1334
+ 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.",
1335
+ inputSchema: {
1336
+ type: "object",
1337
+ properties: {
1338
+ prompt: {
1339
+ type: "string",
1340
+ description: "Explanatory text for the user; do not duplicate field labels here."
1341
+ },
1342
+ responseSchema: {
1343
+ type: "object",
1344
+ 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."
1345
+ },
1346
+ expiresAt: { type: "string" }
1347
+ },
1348
+ required: ["prompt", "responseSchema"],
1349
+ additionalProperties: false
1350
+ }
1351
+ }] : []
1352
+ ];
1202
1353
  const effectiveLimits = state.checkpoint?.limits ?? combineLimits(agent.limits, request.limits);
1203
1354
  const maxSteps = effectiveLimits?.maxSteps ?? 8;
1204
1355
  const maxToolCalls = effectiveLimits?.maxToolCalls ?? 16;
@@ -1206,6 +1357,59 @@ function createAgentEngine(composition, core) {
1206
1357
  while (step <= maxSteps) {
1207
1358
  request.signal.throwIfAborted();
1208
1359
  if (pendingTools.length === 0) {
1360
+ if (contextBudget !== void 0) {
1361
+ const compacted = await compactContext({
1362
+ runId: state.run.id,
1363
+ agentName: name,
1364
+ sessionId: state.session.id,
1365
+ messages,
1366
+ tools: allTools,
1367
+ contextBudget,
1368
+ compactor: compaction.compactor,
1369
+ estimateTokens: compaction.estimateTokens,
1370
+ step,
1371
+ ...request.context === void 0 ? {} : { context: request.context },
1372
+ signal: request.signal,
1373
+ allowCompaction: !compactionApplied
1374
+ });
1375
+ if (compacted !== void 0) {
1376
+ messages = compacted.messages;
1377
+ compactionApplied = true;
1378
+ const previous = state.session.compaction;
1379
+ const combined = previous === void 0 ? compacted.summary : `${previous.summary}\n\n${compacted.summary}`;
1380
+ const maxSummaryChars = Math.max(200, contextBudget.targetTokens * 4);
1381
+ state.session.compaction = {
1382
+ summary: combined.length > maxSummaryChars ? `…${combined.slice(combined.length - maxSummaryChars + 1)}` : combined,
1383
+ coveredMessages: state.session.history.length,
1384
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1385
+ };
1386
+ const summaryTruncated = compacted.summary.length > MAX_COMPACTION_EVENT_SUMMARY_CHARS;
1387
+ const event = createEvent(state, "context.compacted", {
1388
+ step,
1389
+ removedMessages: compacted.removedMessages,
1390
+ estimatedTokens: compacted.estimatedTokens,
1391
+ compactedTokens: compacted.compactedTokens,
1392
+ targetTokens: contextBudget.targetTokens,
1393
+ contextWindow: contextBudget.contextWindow,
1394
+ summary: summaryTruncated ? `${compacted.summary.slice(0, MAX_COMPACTION_EVENT_SUMMARY_CHARS - 1)}…` : compacted.summary,
1395
+ ...summaryTruncated ? { summaryTruncated: true } : {}
1396
+ });
1397
+ if (isDurableRunStore(runStore)) {
1398
+ const checkpoint = await durableCheckpoint(state, model, modelName, messages, inputContent, usage, providerState, step, toolCallCount, seenToolCallIds, pendingTools, pendingIndex);
1399
+ await commit(state, {
1400
+ checkpoint,
1401
+ session: state.session,
1402
+ events: [event]
1403
+ });
1404
+ state.checkpoint = checkpoint;
1405
+ } else {
1406
+ await runStore.appendEvent(event);
1407
+ notifyObserver(event);
1408
+ }
1409
+ events.push(event);
1410
+ yield event;
1411
+ }
1412
+ }
1209
1413
  const hasToolBudget = step < maxSteps && toolCallCount < maxToolCalls;
1210
1414
  const modelToolChoice = hasToolBudget && allTools.length ? currentToolChoice === "auto" ? void 0 : currentToolChoice : void 0;
1211
1415
  yield await emit("model.started", { step });
@@ -1287,6 +1491,57 @@ function createAgentEngine(composition, core) {
1287
1491
  assertContinuationBudget(name, "maxInputTokens", "inputTokens", effectiveLimits?.maxInputTokens, usage);
1288
1492
  assertContinuationBudget(name, "maxOutputTokens", "outputTokens", effectiveLimits?.maxOutputTokens, usage);
1289
1493
  if (result.toolCalls.length > maxToolCalls - toolCallCount) throw new Error(`Agent "${name}" exceeded maxToolCalls limit of ${maxToolCalls}`);
1494
+ const loadSkillCall = progressiveSkills.length === 0 ? void 0 : result.toolCalls.find((call) => call.name === LOAD_SKILL_TOOL_NAME);
1495
+ if (loadSkillCall) {
1496
+ if (result.toolCalls.length !== 1) throw new Error(`${LOAD_SKILL_TOOL_NAME} must be the only tool call in a model turn`);
1497
+ if (!loadSkillCall.id?.trim()) throw new Error("Tool call id cannot be empty");
1498
+ if (seenToolCallIds.has(loadSkillCall.id)) throw new Error(`Tool call id "${loadSkillCall.id}" is duplicated in run "${state.run.id}"`);
1499
+ if (toolCallCount + 1 > maxToolCalls) throw new Error(`Agent "${name}" exceeded maxToolCalls limit of ${maxToolCalls}`);
1500
+ const rawInput = toJsonValue(loadSkillCall.input, `Input for tool "${LOAD_SKILL_TOOL_NAME}" must be JSON-serializable`);
1501
+ const skillInput = readSkillInput(rawInput);
1502
+ const skill = progressiveSkills.find((item) => item.name === skillInput.name);
1503
+ if (!skill) throw new Error(`Skill "${skillInput.name}" is not available to agent "${name}"`);
1504
+ seenToolCallIds.add(loadSkillCall.id);
1505
+ messages.push({
1506
+ role: "assistant",
1507
+ content: result.output === void 0 ? "" : serializeValue(result.output, `Model output for agent "${name}" must be serializable`),
1508
+ toolCalls: [{
1509
+ ...loadSkillCall,
1510
+ input: rawInput
1511
+ }]
1512
+ });
1513
+ yield await emit("tool.started", {
1514
+ step,
1515
+ toolCallId: loadSkillCall.id,
1516
+ toolName: LOAD_SKILL_TOOL_NAME
1517
+ });
1518
+ let blocks;
1519
+ try {
1520
+ blocks = await abortable(() => skill.load(knowledgeContext(state, inputContent), skillInput.resource), request.signal);
1521
+ } catch (error) {
1522
+ yield await emit("tool.failed", {
1523
+ step,
1524
+ toolCallId: loadSkillCall.id,
1525
+ toolName: LOAD_SKILL_TOOL_NAME,
1526
+ error: toErrorMessage(error)
1527
+ });
1528
+ throw new Error(`Skill provider "${skill.name}" failed: ${toErrorMessage(error)}`, { cause: error });
1529
+ }
1530
+ messages.push({
1531
+ role: "tool",
1532
+ name: LOAD_SKILL_TOOL_NAME,
1533
+ toolCallId: loadSkillCall.id,
1534
+ content: blocks.filter((block) => block.content.trim()).map((block) => `[Skill: ${skill.name}/${block.id}]\n${block.content}`).join("\n\n")
1535
+ });
1536
+ yield await emit("tool.completed", {
1537
+ step,
1538
+ toolCallId: loadSkillCall.id,
1539
+ toolName: LOAD_SKILL_TOOL_NAME
1540
+ });
1541
+ toolCallCount += 1;
1542
+ step += 1;
1543
+ continue;
1544
+ }
1290
1545
  const elicitationCall = result.toolCalls.find(isElicitationToolCall);
1291
1546
  if (elicitationCall) {
1292
1547
  if (!isDurableRunStore(runStore)) throw new FevexRunError("DURABLE_STORE_REQUIRED", "Elicitation requires a DurableRunStore", state.run.id);
@@ -2036,6 +2291,26 @@ function createAgentEngine(composition, core) {
2036
2291
  cancelStoredAgent
2037
2292
  };
2038
2293
  }
2294
+ function sessionHistoryMessages(session) {
2295
+ const compaction = session.compaction;
2296
+ if (compaction === void 0 || compaction.coveredMessages <= 0 || compaction.coveredMessages > session.history.length) return session.history;
2297
+ return [{
2298
+ role: "system",
2299
+ content: `[Compacted conversation]\n${compaction.summary}`
2300
+ }, ...session.history.slice(compaction.coveredMessages)];
2301
+ }
2302
+ function readSkillInput(input) {
2303
+ if (typeof input !== "object" || input === null || Array.isArray(input)) throw new TypeError(`${LOAD_SKILL_TOOL_NAME} input must be an object`);
2304
+ const record = input;
2305
+ const name = record.name;
2306
+ const resource = record.resource;
2307
+ if (typeof name !== "string" || !name.trim()) throw new TypeError(`${LOAD_SKILL_TOOL_NAME} name must be a non-empty string`);
2308
+ if (resource !== void 0 && (typeof resource !== "string" || !resource.trim())) throw new TypeError(`${LOAD_SKILL_TOOL_NAME} resource must be a non-empty string`);
2309
+ return {
2310
+ name: name.trim(),
2311
+ ...resource === void 0 ? {} : { resource: resource.trim() }
2312
+ };
2313
+ }
2039
2314
  function scopedArtifacts(store, options) {
2040
2315
  const scopedContext = (value) => {
2041
2316
  const contextTenant = options.request.context?.attributes?.tenantId;
@@ -2195,6 +2470,17 @@ function annotation(event) {
2195
2470
  delayMs: event.payload.delayMs
2196
2471
  }
2197
2472
  };
2473
+ case "context.compacted": return {
2474
+ name: event.type,
2475
+ timestamp: event.timestamp,
2476
+ attributes: {
2477
+ removedMessages: event.payload.removedMessages,
2478
+ estimatedTokens: event.payload.estimatedTokens,
2479
+ compactedTokens: event.payload.compactedTokens,
2480
+ targetTokens: event.payload.targetTokens,
2481
+ contextWindow: event.payload.contextWindow
2482
+ }
2483
+ };
2198
2484
  case "tool.execution_unknown": return {
2199
2485
  name: event.type,
2200
2486
  timestamp: event.timestamp,
@@ -2793,12 +3079,12 @@ function createRunCore({ models, agents, tools, runStore, policies, onEvent, obs
2793
3079
  };
2794
3080
  const resolveCheckpointModel = async (agentName, agent, checkpoint) => {
2795
3081
  const preferred = resolveModel(agent, checkpoint.modelName);
2796
- if (checkpoint.definitionHash === await definitionHash(agentName, preferred.modelName, agent, tools)) return preferred;
3082
+ if (checkpoint.definitionHash === await definitionHash(agentName, preferred.modelName, agent, tools, checkpoint.contextBudget)) return preferred;
2797
3083
  if (checkpoint.modelName !== void 0) return void 0;
2798
3084
  for (const candidateName of models.keys()) {
2799
3085
  if (candidateName === preferred.modelName) continue;
2800
3086
  const candidate = resolveModel(agent, candidateName);
2801
- if (checkpoint.definitionHash === await definitionHash(agentName, candidate.modelName, agent, tools)) return candidate;
3087
+ if (checkpoint.definitionHash === await definitionHash(agentName, candidate.modelName, agent, tools, checkpoint.contextBudget)) return candidate;
2802
3088
  }
2803
3089
  };
2804
3090
  const scheduleTraceExport = (event) => {
@@ -4319,6 +4605,7 @@ function createRuntime(composition) {
4319
4605
  role: "system",
4320
4606
  content: summary
4321
4607
  }];
4608
+ session.compaction = void 0;
4322
4609
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
4323
4610
  await runStore.saveSession(session);
4324
4611
  return structuredClone(session);
@@ -4344,4 +4631,4 @@ function createFevex(config) {
4344
4631
  return createRuntime(createComposition(config));
4345
4632
  }
4346
4633
  //#endregion
4347
- 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, createFevex, createLocalSandbox, createMemoryContextProvider, defineAgent, defineConnection, defineContextProvider, definePolicy, defineSkill, defineTeam, defineTool, defineWorkflow, deliverChannelRun, handleChannelInput, isDurableRunStore, isRetainableRunStore, sandboxResultToJson, toToolSpec, validateFevexJsonSchemaProfile, validateListEventsOptions, validatePurgeRunsOptions };
4634
+ 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, createDigestContextCompactor, createFevex, createLocalSandbox, createMemoryContextProvider, defineAgent, defineConnection, defineContextProvider, definePolicy, defineSkill, defineTeam, defineTool, defineWorkflow, deliverChannelRun, estimateContextTokens, handleChannelInput, isDurableRunStore, isProgressiveSkillProvider, isRetainableRunStore, loadSkillFile, loadSkillFiles, resolveContextBudget, sandboxResultToJson, toToolSpec, validateFevexJsonSchemaProfile, validateListEventsOptions, validatePurgeRunsOptions };
@@ -1,2 +1,2 @@
1
- import { a as MemoryQuery, c as MemoryWrite, d as defineContextProvider, f as defineSkill, i as KnowledgeContext, l as SkillDefinition, n as ContextBlock, o as MemoryRecord, r as ContextProvider, s as MemoryStore, t as InMemoryMemoryStore, u as createMemoryContextProvider } from "../index-BJktlD4E.mjs";
2
- export { ContextBlock, ContextProvider, InMemoryMemoryStore, KnowledgeContext, MemoryQuery, MemoryRecord, MemoryStore, MemoryWrite, SkillDefinition, createMemoryContextProvider, defineContextProvider, defineSkill };
1
+ import { _ as defineContextProvider, a as AgentsMdOptions, c as ContextProvider, d as MemoryRecord, f as MemoryStore, g as createMemoryContextProvider, h as SkillDefinition, i as InMemoryMemoryStore, l as KnowledgeContext, m as ProgressiveSkillProvider, n as loadSkillFile, o as createAgentsMdContextProvider, p as MemoryWrite, r as loadSkillFiles, s as ContextBlock, t as LoadSkillFileOptions, u as MemoryQuery, v as defineSkill, y as isProgressiveSkillProvider } from "../index-CAyAoOW0.mjs";
2
+ export { AgentsMdOptions, ContextBlock, ContextProvider, InMemoryMemoryStore, KnowledgeContext, LoadSkillFileOptions, MemoryQuery, MemoryRecord, MemoryStore, MemoryWrite, ProgressiveSkillProvider, SkillDefinition, createAgentsMdContextProvider, createMemoryContextProvider, defineContextProvider, defineSkill, isProgressiveSkillProvider, loadSkillFile, loadSkillFiles };