@aigne/core 1.72.0-beta.2 → 1.72.0-beta.23

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 (175) hide show
  1. package/CHANGELOG.md +261 -0
  2. package/lib/cjs/agents/agent.d.ts +42 -11
  3. package/lib/cjs/agents/agent.js +34 -8
  4. package/lib/cjs/agents/ai-agent.d.ts +63 -4
  5. package/lib/cjs/agents/ai-agent.js +154 -20
  6. package/lib/cjs/agents/chat-model.d.ts +157 -0
  7. package/lib/cjs/agents/chat-model.js +71 -6
  8. package/lib/cjs/agents/image-agent.d.ts +17 -1
  9. package/lib/cjs/agents/image-agent.js +16 -0
  10. package/lib/cjs/agents/image-model.d.ts +12 -2
  11. package/lib/cjs/agents/image-model.js +1 -1
  12. package/lib/cjs/agents/mcp-agent.d.ts +17 -0
  13. package/lib/cjs/agents/mcp-agent.js +18 -0
  14. package/lib/cjs/agents/model.d.ts +3 -3
  15. package/lib/cjs/agents/model.js +2 -2
  16. package/lib/cjs/agents/team-agent.d.ts +55 -0
  17. package/lib/cjs/agents/team-agent.js +31 -0
  18. package/lib/cjs/agents/transform-agent.d.ts +12 -0
  19. package/lib/cjs/agents/transform-agent.js +13 -0
  20. package/lib/cjs/agents/video-model.d.ts +10 -0
  21. package/lib/cjs/agents/video-model.js +1 -1
  22. package/lib/cjs/aigne/context.js +1 -3
  23. package/lib/cjs/aigne/usage.d.ts +4 -0
  24. package/lib/cjs/aigne/usage.js +6 -0
  25. package/lib/cjs/index.d.ts +1 -0
  26. package/lib/cjs/index.js +1 -0
  27. package/lib/cjs/loader/agent-yaml.d.ts +5 -63
  28. package/lib/cjs/loader/agent-yaml.js +4 -129
  29. package/lib/cjs/loader/agents.d.ts +4 -0
  30. package/lib/cjs/loader/agents.js +17 -0
  31. package/lib/cjs/loader/index.d.ts +16 -12
  32. package/lib/cjs/loader/index.js +20 -81
  33. package/lib/cjs/loader/schema.d.ts +21 -6
  34. package/lib/cjs/loader/schema.js +60 -1
  35. package/lib/cjs/memory/recorder.d.ts +4 -4
  36. package/lib/cjs/memory/retriever.d.ts +4 -4
  37. package/lib/cjs/prompt/agent-session.d.ts +163 -0
  38. package/lib/cjs/prompt/agent-session.js +1008 -0
  39. package/lib/cjs/prompt/compact/compactor.d.ts +7 -0
  40. package/lib/cjs/prompt/compact/compactor.js +52 -0
  41. package/lib/cjs/prompt/compact/session-memory-extractor.d.ts +7 -0
  42. package/lib/cjs/prompt/compact/session-memory-extractor.js +143 -0
  43. package/lib/cjs/prompt/compact/types.d.ts +336 -0
  44. package/lib/cjs/prompt/compact/types.js +53 -0
  45. package/lib/cjs/prompt/compact/user-memory-extractor.d.ts +7 -0
  46. package/lib/cjs/prompt/compact/user-memory-extractor.js +124 -0
  47. package/lib/cjs/prompt/context/afs/history.d.ts +5 -1
  48. package/lib/cjs/prompt/context/afs/history.js +3 -2
  49. package/lib/cjs/prompt/context/afs/index.js +8 -1
  50. package/lib/cjs/prompt/prompt-builder.d.ts +11 -9
  51. package/lib/cjs/prompt/prompt-builder.js +79 -120
  52. package/lib/cjs/prompt/skills/afs/agent-skill/agent-skill.d.ts +19 -0
  53. package/lib/cjs/prompt/skills/afs/agent-skill/agent-skill.js +69 -0
  54. package/lib/cjs/prompt/skills/afs/agent-skill/skill-loader.d.ts +12 -0
  55. package/lib/cjs/prompt/skills/afs/agent-skill/skill-loader.js +50 -0
  56. package/lib/cjs/prompt/skills/afs/delete.js +15 -3
  57. package/lib/cjs/prompt/skills/afs/edit.d.ts +6 -9
  58. package/lib/cjs/prompt/skills/afs/edit.js +85 -59
  59. package/lib/cjs/prompt/skills/afs/exec.js +17 -6
  60. package/lib/cjs/prompt/skills/afs/index.js +4 -1
  61. package/lib/cjs/prompt/skills/afs/list.d.ts +2 -0
  62. package/lib/cjs/prompt/skills/afs/list.js +35 -11
  63. package/lib/cjs/prompt/skills/afs/read.d.ts +9 -3
  64. package/lib/cjs/prompt/skills/afs/read.js +67 -15
  65. package/lib/cjs/prompt/skills/afs/rename.js +18 -4
  66. package/lib/cjs/prompt/skills/afs/search.js +21 -5
  67. package/lib/cjs/prompt/skills/afs/write.js +20 -6
  68. package/lib/cjs/prompt/template.d.ts +84 -9
  69. package/lib/cjs/prompt/template.js +46 -17
  70. package/lib/cjs/utils/mcp-utils.js +1 -1
  71. package/lib/cjs/utils/token-estimator.js +1 -1
  72. package/lib/dts/agents/agent.d.ts +42 -11
  73. package/lib/dts/agents/ai-agent.d.ts +63 -4
  74. package/lib/dts/agents/chat-model.d.ts +157 -0
  75. package/lib/dts/agents/image-agent.d.ts +17 -1
  76. package/lib/dts/agents/image-model.d.ts +12 -2
  77. package/lib/dts/agents/mcp-agent.d.ts +17 -0
  78. package/lib/dts/agents/model.d.ts +3 -3
  79. package/lib/dts/agents/team-agent.d.ts +55 -0
  80. package/lib/dts/agents/transform-agent.d.ts +12 -0
  81. package/lib/dts/agents/video-model.d.ts +10 -0
  82. package/lib/dts/aigne/context.d.ts +2 -2
  83. package/lib/dts/aigne/usage.d.ts +4 -0
  84. package/lib/dts/index.d.ts +1 -0
  85. package/lib/dts/loader/agent-yaml.d.ts +5 -63
  86. package/lib/dts/loader/agents.d.ts +4 -0
  87. package/lib/dts/loader/index.d.ts +16 -12
  88. package/lib/dts/loader/schema.d.ts +21 -6
  89. package/lib/dts/memory/recorder.d.ts +4 -4
  90. package/lib/dts/memory/retriever.d.ts +4 -4
  91. package/lib/dts/prompt/agent-session.d.ts +163 -0
  92. package/lib/dts/prompt/compact/compactor.d.ts +7 -0
  93. package/lib/dts/prompt/compact/session-memory-extractor.d.ts +7 -0
  94. package/lib/dts/prompt/compact/types.d.ts +336 -0
  95. package/lib/dts/prompt/compact/user-memory-extractor.d.ts +7 -0
  96. package/lib/dts/prompt/context/afs/history.d.ts +5 -1
  97. package/lib/dts/prompt/prompt-builder.d.ts +11 -9
  98. package/lib/dts/prompt/skills/afs/agent-skill/agent-skill.d.ts +19 -0
  99. package/lib/dts/prompt/skills/afs/agent-skill/skill-loader.d.ts +12 -0
  100. package/lib/dts/prompt/skills/afs/edit.d.ts +6 -9
  101. package/lib/dts/prompt/skills/afs/list.d.ts +2 -0
  102. package/lib/dts/prompt/skills/afs/read.d.ts +9 -3
  103. package/lib/dts/prompt/template.d.ts +84 -9
  104. package/lib/esm/agents/agent.d.ts +42 -11
  105. package/lib/esm/agents/agent.js +34 -8
  106. package/lib/esm/agents/ai-agent.d.ts +63 -4
  107. package/lib/esm/agents/ai-agent.js +154 -20
  108. package/lib/esm/agents/chat-model.d.ts +157 -0
  109. package/lib/esm/agents/chat-model.js +70 -5
  110. package/lib/esm/agents/image-agent.d.ts +17 -1
  111. package/lib/esm/agents/image-agent.js +16 -0
  112. package/lib/esm/agents/image-model.d.ts +12 -2
  113. package/lib/esm/agents/image-model.js +1 -1
  114. package/lib/esm/agents/mcp-agent.d.ts +17 -0
  115. package/lib/esm/agents/mcp-agent.js +18 -0
  116. package/lib/esm/agents/model.d.ts +3 -3
  117. package/lib/esm/agents/model.js +2 -2
  118. package/lib/esm/agents/team-agent.d.ts +55 -0
  119. package/lib/esm/agents/team-agent.js +31 -0
  120. package/lib/esm/agents/transform-agent.d.ts +12 -0
  121. package/lib/esm/agents/transform-agent.js +13 -0
  122. package/lib/esm/agents/video-model.d.ts +10 -0
  123. package/lib/esm/agents/video-model.js +1 -1
  124. package/lib/esm/aigne/context.d.ts +2 -2
  125. package/lib/esm/aigne/context.js +2 -4
  126. package/lib/esm/aigne/usage.d.ts +4 -0
  127. package/lib/esm/aigne/usage.js +6 -0
  128. package/lib/esm/index.d.ts +1 -0
  129. package/lib/esm/index.js +1 -0
  130. package/lib/esm/loader/agent-yaml.d.ts +5 -63
  131. package/lib/esm/loader/agent-yaml.js +4 -128
  132. package/lib/esm/loader/agents.d.ts +4 -0
  133. package/lib/esm/loader/agents.js +14 -0
  134. package/lib/esm/loader/index.d.ts +16 -12
  135. package/lib/esm/loader/index.js +21 -81
  136. package/lib/esm/loader/schema.d.ts +21 -6
  137. package/lib/esm/loader/schema.js +57 -0
  138. package/lib/esm/memory/recorder.d.ts +4 -4
  139. package/lib/esm/memory/retriever.d.ts +4 -4
  140. package/lib/esm/prompt/agent-session.d.ts +163 -0
  141. package/lib/esm/prompt/agent-session.js +968 -0
  142. package/lib/esm/prompt/compact/compactor.d.ts +7 -0
  143. package/lib/esm/prompt/compact/compactor.js +48 -0
  144. package/lib/esm/prompt/compact/session-memory-extractor.d.ts +7 -0
  145. package/lib/esm/prompt/compact/session-memory-extractor.js +139 -0
  146. package/lib/esm/prompt/compact/types.d.ts +336 -0
  147. package/lib/esm/prompt/compact/types.js +50 -0
  148. package/lib/esm/prompt/compact/user-memory-extractor.d.ts +7 -0
  149. package/lib/esm/prompt/compact/user-memory-extractor.js +120 -0
  150. package/lib/esm/prompt/context/afs/history.d.ts +5 -1
  151. package/lib/esm/prompt/context/afs/history.js +3 -2
  152. package/lib/esm/prompt/context/afs/index.js +8 -1
  153. package/lib/esm/prompt/prompt-builder.d.ts +11 -9
  154. package/lib/esm/prompt/prompt-builder.js +80 -121
  155. package/lib/esm/prompt/skills/afs/agent-skill/agent-skill.d.ts +19 -0
  156. package/lib/esm/prompt/skills/afs/agent-skill/agent-skill.js +65 -0
  157. package/lib/esm/prompt/skills/afs/agent-skill/skill-loader.d.ts +12 -0
  158. package/lib/esm/prompt/skills/afs/agent-skill/skill-loader.js +43 -0
  159. package/lib/esm/prompt/skills/afs/delete.js +15 -3
  160. package/lib/esm/prompt/skills/afs/edit.d.ts +6 -9
  161. package/lib/esm/prompt/skills/afs/edit.js +85 -59
  162. package/lib/esm/prompt/skills/afs/exec.js +17 -6
  163. package/lib/esm/prompt/skills/afs/index.js +4 -1
  164. package/lib/esm/prompt/skills/afs/list.d.ts +2 -0
  165. package/lib/esm/prompt/skills/afs/list.js +35 -11
  166. package/lib/esm/prompt/skills/afs/read.d.ts +9 -3
  167. package/lib/esm/prompt/skills/afs/read.js +67 -15
  168. package/lib/esm/prompt/skills/afs/rename.js +18 -4
  169. package/lib/esm/prompt/skills/afs/search.js +21 -5
  170. package/lib/esm/prompt/skills/afs/write.js +20 -6
  171. package/lib/esm/prompt/template.d.ts +84 -9
  172. package/lib/esm/prompt/template.js +46 -17
  173. package/lib/esm/utils/mcp-utils.js +1 -1
  174. package/lib/esm/utils/token-estimator.js +1 -1
  175. package/package.json +7 -6
