@aigne/core 1.72.0-beta.6 → 1.72.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.72.0-beta.7](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.72.0-beta.6...core-v1.72.0-beta.7) (2025-12-26)
4
+
5
+
6
+ ### Features
7
+
8
+ * **core:** add session history support ([#858](https://github.com/AIGNE-io/aigne-framework/issues/858)) ([28a070e](https://github.com/AIGNE-io/aigne-framework/commit/28a070ed33b821d1fd344b899706d817ca992b9f))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @aigne/afs bumped to 1.4.0-beta.4
16
+ * @aigne/afs-history bumped to 1.2.0-beta.4
17
+
3
18
  ## [1.72.0-beta.6](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.72.0-beta.5...core-v1.72.0-beta.6) (2025-12-25)
4
19
 
5
20
 
@@ -9,7 +9,7 @@ import type { Memory, MemoryAgent } from "../memory/memory.js";
9
9
  import type { MemoryRecorderInput } from "../memory/recorder.js";
10
10
  import type { MemoryRetrieverInput } from "../memory/retriever.js";
11
11
  import { type Nullish, type PromiseOrValue, type XOr } from "../utils/type-utils.js";
12
- import type { ChatModel } from "./chat-model.js";
12
+ import type { ChatModel, ChatModelInputMessage } from "./chat-model.js";
13
13
  import type { GuideRailAgent, GuideRailAgentOutput } from "./guide-rail-agent.js";
14
14
  import type { ImageModel } from "./image-model.js";
15
15
  import { type GetterSchema, type TransferAgentOutput } from "./types.js";
@@ -350,7 +350,20 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
350
350
  */
351
351
  private disableEvents?;
352
352
  historyConfig?: {
353
- disabled?: boolean;
353
+ /**
354
+ * Whether to enable history recording and injection
355
+ * @default false
356
+ */
357
+ enabled?: boolean;
358
+ /**
359
+ * Whether to record history entries, default to enabled when history is enabled
360
+ */
361
+ record?: boolean;
362
+ /**
363
+ * Whether to inject history entries into the context, default to enabled when history is enabled
364
+ */
365
+ inject?: boolean;
366
+ useOldMemory?: boolean;
354
367
  maxTokens?: number;
355
368
  maxItems?: number;
356
369
  };
@@ -455,7 +468,9 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
455
468
  * @param options Invocation options
456
469
  * @returns Final processed output
457
470
  */
458
- protected processAgentOutput(input: I, output: Exclude<AgentResponse<O>, AgentResponseStream<O>>, options: AgentInvokeOptions): Promise<O>;
471
+ protected processAgentOutput(input: I, output: Exclude<AgentResponse<O>, AgentResponseStream<O>>, { messages, ...options }: AgentInvokeOptions & {
472
+ messages?: ChatModelInputMessage[];
473
+ }): Promise<O>;
459
474
  /**
460
475
  * Process errors that occur during agent execution
461
476
  *
@@ -785,29 +800,29 @@ export interface AgentResponseProgress {
785
800
  error: Error;
786
801
  } | {
787
802
  event: "message";
788
- role: "user" | "agent";
789
- message: ({
790
- type: "text";
791
- content: string;
792
- } | {
793
- type: "thinking";
794
- thoughts: string;
795
- } | {
796
- type: "tool_use";
797
- toolUseId: string;
798
- name: string;
799
- input: unknown;
800
- } | {
801
- type: "tool_result";
802
- toolUseId: string;
803
- content: unknown;
804
- })[];
803
+ message: ChatModelInputMessage;
805
804
  }) & Omit<AgentEvent, "agent"> & {
806
805
  agent: {
807
806
  name: string;
808
807
  };
809
808
  };
810
809
  }
810
+ export type AgentResponseProgressMessageItem = {
811
+ type: "text";
812
+ content: string;
813
+ } | {
814
+ type: "thinking";
815
+ thoughts: string;
816
+ } | {
817
+ type: "tool_use";
818
+ toolUseId: string;
819
+ name: string;
820
+ input: unknown;
821
+ } | {
822
+ type: "tool_result";
823
+ toolUseId: string;
824
+ content: unknown;
825
+ };
811
826
  export declare function isAgentResponseProgress<T>(chunk: AgentResponseChunk<T>): chunk is AgentResponseProgress;
812
827
  /**
813
828
  * Creates a text delta for streaming responses
@@ -463,11 +463,15 @@ class Agent {
463
463
  : (0, stream_utils_js_1.isAsyncGenerator)(response)
464
464
  ? (0, stream_utils_js_1.asyncGeneratorToReadableStream)(response)
465
465
  : (0, stream_utils_js_1.objectToAgentResponseStream)(response);
466
+ const messages = [];
466
467
  for await (const chunk of stream) {
467
468
  (0, stream_utils_js_1.mergeAgentResponseChunk)(output, chunk);
468
469
  yield chunk;
470
+ if (isAgentResponseProgress(chunk) && chunk.progress.event === "message") {
471
+ messages.push(chunk.progress.message);
472
+ }
469
473
  }
470
- let result = await this.processAgentOutput(input, output, options);
474
+ let result = await this.processAgentOutput(input, output, { ...options, messages });
471
475
  if (attempt > 0) {
472
476
  result = { ...result, $meta: { ...result.$meta, retries: attempt } };
473
477
  }
@@ -564,7 +568,7 @@ class Agent {
564
568
  * @param options Invocation options
565
569
  * @returns Final processed output
566
570
  */
567
- async processAgentOutput(input, output, options) {
571
+ async processAgentOutput(input, output, { messages, ...options }) {
568
572
  const { context } = options;
569
573
  if (!(0, type_utils_js_1.isRecord)(output)) {
570
574
  throw new Error(`expect to return a record type such as {result: ...}, but got (${typeof output}): ${output}`);
@@ -576,8 +580,17 @@ class Agent {
576
580
  const o = await this.callHooks(["onSuccess", "onEnd"], { input, output: finalOutput }, options);
577
581
  if (o?.output)
578
582
  finalOutput = o.output;
579
- if (this.historyConfig?.disabled !== true)
580
- this.afs?.emit("agentSucceed", { input, output: finalOutput });
583
+ if (this.historyConfig?.record === true ||
584
+ (this.historyConfig?.record !== false && this.historyConfig?.enabled)) {
585
+ this.afs?.emit("agentSucceed", {
586
+ agentId: this.name,
587
+ userId: context.userContext.userId,
588
+ sessionId: context.userContext.sessionId,
589
+ input,
590
+ output: finalOutput,
591
+ messages,
592
+ });
593
+ }
581
594
  if (!this.disableEvents)
582
595
  context.emit("agentSucceed", { agent: this, output: finalOutput });
583
596
  return finalOutput;
@@ -349,8 +349,10 @@ class AIAgent extends agent_js_1.Agent {
349
349
  yield {
350
350
  progress: {
351
351
  event: "message",
352
- role: "user",
353
- message: [{ type: "text", content: inputMessage }],
352
+ message: {
353
+ role: "user",
354
+ content: [{ type: "text", text: inputMessage }],
355
+ },
354
356
  },
355
357
  };
356
358
  }
@@ -379,15 +381,22 @@ class AIAgent extends agent_js_1.Agent {
379
381
  }
380
382
  }
381
383
  const { toolCalls, json, text, thoughts, files } = modelOutput;
382
- if (text) {
383
- yield {
384
- progress: { event: "message", role: "agent", message: [{ type: "text", content: text }] },
385
- };
386
- }
387
- if (thoughts) {
388
- yield {
389
- progress: { event: "message", role: "agent", message: [{ type: "thinking", thoughts }] },
390
- };
384
+ if (text || thoughts) {
385
+ const content = [];
386
+ if (thoughts) {
387
+ content.push({ type: "text", text: thoughts, isThinking: true });
388
+ }
389
+ if (text) {
390
+ content.push({ type: "text", text });
391
+ }
392
+ if (content.length) {
393
+ yield {
394
+ progress: {
395
+ event: "message",
396
+ message: { role: "agent", content },
397
+ },
398
+ };
399
+ }
391
400
  }
392
401
  if (toolCalls?.length) {
393
402
  if (this.keepTextInToolUses !== true) {
@@ -419,25 +428,14 @@ class AIAgent extends agent_js_1.Agent {
419
428
  queue.killAndDrain();
420
429
  }
421
430
  }, this.toolCallsConcurrency || 1);
431
+ yield {
432
+ progress: { event: "message", message: { role: "agent", toolCalls } },
433
+ };
422
434
  // Execute tools
423
435
  for (const call of toolCalls) {
424
436
  const tool = toolsMap.get(call.function.name);
425
437
  if (!tool)
426
438
  throw new Error(`Tool not found: ${call.function.name}`);
427
- yield {
428
- progress: {
429
- event: "message",
430
- role: "agent",
431
- message: [
432
- {
433
- type: "tool_use",
434
- name: call.function.name,
435
- input: call.function.arguments,
436
- toolUseId: call.id,
437
- },
438
- ],
439
- },
440
- };
441
439
  queue.push({ tool, call });
442
440
  }
443
441
  await queue.drained();
@@ -449,14 +447,11 @@ class AIAgent extends agent_js_1.Agent {
449
447
  yield {
450
448
  progress: {
451
449
  event: "message",
452
- role: "agent",
453
- message: [
454
- {
455
- type: "tool_result",
456
- toolUseId: call.id,
457
- content: output,
458
- },
459
- ],
450
+ message: {
451
+ role: "tool",
452
+ toolCallId: call.id,
453
+ content: JSON.stringify(output),
454
+ },
460
455
  },
461
456
  };
462
457
  }
@@ -480,11 +475,6 @@ class AIAgent extends agent_js_1.Agent {
480
475
  if (!(0, type_utils_js_1.isEmpty)(result)) {
481
476
  yield { delta: { json: result } };
482
477
  }
483
- if (text) {
484
- yield {
485
- progress: { event: "message", role: "agent", message: [{ type: "text", content: text }] },
486
- };
487
- }
488
478
  return;
489
479
  }
490
480
  }
@@ -225,6 +225,7 @@ export type ChatModelInputMessageContent = string | UnionContent[];
225
225
  export type TextContent = {
226
226
  type: "text";
227
227
  text: string;
228
+ isThinking?: boolean;
228
229
  /**
229
230
  * Cache control marker (only supported by Claude)
230
231
  *
@@ -1,6 +1,6 @@
1
1
  import type { AFSOptions } from "@aigne/afs";
2
2
  import { type ZodType, z } from "zod";
3
- import type { AgentHooks, TaskRenderMode } from "../agents/agent.js";
3
+ import type { Agent, AgentHooks, TaskRenderMode } from "../agents/agent.js";
4
4
  import type { LoadOptions } from "./index.js";
5
5
  import { chatModelSchema, imageModelSchema } from "./schema.js";
6
6
  export interface HooksSchema {
@@ -65,6 +65,7 @@ export interface AgentSchema {
65
65
  context?: AFSContextSchema;
66
66
  });
67
67
  shareAFS?: boolean;
68
+ historyConfig?: Agent["historyConfig"];
68
69
  [key: string]: unknown;
69
70
  }
70
71
  export declare function parseAgentFile(path: string, data: any, options: LoadOptions): Promise<AgentSchema>;
@@ -100,6 +100,14 @@ const getAgentSchema = ({ filepath }) => {
100
100
  })),
101
101
  ])),
102
102
  shareAFS: (0, schema_js_1.optionalize)(zod_1.z.boolean()),
103
+ historyConfig: (0, schema_js_1.camelizeSchema)((0, schema_js_1.optionalize)(zod_1.z.object({
104
+ enabled: (0, schema_js_1.optionalize)(zod_1.z.boolean()),
105
+ record: (0, schema_js_1.optionalize)(zod_1.z.boolean()),
106
+ inject: (0, schema_js_1.optionalize)(zod_1.z.boolean()),
107
+ use_old_memory: (0, schema_js_1.optionalize)(zod_1.z.boolean()),
108
+ maxTokens: (0, schema_js_1.optionalize)(zod_1.z.number().int().positive()),
109
+ maxItems: (0, schema_js_1.optionalize)(zod_1.z.number().int().positive()),
110
+ }))),
103
111
  });
104
112
  return (0, schema_js_1.camelizeSchema)(baseAgentSchema.passthrough());
105
113
  });
@@ -1,5 +1,9 @@
1
+ import type { AFSListOptions } from "@aigne/afs";
1
2
  import type { Agent } from "../../../agents/agent.js";
2
- export declare function getHistories(agent: Agent): Promise<{
3
+ export declare function getHistories({ filter, agent, }: {
4
+ filter: AFSListOptions["filter"];
5
+ agent: Agent;
6
+ }): Promise<{
3
7
  role: "user" | "agent";
4
8
  content: unknown;
5
9
  }[]>;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getHistories = getHistories;
4
4
  const afs_history_1 = require("@aigne/afs-history");
5
5
  const type_utils_js_1 = require("../../../utils/type-utils.js");
6
- async function getHistories(agent) {
6
+ async function getHistories({ filter, agent, }) {
7
7
  const afs = agent?.afs;
8
8
  if (!afs)
9
9
  return [];
@@ -11,6 +11,7 @@ async function getHistories(agent) {
11
11
  if (!historyModule)
12
12
  return [];
13
13
  const history = (await afs.list(historyModule.path, {
14
+ filter,
14
15
  limit: agent.historyConfig?.maxItems || 10,
15
16
  orderBy: [["createdAt", "desc"]],
16
17
  })).data;
@@ -20,7 +20,14 @@ function createAFSContext(agent, context) {
20
20
  get histories() {
21
21
  if (!agent)
22
22
  return Promise.resolve([]);
23
- return (0, history_js_1.getHistories)(agent);
23
+ return (0, history_js_1.getHistories)({
24
+ agent,
25
+ filter: {
26
+ agentId: agent.name,
27
+ userId: context?.userContext.userId,
28
+ sessionId: context?.userContext.sessionId,
29
+ },
30
+ });
24
31
  },
25
32
  get skills() {
26
33
  const afs = agent?.afs;
@@ -1,14 +1,16 @@
1
1
  import type { GetPromptResult } from "@modelcontextprotocol/sdk/types.js";
2
- import { Agent, type AgentInvokeOptions, type Message } from "../agents/agent.js";
2
+ import { Agent, type Message } from "../agents/agent.js";
3
3
  import { type AIAgent } from "../agents/ai-agent.js";
4
- import type { ChatModel, ChatModelInput } from "../agents/chat-model.js";
4
+ import { type ChatModel, type ChatModelInput, type ChatModelInputMessage } from "../agents/chat-model.js";
5
5
  import { type FileUnionContent } from "../agents/model.js";
6
+ import type { Context } from "../aigne/context.js";
6
7
  import { ChatMessagesTemplate } from "./template.js";
7
8
  export interface PromptBuilderOptions {
8
9
  instructions?: string | ChatMessagesTemplate;
9
10
  workingDir?: string;
10
11
  }
11
- export interface PromptBuildOptions extends Partial<Pick<AgentInvokeOptions, "context">> {
12
+ export interface PromptBuildOptions {
13
+ context?: Context;
12
14
  agent?: AIAgent;
13
15
  input?: Message;
14
16
  model?: ChatModel;
@@ -37,10 +39,12 @@ export declare class PromptBuilder {
37
39
  }>;
38
40
  private getTemplateVariables;
39
41
  private buildMessages;
40
- getHistories(options: PromptBuildOptions): Promise<{
41
- role: "user" | "agent";
42
- content: unknown;
43
- }[]>;
42
+ protected deprecatedMemories(message: string | undefined, options: PromptBuildOptions): Promise<ChatModelInputMessage[]>;
43
+ getHistories({ agentId, userId, sessionId, ...options }: PromptBuildOptions & {
44
+ agentId?: string;
45
+ userId?: string;
46
+ sessionId?: string;
47
+ }): Promise<ChatModelInputMessage[]>;
44
48
  private refineMessages;
45
49
  private convertMemoriesToMessages;
46
50
  private buildResponseFormat;
@@ -8,6 +8,7 @@ const zod_1 = require("zod");
8
8
  const zod_to_json_schema_1 = require("zod-to-json-schema");
9
9
  const agent_js_1 = require("../agents/agent.js");
10
10
  const ai_agent_js_1 = require("../agents/ai-agent.js");
11
+ const chat_model_js_1 = require("../agents/chat-model.js");
11
12
  const model_js_1 = require("../agents/model.js");
12
13
  const schema_js_1 = require("../loader/schema.js");
13
14
  const json_schema_js_1 = require("../utils/json-schema.js");
@@ -100,6 +101,9 @@ class PromptBuilder {
100
101
  }
101
102
  async buildMessages(options) {
102
103
  const { input } = options;
104
+ const agentId = options.agent?.name;
105
+ const userId = options.context?.userContext.userId;
106
+ const sessionId = options.context?.userContext.sessionId;
103
107
  const inputKey = options.agent?.inputKey;
104
108
  const message = inputKey && typeof input?.[inputKey] === "string" ? input[inputKey] : undefined;
105
109
  const [messages, otherCustomMessages] = (0, type_utils_js_1.partition)((await (typeof this.instructions === "string"
@@ -109,6 +113,50 @@ class PromptBuilder {
109
113
  const files = (0, type_utils_js_1.flat)(inputFileKey
110
114
  ? (0, type_utils_js_1.checkArguments)("Check input files", (0, schema_js_1.optionalize)(model_js_1.fileUnionContentsSchema), input?.[inputFileKey])
111
115
  : null);
116
+ const historyConfig = options.agent?.historyConfig;
117
+ const injectHistory = historyConfig?.inject === true || (historyConfig?.inject !== false && historyConfig?.enabled);
118
+ if (injectHistory) {
119
+ if (historyConfig.useOldMemory) {
120
+ messages.push(...(await this.deprecatedMemories(message, options)));
121
+ }
122
+ else {
123
+ const history = await this.getHistories({ ...options, agentId, userId, sessionId });
124
+ messages.push(...history);
125
+ }
126
+ }
127
+ // if the agent is using structured stream mode, add the instructions
128
+ const { structuredStreamMode, outputSchema } = options.agent || {};
129
+ if (structuredStreamMode && outputSchema) {
130
+ const instructions = options.agent?.customStructuredStreamInstructions?.instructions ||
131
+ PromptBuilder.from(structured_stream_instructions_js_1.STRUCTURED_STREAM_INSTRUCTIONS.instructions);
132
+ messages.push(...(await instructions.buildMessages({
133
+ input: {
134
+ ...input,
135
+ outputJsonSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(outputSchema),
136
+ },
137
+ })));
138
+ }
139
+ messages.push(...otherCustomMessages);
140
+ if (message || files.length) {
141
+ const content = [];
142
+ if (message &&
143
+ // avoid duplicate user messages: developer may have already included the message in the messages
144
+ !otherCustomMessages.some((i) => i.role === "user" &&
145
+ (typeof i.content === "string"
146
+ ? i.content.includes(message)
147
+ : i.content?.some((c) => c.type === "text" && c.text.includes(message))))) {
148
+ content.push({ type: "text", text: message });
149
+ }
150
+ if (files.length)
151
+ content.push(...files);
152
+ if (content.length) {
153
+ messages.push({ role: "user", content });
154
+ }
155
+ }
156
+ return this.refineMessages(options, messages);
157
+ }
158
+ async deprecatedMemories(message, options) {
159
+ const messages = [];
112
160
  const memories = [];
113
161
  if (options.agent && options.context) {
114
162
  memories.push(...(await options.agent.retrieveMemories({ search: message }, { context: options.context })));
@@ -117,7 +165,7 @@ class PromptBuilder {
117
165
  memories.push(...options.context.memories);
118
166
  }
119
167
  const afs = options.agent?.afs;
120
- if (afs && options.agent?.historyConfig?.disabled !== true) {
168
+ if (afs && options.agent?.historyConfig?.enabled) {
121
169
  const historyModule = (await afs.listModules()).find((m) => m.module instanceof afs_history_1.AFSHistory);
122
170
  if (historyModule) {
123
171
  const history = await afs.list(historyModule.path, {
@@ -163,38 +211,9 @@ class PromptBuilder {
163
211
  }
164
212
  if (memories.length)
165
213
  messages.push(...(await this.convertMemoriesToMessages(memories, options)));
166
- // if the agent is using structured stream mode, add the instructions
167
- const { structuredStreamMode, outputSchema } = options.agent || {};
168
- if (structuredStreamMode && outputSchema) {
169
- const instructions = options.agent?.customStructuredStreamInstructions?.instructions ||
170
- PromptBuilder.from(structured_stream_instructions_js_1.STRUCTURED_STREAM_INSTRUCTIONS.instructions);
171
- messages.push(...(await instructions.buildMessages({
172
- input: {
173
- ...input,
174
- outputJsonSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(outputSchema),
175
- },
176
- })));
177
- }
178
- if (message || files.length) {
179
- const content = [];
180
- if (message &&
181
- // avoid duplicate user messages: developer may have already included the message in the custom user messages
182
- !otherCustomMessages.some((i) => i.role === "user" &&
183
- (typeof i.content === "string"
184
- ? i.content.includes(message)
185
- : i.content?.some((c) => c.type === "text" && c.text.includes(message))))) {
186
- content.push({ type: "text", text: message });
187
- }
188
- if (files.length)
189
- content.push(...files);
190
- if (content.length) {
191
- messages.push({ role: "user", content });
192
- }
193
- }
194
- messages.push(...otherCustomMessages);
195
- return this.refineMessages(options, messages);
214
+ return messages;
196
215
  }
197
- async getHistories(options) {
216
+ async getHistories({ agentId, userId, sessionId, ...options }) {
198
217
  const { agent } = options;
199
218
  const afs = agent?.afs;
200
219
  if (!afs)
@@ -203,24 +222,21 @@ class PromptBuilder {
203
222
  if (!historyModule)
204
223
  return [];
205
224
  const history = (await afs.list(historyModule.path, {
225
+ filter: { agentId, userId, sessionId },
206
226
  limit: agent.historyConfig?.maxItems || 10,
207
227
  orderBy: [["createdAt", "desc"]],
208
- })).data;
209
- return history
210
- .reverse()
211
- .map((i) => {
212
- if (!i.content)
213
- return;
214
- const { input, output } = i.content;
215
- if (!input || !output)
216
- return;
217
- return [
218
- { role: "user", content: input },
219
- { role: "agent", content: output },
220
- ];
221
- })
222
- .filter(type_utils_js_1.isNonNullable)
223
- .flat();
228
+ })).data.reverse();
229
+ return (await Promise.all(history.map(async (i) => {
230
+ if (Array.isArray(i.content?.messages) &&
231
+ i.content.messages.every((m) => chat_model_js_1.roleSchema.parse(m?.role))) {
232
+ return i.content.messages;
233
+ }
234
+ const { input, output } = i.content || {};
235
+ if (input && output) {
236
+ return await this.convertMemoriesToMessages([{ content: { input, output } }], options);
237
+ }
238
+ return [];
239
+ }))).flat();
224
240
  }
225
241
  refineMessages(options, messages) {
226
242
  const { autoReorderSystemMessages, autoMergeSystemMessages } = options.agent ?? {};
@@ -9,7 +9,7 @@ import type { Memory, MemoryAgent } from "../memory/memory.js";
9
9
  import type { MemoryRecorderInput } from "../memory/recorder.js";
10
10
  import type { MemoryRetrieverInput } from "../memory/retriever.js";
11
11
  import { type Nullish, type PromiseOrValue, type XOr } from "../utils/type-utils.js";
12
- import type { ChatModel } from "./chat-model.js";
12
+ import type { ChatModel, ChatModelInputMessage } from "./chat-model.js";
13
13
  import type { GuideRailAgent, GuideRailAgentOutput } from "./guide-rail-agent.js";
14
14
  import type { ImageModel } from "./image-model.js";
15
15
  import { type GetterSchema, type TransferAgentOutput } from "./types.js";
@@ -350,7 +350,20 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
350
350
  */
351
351
  private disableEvents?;
352
352
  historyConfig?: {
353
- disabled?: boolean;
353
+ /**
354
+ * Whether to enable history recording and injection
355
+ * @default false
356
+ */
357
+ enabled?: boolean;
358
+ /**
359
+ * Whether to record history entries, default to enabled when history is enabled
360
+ */
361
+ record?: boolean;
362
+ /**
363
+ * Whether to inject history entries into the context, default to enabled when history is enabled
364
+ */
365
+ inject?: boolean;
366
+ useOldMemory?: boolean;
354
367
  maxTokens?: number;
355
368
  maxItems?: number;
356
369
  };
@@ -455,7 +468,9 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
455
468
  * @param options Invocation options
456
469
  * @returns Final processed output
457
470
  */
458
- protected processAgentOutput(input: I, output: Exclude<AgentResponse<O>, AgentResponseStream<O>>, options: AgentInvokeOptions): Promise<O>;
471
+ protected processAgentOutput(input: I, output: Exclude<AgentResponse<O>, AgentResponseStream<O>>, { messages, ...options }: AgentInvokeOptions & {
472
+ messages?: ChatModelInputMessage[];
473
+ }): Promise<O>;
459
474
  /**
460
475
  * Process errors that occur during agent execution
461
476
  *
@@ -785,29 +800,29 @@ export interface AgentResponseProgress {
785
800
  error: Error;
786
801
  } | {
787
802
  event: "message";
788
- role: "user" | "agent";
789
- message: ({
790
- type: "text";
791
- content: string;
792
- } | {
793
- type: "thinking";
794
- thoughts: string;
795
- } | {
796
- type: "tool_use";
797
- toolUseId: string;
798
- name: string;
799
- input: unknown;
800
- } | {
801
- type: "tool_result";
802
- toolUseId: string;
803
- content: unknown;
804
- })[];
803
+ message: ChatModelInputMessage;
805
804
  }) & Omit<AgentEvent, "agent"> & {
806
805
  agent: {
807
806
  name: string;
808
807
  };
809
808
  };
810
809
  }
810
+ export type AgentResponseProgressMessageItem = {
811
+ type: "text";
812
+ content: string;
813
+ } | {
814
+ type: "thinking";
815
+ thoughts: string;
816
+ } | {
817
+ type: "tool_use";
818
+ toolUseId: string;
819
+ name: string;
820
+ input: unknown;
821
+ } | {
822
+ type: "tool_result";
823
+ toolUseId: string;
824
+ content: unknown;
825
+ };
811
826
  export declare function isAgentResponseProgress<T>(chunk: AgentResponseChunk<T>): chunk is AgentResponseProgress;
812
827
  /**
813
828
  * Creates a text delta for streaming responses
@@ -225,6 +225,7 @@ export type ChatModelInputMessageContent = string | UnionContent[];
225
225
  export type TextContent = {
226
226
  type: "text";
227
227
  text: string;
228
+ isThinking?: boolean;
228
229
  /**
229
230
  * Cache control marker (only supported by Claude)
230
231
  *
@@ -1,6 +1,6 @@
1
1
  import type { AFSOptions } from "@aigne/afs";
2
2
  import { type ZodType, z } from "zod";
3
- import type { AgentHooks, TaskRenderMode } from "../agents/agent.js";
3
+ import type { Agent, AgentHooks, TaskRenderMode } from "../agents/agent.js";
4
4
  import type { LoadOptions } from "./index.js";
5
5
  import { chatModelSchema, imageModelSchema } from "./schema.js";
6
6
  export interface HooksSchema {
@@ -65,6 +65,7 @@ export interface AgentSchema {
65
65
  context?: AFSContextSchema;
66
66
  });
67
67
  shareAFS?: boolean;
68
+ historyConfig?: Agent["historyConfig"];
68
69
  [key: string]: unknown;
69
70
  }
70
71
  export declare function parseAgentFile(path: string, data: any, options: LoadOptions): Promise<AgentSchema>;
@@ -1,5 +1,9 @@
1
+ import type { AFSListOptions } from "@aigne/afs";
1
2
  import type { Agent } from "../../../agents/agent.js";
2
- export declare function getHistories(agent: Agent): Promise<{
3
+ export declare function getHistories({ filter, agent, }: {
4
+ filter: AFSListOptions["filter"];
5
+ agent: Agent;
6
+ }): Promise<{
3
7
  role: "user" | "agent";
4
8
  content: unknown;
5
9
  }[]>;
@@ -1,14 +1,16 @@
1
1
  import type { GetPromptResult } from "@modelcontextprotocol/sdk/types.js";
2
- import { Agent, type AgentInvokeOptions, type Message } from "../agents/agent.js";
2
+ import { Agent, type Message } from "../agents/agent.js";
3
3
  import { type AIAgent } from "../agents/ai-agent.js";
4
- import type { ChatModel, ChatModelInput } from "../agents/chat-model.js";
4
+ import { type ChatModel, type ChatModelInput, type ChatModelInputMessage } from "../agents/chat-model.js";
5
5
  import { type FileUnionContent } from "../agents/model.js";
6
+ import type { Context } from "../aigne/context.js";
6
7
  import { ChatMessagesTemplate } from "./template.js";
7
8
  export interface PromptBuilderOptions {
8
9
  instructions?: string | ChatMessagesTemplate;
9
10
  workingDir?: string;
10
11
  }
11
- export interface PromptBuildOptions extends Partial<Pick<AgentInvokeOptions, "context">> {
12
+ export interface PromptBuildOptions {
13
+ context?: Context;
12
14
  agent?: AIAgent;
13
15
  input?: Message;
14
16
  model?: ChatModel;
@@ -37,10 +39,12 @@ export declare class PromptBuilder {
37
39
  }>;
38
40
  private getTemplateVariables;
39
41
  private buildMessages;
40
- getHistories(options: PromptBuildOptions): Promise<{
41
- role: "user" | "agent";
42
- content: unknown;
43
- }[]>;
42
+ protected deprecatedMemories(message: string | undefined, options: PromptBuildOptions): Promise<ChatModelInputMessage[]>;
43
+ getHistories({ agentId, userId, sessionId, ...options }: PromptBuildOptions & {
44
+ agentId?: string;
45
+ userId?: string;
46
+ sessionId?: string;
47
+ }): Promise<ChatModelInputMessage[]>;
44
48
  private refineMessages;
45
49
  private convertMemoriesToMessages;
46
50
  private buildResponseFormat;
@@ -9,7 +9,7 @@ import type { Memory, MemoryAgent } from "../memory/memory.js";
9
9
  import type { MemoryRecorderInput } from "../memory/recorder.js";
10
10
  import type { MemoryRetrieverInput } from "../memory/retriever.js";
11
11
  import { type Nullish, type PromiseOrValue, type XOr } from "../utils/type-utils.js";
12
- import type { ChatModel } from "./chat-model.js";
12
+ import type { ChatModel, ChatModelInputMessage } from "./chat-model.js";
13
13
  import type { GuideRailAgent, GuideRailAgentOutput } from "./guide-rail-agent.js";
14
14
  import type { ImageModel } from "./image-model.js";
15
15
  import { type GetterSchema, type TransferAgentOutput } from "./types.js";
@@ -350,7 +350,20 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
350
350
  */
351
351
  private disableEvents?;
352
352
  historyConfig?: {
353
- disabled?: boolean;
353
+ /**
354
+ * Whether to enable history recording and injection
355
+ * @default false
356
+ */
357
+ enabled?: boolean;
358
+ /**
359
+ * Whether to record history entries, default to enabled when history is enabled
360
+ */
361
+ record?: boolean;
362
+ /**
363
+ * Whether to inject history entries into the context, default to enabled when history is enabled
364
+ */
365
+ inject?: boolean;
366
+ useOldMemory?: boolean;
354
367
  maxTokens?: number;
355
368
  maxItems?: number;
356
369
  };
@@ -455,7 +468,9 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
455
468
  * @param options Invocation options
456
469
  * @returns Final processed output
457
470
  */
458
- protected processAgentOutput(input: I, output: Exclude<AgentResponse<O>, AgentResponseStream<O>>, options: AgentInvokeOptions): Promise<O>;
471
+ protected processAgentOutput(input: I, output: Exclude<AgentResponse<O>, AgentResponseStream<O>>, { messages, ...options }: AgentInvokeOptions & {
472
+ messages?: ChatModelInputMessage[];
473
+ }): Promise<O>;
459
474
  /**
460
475
  * Process errors that occur during agent execution
461
476
  *
@@ -785,29 +800,29 @@ export interface AgentResponseProgress {
785
800
  error: Error;
786
801
  } | {
787
802
  event: "message";
788
- role: "user" | "agent";
789
- message: ({
790
- type: "text";
791
- content: string;
792
- } | {
793
- type: "thinking";
794
- thoughts: string;
795
- } | {
796
- type: "tool_use";
797
- toolUseId: string;
798
- name: string;
799
- input: unknown;
800
- } | {
801
- type: "tool_result";
802
- toolUseId: string;
803
- content: unknown;
804
- })[];
803
+ message: ChatModelInputMessage;
805
804
  }) & Omit<AgentEvent, "agent"> & {
806
805
  agent: {
807
806
  name: string;
808
807
  };
809
808
  };
810
809
  }
810
+ export type AgentResponseProgressMessageItem = {
811
+ type: "text";
812
+ content: string;
813
+ } | {
814
+ type: "thinking";
815
+ thoughts: string;
816
+ } | {
817
+ type: "tool_use";
818
+ toolUseId: string;
819
+ name: string;
820
+ input: unknown;
821
+ } | {
822
+ type: "tool_result";
823
+ toolUseId: string;
824
+ content: unknown;
825
+ };
811
826
  export declare function isAgentResponseProgress<T>(chunk: AgentResponseChunk<T>): chunk is AgentResponseProgress;
812
827
  /**
813
828
  * Creates a text delta for streaming responses
@@ -415,11 +415,15 @@ export class Agent {
415
415
  : isAsyncGenerator(response)
416
416
  ? asyncGeneratorToReadableStream(response)
417
417
  : objectToAgentResponseStream(response);
418
+ const messages = [];
418
419
  for await (const chunk of stream) {
419
420
  mergeAgentResponseChunk(output, chunk);
420
421
  yield chunk;
422
+ if (isAgentResponseProgress(chunk) && chunk.progress.event === "message") {
423
+ messages.push(chunk.progress.message);
424
+ }
421
425
  }
422
- let result = await this.processAgentOutput(input, output, options);
426
+ let result = await this.processAgentOutput(input, output, { ...options, messages });
423
427
  if (attempt > 0) {
424
428
  result = { ...result, $meta: { ...result.$meta, retries: attempt } };
425
429
  }
@@ -516,7 +520,7 @@ export class Agent {
516
520
  * @param options Invocation options
517
521
  * @returns Final processed output
518
522
  */
519
- async processAgentOutput(input, output, options) {
523
+ async processAgentOutput(input, output, { messages, ...options }) {
520
524
  const { context } = options;
521
525
  if (!isRecord(output)) {
522
526
  throw new Error(`expect to return a record type such as {result: ...}, but got (${typeof output}): ${output}`);
@@ -528,8 +532,17 @@ export class Agent {
528
532
  const o = await this.callHooks(["onSuccess", "onEnd"], { input, output: finalOutput }, options);
529
533
  if (o?.output)
530
534
  finalOutput = o.output;
531
- if (this.historyConfig?.disabled !== true)
532
- this.afs?.emit("agentSucceed", { input, output: finalOutput });
535
+ if (this.historyConfig?.record === true ||
536
+ (this.historyConfig?.record !== false && this.historyConfig?.enabled)) {
537
+ this.afs?.emit("agentSucceed", {
538
+ agentId: this.name,
539
+ userId: context.userContext.userId,
540
+ sessionId: context.userContext.sessionId,
541
+ input,
542
+ output: finalOutput,
543
+ messages,
544
+ });
545
+ }
533
546
  if (!this.disableEvents)
534
547
  context.emit("agentSucceed", { agent: this, output: finalOutput });
535
548
  return finalOutput;
@@ -313,8 +313,10 @@ export class AIAgent extends Agent {
313
313
  yield {
314
314
  progress: {
315
315
  event: "message",
316
- role: "user",
317
- message: [{ type: "text", content: inputMessage }],
316
+ message: {
317
+ role: "user",
318
+ content: [{ type: "text", text: inputMessage }],
319
+ },
318
320
  },
319
321
  };
320
322
  }
@@ -343,15 +345,22 @@ export class AIAgent extends Agent {
343
345
  }
344
346
  }
345
347
  const { toolCalls, json, text, thoughts, files } = modelOutput;
346
- if (text) {
347
- yield {
348
- progress: { event: "message", role: "agent", message: [{ type: "text", content: text }] },
349
- };
350
- }
351
- if (thoughts) {
352
- yield {
353
- progress: { event: "message", role: "agent", message: [{ type: "thinking", thoughts }] },
354
- };
348
+ if (text || thoughts) {
349
+ const content = [];
350
+ if (thoughts) {
351
+ content.push({ type: "text", text: thoughts, isThinking: true });
352
+ }
353
+ if (text) {
354
+ content.push({ type: "text", text });
355
+ }
356
+ if (content.length) {
357
+ yield {
358
+ progress: {
359
+ event: "message",
360
+ message: { role: "agent", content },
361
+ },
362
+ };
363
+ }
355
364
  }
356
365
  if (toolCalls?.length) {
357
366
  if (this.keepTextInToolUses !== true) {
@@ -383,25 +392,14 @@ export class AIAgent extends Agent {
383
392
  queue.killAndDrain();
384
393
  }
385
394
  }, this.toolCallsConcurrency || 1);
395
+ yield {
396
+ progress: { event: "message", message: { role: "agent", toolCalls } },
397
+ };
386
398
  // Execute tools
387
399
  for (const call of toolCalls) {
388
400
  const tool = toolsMap.get(call.function.name);
389
401
  if (!tool)
390
402
  throw new Error(`Tool not found: ${call.function.name}`);
391
- yield {
392
- progress: {
393
- event: "message",
394
- role: "agent",
395
- message: [
396
- {
397
- type: "tool_use",
398
- name: call.function.name,
399
- input: call.function.arguments,
400
- toolUseId: call.id,
401
- },
402
- ],
403
- },
404
- };
405
403
  queue.push({ tool, call });
406
404
  }
407
405
  await queue.drained();
@@ -413,14 +411,11 @@ export class AIAgent extends Agent {
413
411
  yield {
414
412
  progress: {
415
413
  event: "message",
416
- role: "agent",
417
- message: [
418
- {
419
- type: "tool_result",
420
- toolUseId: call.id,
421
- content: output,
422
- },
423
- ],
414
+ message: {
415
+ role: "tool",
416
+ toolCallId: call.id,
417
+ content: JSON.stringify(output),
418
+ },
424
419
  },
425
420
  };
426
421
  }
@@ -444,11 +439,6 @@ export class AIAgent extends Agent {
444
439
  if (!isEmpty(result)) {
445
440
  yield { delta: { json: result } };
446
441
  }
447
- if (text) {
448
- yield {
449
- progress: { event: "message", role: "agent", message: [{ type: "text", content: text }] },
450
- };
451
- }
452
442
  return;
453
443
  }
454
444
  }
@@ -225,6 +225,7 @@ export type ChatModelInputMessageContent = string | UnionContent[];
225
225
  export type TextContent = {
226
226
  type: "text";
227
227
  text: string;
228
+ isThinking?: boolean;
228
229
  /**
229
230
  * Cache control marker (only supported by Claude)
230
231
  *
@@ -1,6 +1,6 @@
1
1
  import type { AFSOptions } from "@aigne/afs";
2
2
  import { type ZodType, z } from "zod";
3
- import type { AgentHooks, TaskRenderMode } from "../agents/agent.js";
3
+ import type { Agent, AgentHooks, TaskRenderMode } from "../agents/agent.js";
4
4
  import type { LoadOptions } from "./index.js";
5
5
  import { chatModelSchema, imageModelSchema } from "./schema.js";
6
6
  export interface HooksSchema {
@@ -65,6 +65,7 @@ export interface AgentSchema {
65
65
  context?: AFSContextSchema;
66
66
  });
67
67
  shareAFS?: boolean;
68
+ historyConfig?: Agent["historyConfig"];
68
69
  [key: string]: unknown;
69
70
  }
70
71
  export declare function parseAgentFile(path: string, data: any, options: LoadOptions): Promise<AgentSchema>;
@@ -95,6 +95,14 @@ export const getAgentSchema = ({ filepath }) => {
95
95
  })),
96
96
  ])),
97
97
  shareAFS: optionalize(z.boolean()),
98
+ historyConfig: camelizeSchema(optionalize(z.object({
99
+ enabled: optionalize(z.boolean()),
100
+ record: optionalize(z.boolean()),
101
+ inject: optionalize(z.boolean()),
102
+ use_old_memory: optionalize(z.boolean()),
103
+ maxTokens: optionalize(z.number().int().positive()),
104
+ maxItems: optionalize(z.number().int().positive()),
105
+ }))),
98
106
  });
99
107
  return camelizeSchema(baseAgentSchema.passthrough());
100
108
  });
@@ -1,5 +1,9 @@
1
+ import type { AFSListOptions } from "@aigne/afs";
1
2
  import type { Agent } from "../../../agents/agent.js";
2
- export declare function getHistories(agent: Agent): Promise<{
3
+ export declare function getHistories({ filter, agent, }: {
4
+ filter: AFSListOptions["filter"];
5
+ agent: Agent;
6
+ }): Promise<{
3
7
  role: "user" | "agent";
4
8
  content: unknown;
5
9
  }[]>;
@@ -1,6 +1,6 @@
1
1
  import { AFSHistory } from "@aigne/afs-history";
2
2
  import { isNonNullable } from "../../../utils/type-utils.js";
3
- export async function getHistories(agent) {
3
+ export async function getHistories({ filter, agent, }) {
4
4
  const afs = agent?.afs;
5
5
  if (!afs)
6
6
  return [];
@@ -8,6 +8,7 @@ export async function getHistories(agent) {
8
8
  if (!historyModule)
9
9
  return [];
10
10
  const history = (await afs.list(historyModule.path, {
11
+ filter,
11
12
  limit: agent.historyConfig?.maxItems || 10,
12
13
  orderBy: [["createdAt", "desc"]],
13
14
  })).data;
@@ -17,7 +17,14 @@ export function createAFSContext(agent, context) {
17
17
  get histories() {
18
18
  if (!agent)
19
19
  return Promise.resolve([]);
20
- return getHistories(agent);
20
+ return getHistories({
21
+ agent,
22
+ filter: {
23
+ agentId: agent.name,
24
+ userId: context?.userContext.userId,
25
+ sessionId: context?.userContext.sessionId,
26
+ },
27
+ });
21
28
  },
22
29
  get skills() {
23
30
  const afs = agent?.afs;
@@ -1,14 +1,16 @@
1
1
  import type { GetPromptResult } from "@modelcontextprotocol/sdk/types.js";
2
- import { Agent, type AgentInvokeOptions, type Message } from "../agents/agent.js";
2
+ import { Agent, type Message } from "../agents/agent.js";
3
3
  import { type AIAgent } from "../agents/ai-agent.js";
4
- import type { ChatModel, ChatModelInput } from "../agents/chat-model.js";
4
+ import { type ChatModel, type ChatModelInput, type ChatModelInputMessage } from "../agents/chat-model.js";
5
5
  import { type FileUnionContent } from "../agents/model.js";
6
+ import type { Context } from "../aigne/context.js";
6
7
  import { ChatMessagesTemplate } from "./template.js";
7
8
  export interface PromptBuilderOptions {
8
9
  instructions?: string | ChatMessagesTemplate;
9
10
  workingDir?: string;
10
11
  }
11
- export interface PromptBuildOptions extends Partial<Pick<AgentInvokeOptions, "context">> {
12
+ export interface PromptBuildOptions {
13
+ context?: Context;
12
14
  agent?: AIAgent;
13
15
  input?: Message;
14
16
  model?: ChatModel;
@@ -37,10 +39,12 @@ export declare class PromptBuilder {
37
39
  }>;
38
40
  private getTemplateVariables;
39
41
  private buildMessages;
40
- getHistories(options: PromptBuildOptions): Promise<{
41
- role: "user" | "agent";
42
- content: unknown;
43
- }[]>;
42
+ protected deprecatedMemories(message: string | undefined, options: PromptBuildOptions): Promise<ChatModelInputMessage[]>;
43
+ getHistories({ agentId, userId, sessionId, ...options }: PromptBuildOptions & {
44
+ agentId?: string;
45
+ userId?: string;
46
+ sessionId?: string;
47
+ }): Promise<ChatModelInputMessage[]>;
44
48
  private refineMessages;
45
49
  private convertMemoriesToMessages;
46
50
  private buildResponseFormat;
@@ -5,6 +5,7 @@ import { ZodObject } from "zod";
5
5
  import { zodToJsonSchema } from "zod-to-json-schema";
6
6
  import { Agent } from "../agents/agent.js";
7
7
  import { DEFAULT_OUTPUT_FILE_KEY, DEFAULT_OUTPUT_KEY } from "../agents/ai-agent.js";
8
+ import { roleSchema, } from "../agents/chat-model.js";
8
9
  import { fileUnionContentsSchema } from "../agents/model.js";
9
10
  import { optionalize } from "../loader/schema.js";
10
11
  import { outputSchemaToResponseFormatSchema } from "../utils/json-schema.js";
@@ -97,6 +98,9 @@ export class PromptBuilder {
97
98
  }
98
99
  async buildMessages(options) {
99
100
  const { input } = options;
101
+ const agentId = options.agent?.name;
102
+ const userId = options.context?.userContext.userId;
103
+ const sessionId = options.context?.userContext.sessionId;
100
104
  const inputKey = options.agent?.inputKey;
101
105
  const message = inputKey && typeof input?.[inputKey] === "string" ? input[inputKey] : undefined;
102
106
  const [messages, otherCustomMessages] = partition((await (typeof this.instructions === "string"
@@ -106,6 +110,50 @@ export class PromptBuilder {
106
110
  const files = flat(inputFileKey
107
111
  ? checkArguments("Check input files", optionalize(fileUnionContentsSchema), input?.[inputFileKey])
108
112
  : null);
113
+ const historyConfig = options.agent?.historyConfig;
114
+ const injectHistory = historyConfig?.inject === true || (historyConfig?.inject !== false && historyConfig?.enabled);
115
+ if (injectHistory) {
116
+ if (historyConfig.useOldMemory) {
117
+ messages.push(...(await this.deprecatedMemories(message, options)));
118
+ }
119
+ else {
120
+ const history = await this.getHistories({ ...options, agentId, userId, sessionId });
121
+ messages.push(...history);
122
+ }
123
+ }
124
+ // if the agent is using structured stream mode, add the instructions
125
+ const { structuredStreamMode, outputSchema } = options.agent || {};
126
+ if (structuredStreamMode && outputSchema) {
127
+ const instructions = options.agent?.customStructuredStreamInstructions?.instructions ||
128
+ PromptBuilder.from(STRUCTURED_STREAM_INSTRUCTIONS.instructions);
129
+ messages.push(...(await instructions.buildMessages({
130
+ input: {
131
+ ...input,
132
+ outputJsonSchema: zodToJsonSchema(outputSchema),
133
+ },
134
+ })));
135
+ }
136
+ messages.push(...otherCustomMessages);
137
+ if (message || files.length) {
138
+ const content = [];
139
+ if (message &&
140
+ // avoid duplicate user messages: developer may have already included the message in the messages
141
+ !otherCustomMessages.some((i) => i.role === "user" &&
142
+ (typeof i.content === "string"
143
+ ? i.content.includes(message)
144
+ : i.content?.some((c) => c.type === "text" && c.text.includes(message))))) {
145
+ content.push({ type: "text", text: message });
146
+ }
147
+ if (files.length)
148
+ content.push(...files);
149
+ if (content.length) {
150
+ messages.push({ role: "user", content });
151
+ }
152
+ }
153
+ return this.refineMessages(options, messages);
154
+ }
155
+ async deprecatedMemories(message, options) {
156
+ const messages = [];
109
157
  const memories = [];
110
158
  if (options.agent && options.context) {
111
159
  memories.push(...(await options.agent.retrieveMemories({ search: message }, { context: options.context })));
@@ -114,7 +162,7 @@ export class PromptBuilder {
114
162
  memories.push(...options.context.memories);
115
163
  }
116
164
  const afs = options.agent?.afs;
117
- if (afs && options.agent?.historyConfig?.disabled !== true) {
165
+ if (afs && options.agent?.historyConfig?.enabled) {
118
166
  const historyModule = (await afs.listModules()).find((m) => m.module instanceof AFSHistory);
119
167
  if (historyModule) {
120
168
  const history = await afs.list(historyModule.path, {
@@ -160,38 +208,9 @@ export class PromptBuilder {
160
208
  }
161
209
  if (memories.length)
162
210
  messages.push(...(await this.convertMemoriesToMessages(memories, options)));
163
- // if the agent is using structured stream mode, add the instructions
164
- const { structuredStreamMode, outputSchema } = options.agent || {};
165
- if (structuredStreamMode && outputSchema) {
166
- const instructions = options.agent?.customStructuredStreamInstructions?.instructions ||
167
- PromptBuilder.from(STRUCTURED_STREAM_INSTRUCTIONS.instructions);
168
- messages.push(...(await instructions.buildMessages({
169
- input: {
170
- ...input,
171
- outputJsonSchema: zodToJsonSchema(outputSchema),
172
- },
173
- })));
174
- }
175
- if (message || files.length) {
176
- const content = [];
177
- if (message &&
178
- // avoid duplicate user messages: developer may have already included the message in the custom user messages
179
- !otherCustomMessages.some((i) => i.role === "user" &&
180
- (typeof i.content === "string"
181
- ? i.content.includes(message)
182
- : i.content?.some((c) => c.type === "text" && c.text.includes(message))))) {
183
- content.push({ type: "text", text: message });
184
- }
185
- if (files.length)
186
- content.push(...files);
187
- if (content.length) {
188
- messages.push({ role: "user", content });
189
- }
190
- }
191
- messages.push(...otherCustomMessages);
192
- return this.refineMessages(options, messages);
211
+ return messages;
193
212
  }
194
- async getHistories(options) {
213
+ async getHistories({ agentId, userId, sessionId, ...options }) {
195
214
  const { agent } = options;
196
215
  const afs = agent?.afs;
197
216
  if (!afs)
@@ -200,24 +219,21 @@ export class PromptBuilder {
200
219
  if (!historyModule)
201
220
  return [];
202
221
  const history = (await afs.list(historyModule.path, {
222
+ filter: { agentId, userId, sessionId },
203
223
  limit: agent.historyConfig?.maxItems || 10,
204
224
  orderBy: [["createdAt", "desc"]],
205
- })).data;
206
- return history
207
- .reverse()
208
- .map((i) => {
209
- if (!i.content)
210
- return;
211
- const { input, output } = i.content;
212
- if (!input || !output)
213
- return;
214
- return [
215
- { role: "user", content: input },
216
- { role: "agent", content: output },
217
- ];
218
- })
219
- .filter(isNonNullable)
220
- .flat();
225
+ })).data.reverse();
226
+ return (await Promise.all(history.map(async (i) => {
227
+ if (Array.isArray(i.content?.messages) &&
228
+ i.content.messages.every((m) => roleSchema.parse(m?.role))) {
229
+ return i.content.messages;
230
+ }
231
+ const { input, output } = i.content || {};
232
+ if (input && output) {
233
+ return await this.convertMemoriesToMessages([{ content: { input, output } }], options);
234
+ }
235
+ return [];
236
+ }))).flat();
221
237
  }
222
238
  refineMessages(options, messages) {
223
239
  const { autoReorderSystemMessages, autoMergeSystemMessages } = options.agent ?? {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.72.0-beta.6",
3
+ "version": "1.72.0-beta.7",
4
4
  "description": "The functional core of agentic AI",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -93,10 +93,10 @@
93
93
  "zod": "^3.25.67",
94
94
  "zod-from-json-schema": "^0.0.5",
95
95
  "zod-to-json-schema": "^3.24.6",
96
- "@aigne/afs": "^1.4.0-beta.3",
97
- "@aigne/afs-history": "^1.2.0-beta.3",
98
- "@aigne/platform-helpers": "^0.6.7-beta",
99
- "@aigne/observability-api": "^0.11.14-beta.1"
96
+ "@aigne/afs": "^1.4.0-beta.4",
97
+ "@aigne/afs-history": "^1.2.0-beta.4",
98
+ "@aigne/observability-api": "^0.11.14-beta.1",
99
+ "@aigne/platform-helpers": "^0.6.7-beta"
100
100
  },
101
101
  "devDependencies": {
102
102
  "@types/bun": "^1.2.22",