@@ -1,8 +1,11 @@
1
1
  import { z } from "zod";
2
+ import { getNestAgentSchema } from "../loader/agent-yaml.js";
3
+ import { camelizeSchema, getInstructionsSchema, instructionsToPromptBuilder, optionalize, } from "../loader/schema.js";
2
4
  import { PromptBuilder } from "../prompt/prompt-builder.js";
3
5
  import { STRUCTURED_STREAM_INSTRUCTIONS } from "../prompt/prompts/structured-stream-instructions.js";
4
- import { AgentMessageTemplate, ToolMessageTemplate } from "../prompt/template.js";
6
+ import { AgentSkill } from "../prompt/skills/afs/agent-skill/agent-skill.js";
5
7
  import * as fastq from "../utils/queue.js";
8
+ import { mergeAgentResponseChunk } from "../utils/stream-utils.js";
6
9
  import { ExtractMetadataTransform } from "../utils/structured-stream-extractor.js";
7
10
  import { checkArguments, isEmpty } from "../utils/type-utils.js";
8
11
  import { Agent, agentOptionsSchema, isAgentResponseDelta, } from "./agent.js";
@@ -84,6 +87,91 @@ export const aiAgentOptionsSchema = agentOptionsSchema.extend({
84
87
  */
85
88
  export class AIAgent extends Agent {
86
89
  tag = "AIAgent";
90
+ static schema({ filepath }) {
91
+ const instructionsSchema = getInstructionsSchema({ filepath });
92
+ const nestAgentSchema = getNestAgentSchema({ filepath });
93
+ const schema = camelizeSchema(z.object({
94
+ instructions: optionalize(instructionsSchema),
95
+ inputKey: optionalize(z.string()),
96
+ outputKey: optionalize(z.string()),
97
+ inputFileKey: optionalize(z.string()),
98
+ outputFileKey: optionalize(z.string()),
99
+ toolChoice: optionalize(z.nativeEnum(AIAgentToolChoice)),
100
+ toolCallsConcurrency: optionalize(z.number().int().min(0)),
101
+ keepTextInToolUses: optionalize(z.boolean()),
102
+ catchToolsError: optionalize(z.boolean()),
103
+ structuredStreamMode: optionalize(z.boolean()),
104
+ session: optionalize(camelizeSchema(z.object({
105
+ mode: optionalize(z.enum(["auto", "disabled"])),
106
+ sessionMemory: optionalize(camelizeSchema(z.object({
107
+ mode: optionalize(z.enum(["auto", "disabled"])),
108
+ memoryRatio: optionalize(z.number().min(0).max(1)),
109
+ queryLimit: optionalize(z.number().int().min(0)),
110
+ async: optionalize(z.boolean()),
111
+ extractor: optionalize(nestAgentSchema),
112
+ }))),
113
+ userMemory: optionalize(camelizeSchema(z.object({
114
+ mode: optionalize(z.enum(["auto", "disabled"])),
115
+ memoryRatio: optionalize(z.number().min(0).max(1)),
116
+ queryLimit: optionalize(z.number().int().min(0)),
117
+ async: optionalize(z.boolean()),
118
+ extractor: optionalize(nestAgentSchema),
119
+ }))),
120
+ compact: optionalize(camelizeSchema(z.object({
121
+ mode: optionalize(z.enum(["auto", "disabled"])),
122
+ maxTokens: z.number().int().min(0).optional(),
123
+ keepRecentRatio: optionalize(z.number().min(0).max(1)),
124
+ async: optionalize(z.boolean()),
125
+ compactor: optionalize(nestAgentSchema),
126
+ }))),
127
+ }))),
128
+ }));
129
+ return schema;
130
+ }
131
+ static async load(options) {
132
+ const schema = AIAgent.schema(options);
133
+ const valid = await schema.parseAsync(options.parsed);
134
+ // Load nested agents from session config if present
135
+ const sessionCompactor = valid.session?.compact?.compactor
136
+ ? await options.options?.loadNestAgent(options.filepath, valid.session.compact.compactor, options.options)
137
+ : undefined;
138
+ const sessionMemoryExtractor = valid.session?.sessionMemory?.extractor
139
+ ? await options.options?.loadNestAgent(options.filepath, valid.session.sessionMemory.extractor, options.options)
140
+ : undefined;
141
+ const userMemoryExtractor = valid.session?.userMemory?.extractor
142
+ ? await options.options?.loadNestAgent(options.filepath, valid.session.userMemory.extractor, options.options)
143
+ : undefined;
144
+ // Build session configuration with loaded agents
145
+ const sessionConfig = valid.session
146
+ ? {
147
+ ...valid.session,
148
+ compact: valid.session.compact
149
+ ? {
150
+ ...valid.session.compact,
151
+ compactor: sessionCompactor,
152
+ }
153
+ : undefined,
154
+ sessionMemory: valid.session.sessionMemory
155
+ ? {
156
+ ...valid.session.sessionMemory,
157
+ extractor: sessionMemoryExtractor,
158
+ }
159
+ : undefined,
160
+ userMemory: valid.session.userMemory
161
+ ? {
162
+ ...valid.session.userMemory,
163
+ extractor: userMemoryExtractor,
164
+ }
165
+ : undefined,
166
+ }
167
+ : undefined;
168
+ return new AIAgent({
169
+ ...options.parsed,
170
+ ...valid,
171
+ instructions: valid.instructions && instructionsToPromptBuilder(valid.instructions),
172
+ session: sessionConfig,
173
+ });
174
+ }
87
175
  /**
88
176
  * Create an AIAgent with the specified options
89
177
  *
@@ -111,8 +199,6 @@ export class AIAgent extends Agent {
111
199
  typeof options.instructions === "string"
112
200
  ? PromptBuilder.from(options.instructions)
113
201
  : (options.instructions ?? new PromptBuilder());
114
- this.autoReorderSystemMessages = options.autoReorderSystemMessages ?? true;
115
- this.autoMergeSystemMessages = options.autoMergeSystemMessages ?? true;
116
202
  this.inputKey = options.inputKey;
117
203
  this.inputFileKey = options.inputFileKey;
118
204
  this.outputKey = options.outputKey || DEFAULT_OUTPUT_KEY;
@@ -124,6 +210,7 @@ export class AIAgent extends Agent {
124
210
  this.memoryAgentsAsTools = options.memoryAgentsAsTools;
125
211
  this.memoryPromptTemplate = options.memoryPromptTemplate;
126
212
  this.useMemoriesFromContext = options.useMemoriesFromContext;
213
+ this.session = options.session;
127
214
  if (typeof options.catchToolsError === "boolean")
128
215
  this.catchToolsError = options.catchToolsError;
129
216
  this.structuredStreamMode = options.structuredStreamMode;
@@ -149,8 +236,6 @@ export class AIAgent extends Agent {
149
236
  * {@includeCode ../../test/agents/ai-agent.test.ts#example-ai-agent-prompt-builder}
150
237
  */
151
238
  instructions;
152
- autoReorderSystemMessages;
153
- autoMergeSystemMessages;
154
239
  /**
155
240
  * Pick a message from input to use as the user's message
156
241
  */
@@ -241,6 +326,10 @@ export class AIAgent extends Agent {
241
326
  * which outputs structured data in YAML format within <metadata> tags.
242
327
  */
243
328
  customStructuredStreamInstructions;
329
+ /**
330
+ * Agent session configuration
331
+ */
332
+ session;
244
333
  get inputSchema() {
245
334
  let schema = super.inputSchema;
246
335
  if (this.inputKey) {
@@ -267,7 +356,7 @@ export class AIAgent extends Agent {
267
356
  const model = this.model || options.model || options.context.model;
268
357
  if (!model)
269
358
  throw new Error("model is required to run AIAgent");
270
- const { toolAgents, ...modelInput } = await this.instructions.build({
359
+ const { toolAgents, session, userMessage, ...modelInput } = await this.instructions.build({
271
360
  ...options,
272
361
  agent: this,
273
362
  input,
@@ -276,25 +365,35 @@ export class AIAgent extends Agent {
276
365
  modelInput.modelOptions = await model.getModelOptions(input, options);
277
366
  const toolsMap = new Map(toolAgents?.map((i) => [i.name, i]));
278
367
  if (this.toolChoice === "router") {
279
- return yield* this._processRouter(input, model, modelInput, options, toolsMap);
368
+ return yield* this._processRouter(input, model, { messages: [...(await session.getMessages()), userMessage], ...modelInput }, options, toolsMap);
369
+ }
370
+ const inputMessage = this.inputKey ? input[this.inputKey] : undefined;
371
+ if (inputMessage) {
372
+ yield {
373
+ progress: {
374
+ event: "message",
375
+ message: { role: "user", content: [{ type: "text", text: inputMessage }] },
376
+ },
377
+ };
280
378
  }
281
- const toolCallMessages = [];
379
+ await session.startMessage(input, userMessage, options);
380
+ // const toolCallMessages: ChatModelInputMessage[] = [];
282
381
  const outputKey = this.outputKey;
283
382
  for (;;) {
284
383
  const modelOutput = {};
285
- let stream = await this.invokeChildAgent(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { ...options, streaming: true });
384
+ let stream = await this.invokeChildAgent(model, { messages: await session.getMessages(), ...modelInput }, { ...options, streaming: true });
286
385
  if (this.structuredStreamMode) {
287
386
  const { metadataStart, metadataEnd, parse } = this.customStructuredStreamInstructions || STRUCTURED_STREAM_INSTRUCTIONS;
288
387
  stream = stream.pipeThrough(new ExtractMetadataTransform({ start: metadataStart, end: metadataEnd, parse }));
289
388
  }
290
389
  let isTextIgnored = false;
291
390
  for await (const value of stream) {
391
+ mergeAgentResponseChunk(modelOutput, value);
292
392
  if (isAgentResponseDelta(value)) {
293
393
  if (!isTextIgnored && value.delta.text?.text) {
294
394
  yield { delta: { text: { [outputKey]: value.delta.text.text } } };
295
395
  }
296
396
  if (value.delta.json) {
297
- Object.assign(modelOutput, value.delta.json);
298
397
  if (this.structuredStreamMode) {
299
398
  yield { delta: { json: value.delta.json.json } };
300
399
  if (!isTextIgnored && modelOutput.json && this.ignoreTextOfStructuredStreamMode) {
@@ -304,14 +403,39 @@ export class AIAgent extends Agent {
304
403
  }
305
404
  }
306
405
  }
307
- const { toolCalls, json, text, files } = modelOutput;
308
- if (toolCalls?.length) {
406
+ const { toolCalls, json, text, thoughts, files } = modelOutput;
407
+ if (text || thoughts) {
408
+ const content = [];
409
+ if (thoughts) {
410
+ content.push({ type: "text", text: thoughts, isThinking: true });
411
+ }
412
+ if (text) {
413
+ content.push({ type: "text", text });
414
+ }
415
+ if (content.length) {
416
+ const message = { role: "agent", content };
417
+ yield { progress: { event: "message", message } };
418
+ await session.appendCurrentMessages(message, options);
419
+ }
420
+ }
421
+ const toolCallsWithTools = toolCalls?.map((call) => {
422
+ const tool = toolsMap.get(call.function.name);
423
+ if (!tool)
424
+ throw new Error(`Tool not found: ${call.function.name}`);
425
+ return {
426
+ ...call,
427
+ tool,
428
+ };
429
+ });
430
+ if (toolCallsWithTools?.length) {
309
431
  if (this.keepTextInToolUses !== true) {
310
432
  yield { delta: { json: { [outputKey]: "" } } };
311
433
  }
312
434
  else {
313
- yield { delta: { text: { [outputKey]: "\n" } } };
435
+ yield { delta: { text: { [outputKey]: "\n\n" } } };
314
436
  }
437
+ const toolCallMessage = { role: "agent", toolCalls };
438
+ yield { progress: { event: "message", message: toolCallMessage } };
315
439
  const executedToolCalls = [];
316
440
  let error;
317
441
  const queue = fastq.promise(async ({ tool, call }) => {
@@ -328,7 +452,7 @@ export class AIAgent extends Agent {
328
452
  },
329
453
  };
330
454
  });
331
- executedToolCalls.push({ call, output });
455
+ executedToolCalls.push({ call, tool, output });
332
456
  }
333
457
  catch (e) {
334
458
  error = e;
@@ -336,21 +460,30 @@ export class AIAgent extends Agent {
336
460
  }
337
461
  }, this.toolCallsConcurrency || 1);
338
462
  // Execute tools
339
- for (const call of toolCalls) {
340
- const tool = toolsMap.get(call.function.name);
341
- if (!tool)
342
- throw new Error(`Tool not found: ${call.function.name}`);
343
- queue.push({ tool, call });
463
+ for (const call of toolCallsWithTools) {
464
+ queue.push({ tool: call.tool, call });
344
465
  }
345
466
  await queue.drained();
346
467
  if (error)
347
468
  throw error;
469
+ const toolResultMessages = [];
348
470
  // Continue LLM function calling loop if any tools were executed
349
471
  if (executedToolCalls.length) {
472
+ for (const { call, tool, output } of executedToolCalls) {
473
+ const isAgentSkill = !output.isError && tool instanceof AgentSkill ? true : undefined;
474
+ const text = await tool.formatOutput(output);
475
+ const message = {
476
+ role: "tool",
477
+ toolCallId: call.id,
478
+ content: [{ type: "text", text, isAgentSkill }],
479
+ };
480
+ yield { progress: { event: "message", message: message } };
481
+ toolResultMessages.push(message);
482
+ }
350
483
  const transferOutput = executedToolCalls.find((i) => isTransferAgentOutput(i.output))?.output;
351
484
  if (transferOutput)
352
485
  return transferOutput;
353
- toolCallMessages.push(await AgentMessageTemplate.from(undefined, executedToolCalls.map(({ call }) => call)).format(), ...(await Promise.all(executedToolCalls.map(({ call, output }) => ToolMessageTemplate.from(output, call.id).format()))));
486
+ await session.appendCurrentMessages([toolCallMessage, ...toolResultMessages], options);
354
487
  continue;
355
488
  }
356
489
  }
@@ -367,6 +500,7 @@ export class AIAgent extends Agent {
367
500
  if (!isEmpty(result)) {
368
501
  yield { delta: { json: result } };
369
502
  }
503
+ await session.endMessage(result, undefined, options);
370
504
  return;
371
505
  }
372
506
  }
@@ -57,7 +57,9 @@ export declare abstract class ChatModel extends Model<ChatModelInput, ChatModelO
57
57
  getModelCapabilities(): {
58
58
  supportsParallelToolCalls: boolean;
59
59
  };
60
+ getModelOptions(input: Message, options: AgentInvokeOptions): Promise<ChatModelInputOptions>;
60
61
  private validateToolNames;
62
+ countTokens(input: ChatModelInput): Promise<number>;
61
63
  /**
62
64
  * Normalizes tool names to ensure compatibility with language models
63
65
  *
@@ -203,6 +205,13 @@ export interface ChatModelInputMessage {
203
205
  * Name of the message sender (for multi-agent scenarios)
204
206
  */
205
207
  name?: string;
208
+ /**
209
+ * Cache control marker for the entire message (only supported by Claude)
210
+ *
211
+ * This is syntactic sugar that applies cacheControl to the last content block
212
+ * of the message. See {@link CacheControl} for details.
213
+ */
214
+ cacheControl?: CacheControl;
206
215
  }
207
216
  /**
208
217
  * Type of input message content
@@ -218,27 +227,66 @@ export type ChatModelInputMessageContent = string | UnionContent[];
218
227
  export type TextContent = {
219
228
  type: "text";
220
229
  text: string;
230
+ isThinking?: boolean;
231
+ isAgentSkill?: boolean;
232
+ /**
233
+ * Cache control marker (only supported by Claude)
234
+ *
235
+ * When set, this content block will be marked as a cache breakpoint.
236
+ * See {@link CacheControl} for details.
237
+ */
238
+ cacheControl?: CacheControl;
221
239
  };
222
240
  export declare const textContentSchema: z.ZodObject<{
223
241
  type: z.ZodLiteral<"text">;
224
242
  text: z.ZodString;
243
+ cacheControl: ZodType<{
244
+ type: "ephemeral";
245
+ ttl?: "5m" | "1h" | undefined;
246
+ } | undefined, z.ZodTypeDef, {
247
+ type: "ephemeral";
248
+ ttl?: "5m" | "1h" | undefined;
249
+ } | undefined>;
225
250
  }, "strip", z.ZodTypeAny, {
226
251
  type: "text";
227
252
  text: string;
253
+ cacheControl?: {
254
+ type: "ephemeral";
255
+ ttl?: "5m" | "1h" | undefined;
256
+ } | undefined;
228
257
  }, {
229
258
  type: "text";
230
259
  text: string;
260
+ cacheControl?: {
261
+ type: "ephemeral";
262
+ ttl?: "5m" | "1h" | undefined;
263
+ } | undefined;
231
264
  }>;
232
265
  export type UnionContent = TextContent | FileUnionContent;
233
266
  export declare const unionContentSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
234
267
  type: z.ZodLiteral<"text">;
235
268
  text: z.ZodString;
269
+ cacheControl: ZodType<{
270
+ type: "ephemeral";
271
+ ttl?: "5m" | "1h" | undefined;
272
+ } | undefined, z.ZodTypeDef, {
273
+ type: "ephemeral";
274
+ ttl?: "5m" | "1h" | undefined;
275
+ } | undefined>;
236
276
  }, "strip", z.ZodTypeAny, {
237
277
  type: "text";
238
278
  text: string;
279
+ cacheControl?: {
280
+ type: "ephemeral";
281
+ ttl?: "5m" | "1h" | undefined;
282
+ } | undefined;
239
283
  }, {
240
284
  type: "text";
241
285
  text: string;
286
+ cacheControl?: {
287
+ type: "ephemeral";
288
+ ttl?: "5m" | "1h" | undefined;
289
+ } | undefined;
242
290
  }>, z.ZodObject<{
243
291
  filename: ZodType<string | undefined, z.ZodTypeDef, string | undefined>;
244
292
  mimeType: ZodType<string | undefined, z.ZodTypeDef, string | undefined>;
@@ -340,6 +388,14 @@ export interface ChatModelInputTool {
340
388
  * For example, Gemini's thought_signature
341
389
  */
342
390
  metadata?: Record<string, any>;
391
+ /**
392
+ * Cache control marker (only supported by Claude)
393
+ *
394
+ * When set, this tool definition will be marked as a cache breakpoint.
395
+ * Typically applied to the last tool in the tools array.
396
+ * See {@link CacheControl} for details.
397
+ */
398
+ cacheControl?: CacheControl;
343
399
  }
344
400
  /**
345
401
  * Tool selection strategy
@@ -362,6 +418,82 @@ export type ChatModelInputToolChoice = "auto" | "none" | "required" | {
362
418
  };
363
419
  };
364
420
  export type Modality = "text" | "image" | "audio";
421
+ /**
422
+ * Cache control marker for prompt caching
423
+ *
424
+ * Used to mark content blocks, messages, or tools for caching.
425
+ * Currently only supported by Anthropic (Claude) models.
426
+ */
427
+ export interface CacheControl {
428
+ /**
429
+ * Cache type (currently only "ephemeral" is supported)
430
+ */
431
+ type: "ephemeral";
432
+ /**
433
+ * Cache TTL (Time To Live)
434
+ * - "5m": 5 minutes (default)
435
+ * - "1h": 1 hour
436
+ */
437
+ ttl?: "5m" | "1h";
438
+ }
439
+ /**
440
+ * Cache configuration options
441
+ *
442
+ * Controls how prompt caching is used for supported providers.
443
+ * Prompt caching can significantly reduce costs and latency by reusing
444
+ * previously processed prompts (system messages, tool definitions, etc.).
445
+ */
446
+ export interface CacheConfig {
447
+ /**
448
+ * Whether to enable prompt caching
449
+ *
450
+ * - OpenAI: Ignored (always enabled automatically)
451
+ * - Gemini: Controls explicit caching
452
+ * - Claude: Controls whether to add cache_control markers
453
+ *
454
+ * @default true
455
+ */
456
+ enabled?: boolean;
457
+ /**
458
+ * Cache TTL (Time To Live)
459
+ *
460
+ * - OpenAI: Ignored (automatic)
461
+ * - Gemini: Supports custom seconds
462
+ * - Claude: Only supports "5m" or "1h"
463
+ *
464
+ * @default "5m"
465
+ */
466
+ ttl?: "5m" | "1h" | number;
467
+ /**
468
+ * Caching strategy
469
+ *
470
+ * - "auto": Automatically add cache breakpoints at optimal locations
471
+ * - "manual": Require explicit cacheControl markers on messages/tools
472
+ *
473
+ * @default "auto"
474
+ */
475
+ strategy?: "auto" | "manual";
476
+ /**
477
+ * Auto cache breakpoint locations (only effective when strategy="auto")
478
+ *
479
+ * @default { tools: true, system: true, lastMessage: false }
480
+ */
481
+ autoBreakpoints?: {
482
+ /** Cache tool definitions */
483
+ tools?: boolean;
484
+ /** Cache system messages */
485
+ system?: boolean;
486
+ /** Cache last message in conversation history */
487
+ lastMessage?: boolean;
488
+ };
489
+ }
490
+ /**
491
+ * Default cache configuration
492
+ *
493
+ * Enables automatic caching for system messages and tool definitions,
494
+ * which typically provides the best cost/performance tradeoff.
495
+ */
496
+ export declare const DEFAULT_CACHE_CONFIG: CacheConfig;
365
497
  /**
366
498
  * Model-specific configuration options
367
499
  *
@@ -395,6 +527,15 @@ export interface ChatModelInputOptions extends Record<string, unknown> {
395
527
  modalities?: Modality[];
396
528
  preferInputFileType?: "file" | "url";
397
529
  reasoningEffort?: number | "minimal" | "low" | "medium" | "high";
530
+ /**
531
+ * Cache configuration for prompt caching
532
+ *
533
+ * Enables caching of system messages, tool definitions, and conversation history
534
+ * to reduce costs and latency. See {@link CacheConfig} for details.
535
+ *
536
+ * @default DEFAULT_CACHE_CONFIG (enabled with auto strategy)
537
+ */
538
+ cacheConfig?: CacheConfig;
398
539
  }
399
540
  export type ChatModelInputOptionsWithGetter = GetterSchema<ChatModelInputOptions>;
400
541
  /**
@@ -492,6 +633,16 @@ export interface ChatModelOutputUsage {
492
633
  * AIGNE Hub credit usage
493
634
  */
494
635
  aigneHubCredits?: number;
636
+ /**
637
+ * Number of tokens written to cache (first time caching)
638
+ * Only applicable for providers that support explicit cache creation (e.g., Anthropic)
639
+ */
640
+ cacheCreationInputTokens?: number;
641
+ /**
642
+ * Number of tokens read from cache (cache hit)
643
+ * Supported by OpenAI, Anthropic, and Gemini
644
+ */
645
+ cacheReadInputTokens?: number;
495
646
  /**
496
647
  * Credit prefix
497
648
  */
@@ -501,15 +652,21 @@ export declare const chatModelOutputUsageSchema: z.ZodObject<{
501
652
  inputTokens: z.ZodNumber;
502
653
  outputTokens: z.ZodNumber;
503
654
  aigneHubCredits: ZodType<number | undefined, z.ZodTypeDef, number | undefined>;
655
+ cacheCreationInputTokens: ZodType<number | undefined, z.ZodTypeDef, number | undefined>;
656
+ cacheReadInputTokens: ZodType<number | undefined, z.ZodTypeDef, number | undefined>;
504
657
  creditPrefix: ZodType<"$" | "€" | "¥" | undefined, z.ZodTypeDef, "$" | "€" | "¥" | undefined>;
505
658
  }, "strip", z.ZodTypeAny, {
506
659
  inputTokens: number;
507
660
  outputTokens: number;
508
661
  aigneHubCredits?: number | undefined;
662
+ cacheCreationInputTokens?: number | undefined;
663
+ cacheReadInputTokens?: number | undefined;
509
664
  creditPrefix?: "$" | "€" | "¥" | undefined;
510
665
  }, {
511
666
  inputTokens: number;
512
667
  outputTokens: number;
513
668
  aigneHubCredits?: number | undefined;
669
+ cacheCreationInputTokens?: number | undefined;
670
+ cacheReadInputTokens?: number | undefined;
514
671
  creditPrefix?: "$" | "€" | "¥" | undefined;
515
672
  }>;
@@ -4,6 +4,7 @@ import { convertJsonSchemaToZod } from "zod-from-json-schema";
4
4
  import { optionalize } from "../loader/schema.js";
5
5
  import { wrapAutoParseJsonSchema } from "../utils/json-schema.js";
6
6
  import { logger } from "../utils/logger.js";
7
+ import { estimateTokens } from "../utils/token-estimator.js";
7
8
  import { checkArguments, isNil, omitByDeep } from "../utils/type-utils.js";
8
9
  import { agentOptionsSchema, getterSchema, } from "./agent.js";
9
10
  import { fileContentSchema, fileUnionContentSchema, localContentSchema, Model, urlContentSchema, } from "./model.js";
@@ -80,6 +81,19 @@ export class ChatModel extends Model {
80
81
  supportsParallelToolCalls: this.supportsParallelToolCalls,
81
82
  };
82
83
  }
84
+ async getModelOptions(input, options) {
85
+ const modelOptions = (await super.getModelOptions(input, options));
86
+ return {
87
+ ...modelOptions,
88
+ cacheConfig: {
89
+ ...modelOptions.cacheConfig,
90
+ autoBreakpoints: {
91
+ ...modelOptions.cacheConfig?.autoBreakpoints,
92
+ lastMessage: modelOptions.cacheConfig?.autoBreakpoints?.lastMessage ?? true,
93
+ },
94
+ },
95
+ };
96
+ }
83
97
  validateToolNames(tools) {
84
98
  for (const tool of tools ?? []) {
85
99
  if (!/^[a-zA-Z0-9_]+$/.test(tool.function.name)) {
@@ -87,6 +101,9 @@ export class ChatModel extends Model {
87
101
  }
88
102
  }
89
103
  }
104
+ async countTokens(input) {
105
+ return estimateTokens(JSON.stringify(input));
106
+ }
90
107
  /**
91
108
  * Normalizes tool names to ensure compatibility with language models
92
109
  *
@@ -188,6 +205,10 @@ export class ChatModel extends Model {
188
205
  options.context.usage.inputTokens += usage.inputTokens;
189
206
  if (usage.aigneHubCredits)
190
207
  options.context.usage.aigneHubCredits += usage.aigneHubCredits;
208
+ if (usage.cacheCreationInputTokens)
209
+ options.context.usage.cacheCreationInputTokens += usage.cacheCreationInputTokens;
210
+ if (usage.cacheReadInputTokens)
211
+ options.context.usage.cacheReadInputTokens += usage.cacheReadInputTokens;
191
212
  if (usage.creditPrefix)
192
213
  options.context.usage.creditPrefix = usage.creditPrefix;
193
214
  }
@@ -197,7 +218,7 @@ export class ChatModel extends Model {
197
218
  const files = z.array(fileUnionContentSchema).parse(output.files);
198
219
  output = {
199
220
  ...output,
200
- files: await Promise.all(files.map((file) => this.transformFileType(input.outputFileType, file, options))),
221
+ files: await Promise.all(files.map((file) => this.transformFileType(input.outputFileType, file))),
201
222
  };
202
223
  }
203
224
  // Remove fields with `null` value for validation
@@ -244,6 +265,10 @@ export const roleSchema = z.union([
244
265
  export const textContentSchema = z.object({
245
266
  type: z.literal("text"),
246
267
  text: z.string(),
268
+ cacheControl: optionalize(z.object({
269
+ type: z.literal("ephemeral"),
270
+ ttl: optionalize(z.union([z.literal("5m"), z.literal("1h")])),
271
+ })),
247
272
  });
248
273
  export const unionContentSchema = z.discriminatedUnion("type", [
249
274
  textContentSchema,
@@ -265,6 +290,10 @@ const chatModelInputMessageSchema = z.object({
265
290
  }))),
266
291
  toolCallId: optionalize(z.string()),
267
292
  name: optionalize(z.string()),
293
+ cacheControl: optionalize(z.object({
294
+ type: z.literal("ephemeral"),
295
+ ttl: optionalize(z.union([z.literal("5m"), z.literal("1h")])),
296
+ })),
268
297
  });
269
298
  const chatModelInputResponseFormatSchema = z.discriminatedUnion("type", [
270
299
  z.object({ type: z.literal("text") }),
@@ -286,6 +315,10 @@ const chatModelInputToolSchema = z.object({
286
315
  parameters: z.record(z.string(), z.unknown()),
287
316
  }),
288
317
  metadata: optionalize(z.record(z.string(), z.unknown())),
318
+ cacheControl: optionalize(z.object({
319
+ type: z.literal("ephemeral"),
320
+ ttl: optionalize(z.union([z.literal("5m"), z.literal("1h")])),
321
+ })),
289
322
  });
290
323
  const chatModelInputToolChoiceSchema = z.union([
291
324
  z.literal("auto"),
@@ -293,6 +326,22 @@ const chatModelInputToolChoiceSchema = z.union([
293
326
  z.literal("required"),
294
327
  chatModelInputToolSchema,
295
328
  ]);
329
+ /**
330
+ * Default cache configuration
331
+ *
332
+ * Enables automatic caching for system messages and tool definitions,
333
+ * which typically provides the best cost/performance tradeoff.
334
+ */
335
+ export const DEFAULT_CACHE_CONFIG = {
336
+ enabled: true,
337
+ ttl: "5m",
338
+ strategy: "auto",
339
+ autoBreakpoints: {
340
+ tools: true,
341
+ system: true,
342
+ lastMessage: false,
343
+ },
344
+ };
296
345
  const modelOptionsSchemaProperties = {
297
346
  model: z.string(),
298
347
  temperature: z.number(),
@@ -308,15 +357,29 @@ const modelOptionsSchemaProperties = {
308
357
  z.literal("medium"),
309
358
  z.literal("high"),
310
359
  ]),
360
+ cacheConfig: z.object({
361
+ enabled: optionalize(z.boolean().default(true)),
362
+ ttl: optionalize(z.union([z.literal("5m"), z.literal("1h"), z.number()]).default("5m")),
363
+ strategy: optionalize(z.union([z.literal("auto"), z.literal("manual")]).default("auto")),
364
+ autoBreakpoints: optionalize(z.object({
365
+ tools: optionalize(z.boolean().default(true)),
366
+ system: optionalize(z.boolean().default(true)),
367
+ lastMessage: optionalize(z.boolean().default(false)),
368
+ })),
369
+ }),
311
370
  };
312
- const modelOptionsSchema = z.object(Object.fromEntries(Object.entries(modelOptionsSchemaProperties).map(([key, schema]) => [
371
+ const modelOptionsSchema = z
372
+ .object(Object.fromEntries(Object.entries(modelOptionsSchemaProperties).map(([key, schema]) => [
313
373
  key,
314
374
  optionalize(schema),
315
- ])));
316
- const modelOptionsWithGetterSchema = z.object(Object.fromEntries(Object.entries(modelOptionsSchemaProperties).map(([key, schema]) => [
375
+ ])))
376
+ .passthrough();
377
+ const modelOptionsWithGetterSchema = z
378
+ .object(Object.fromEntries(Object.entries(modelOptionsSchemaProperties).map(([key, schema]) => [
317
379
  key,
318
380
  optionalize(getterSchema(schema)),
319
- ])));
381
+ ])))
382
+ .passthrough();
320
383
  const chatModelOptionsSchema = agentOptionsSchema.extend({
321
384
  model: optionalize(z.string()),
322
385
  modelOptions: optionalize(modelOptionsWithGetterSchema),
@@ -341,6 +404,8 @@ export const chatModelOutputUsageSchema = z.object({
341
404
  inputTokens: z.number(),
342
405
  outputTokens: z.number(),
343
406
  aigneHubCredits: optionalize(z.number()),
407
+ cacheCreationInputTokens: optionalize(z.number()),
408
+ cacheReadInputTokens: optionalize(z.number()),
344
409
  creditPrefix: optionalize(z.union([z.literal("$"), z.literal("€"), z.literal("¥")])),
345
410
  });
346
411
  const chatModelOutputSchema = z.object({