@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
@@ -0,0 +1,120 @@
1
+ import { optional, z } from "zod";
2
+ import { AIAgent } from "../../agents/ai-agent.js";
3
+ import { isNil, omitBy } from "../../utils/type-utils.js";
4
+ const EXTRACTOR_INSTRUCTIONS = `\
5
+ You are a user memory extractor. Your task is to extract long-term user memory facts from session memory that represent verified, persistent user characteristics.
6
+
7
+ ## Session Memory Facts (from current session)
8
+
9
+ ${"```"}yaml alt="session-facts"
10
+ {{ sessionFacts | yaml.stringify }}
11
+ ${"```"}
12
+
13
+ ## Existing User Memory Facts
14
+
15
+ ${"```"}yaml alt="existing-user-facts"
16
+ {{ existingUserFacts | yaml.stringify }}
17
+ ${"```"}
18
+
19
+ ## Guidelines
20
+
21
+ 1. **Extract session facts into long-term user memory**:
22
+ - Promote facts that represent lasting user characteristics (preferences, skills, working style)
23
+ - Filter out temporary or session-specific information
24
+ - Aggregate patterns observed across multiple sessions
25
+ - Verify consistency with existing user memory
26
+
27
+ 2. **User memory should focus on USER characteristics**:
28
+ - **Preferences**: Lasting tool/framework/language preferences the user likes to use
29
+ - **Skills**: Confirmed technical expertise and proficiency levels of the user
30
+ - **Working style**: User's communication preferences, workflow patterns, coding standards
31
+ - **Personal context**: Stable personal context (timezone, team structure, role, work hours)
32
+ - **Project basic info**: What projects the user is working on and their role in them
33
+ - Example: "User is building an AI Agent framework called aigne-framework"
34
+ - Example: "User is the tech lead on a React e-commerce project"
35
+ - DO NOT include project technical details, architecture, or implementation specifics
36
+
37
+ 3. **DO NOT include in user memory**:
38
+ - Session-specific tasks or temporary goals
39
+ - Bugs or issues being debugged in a specific session
40
+ - One-time decisions or experimental choices
41
+ - Unverified assumptions or single-instance observations
42
+ - **Project technical details**: architecture decisions, tech stack specifics, implementation details
43
+ - **Project problems**: current bugs, issues, technical debt, or temporary blockers
44
+ - Information about the codebase structure or specific files/modules
45
+
46
+ 4. **Output only changes (CRITICAL)**:
47
+ - Only output facts that need to be added or updated
48
+ - DO NOT output facts that already exist and don't need changes
49
+ - Each label in newFacts must be unique
50
+ - When a label already exists in user memory:
51
+ - Include it in newFacts ONLY if session provides new information to update it
52
+ - Omit it from newFacts if it doesn't need changes
53
+ - Use the same label format as session memory: "pref-*", "skill-*", "proj-*", "ctx-*"
54
+
55
+ 5. **When to output a fact in newFacts**:
56
+ - **New fact**: Extracting a brand new user characteristic not in existing user memory
57
+ - **Updated fact**: Session provides new information that enhances an existing fact
58
+ - **Refined fact**: Multiple sessions show patterns that refine existing facts
59
+ - DO NOT output if: The fact already exists and session doesn't add new information
60
+
61
+ 6. **When to remove facts** (add labels to removeFacts):
62
+ - Facts are proven to be outdated or incorrect
63
+ - User explicitly changed their approach permanently
64
+ - Facts are redundant with newer, better facts
65
+
66
+ 7. **Confidence scoring**:
67
+ - Higher confidence (0.8-1.0): Verified across multiple sessions or explicitly stated
68
+ - Medium confidence (0.5-0.7): Observed patterns but limited data
69
+ - Lower confidence (0.3-0.4): Tentative patterns, may need more verification
70
+ - Below 0.3: Don't extract into user memory yet
71
+
72
+ 8. **Extraction criteria**:
73
+ - Only extract facts that are clearly valuable long-term
74
+ - Prefer quality over quantity - fewer high-confidence facts are better
75
+ - When in doubt, wait for more sessions to verify the pattern
76
+
77
+ Output the extracted user memory facts and any facts to remove.`;
78
+ export class AIUserMemoryExtractor extends AIAgent {
79
+ constructor(options) {
80
+ super({
81
+ name: "UserMemoryExtractor",
82
+ description: "Extracts long-term user memory facts from session memory",
83
+ inputSchema: z.object({
84
+ sessionFacts: z
85
+ .array(z.object({
86
+ label: z.string(),
87
+ fact: z.string(),
88
+ confidence: optional(z.number()),
89
+ tags: optional(z.array(z.string())),
90
+ }))
91
+ .describe("Session memory facts to extract from"),
92
+ existingUserFacts: optional(z
93
+ .array(z.object({
94
+ label: z.string(),
95
+ fact: z.string(),
96
+ confidence: optional(z.number()),
97
+ tags: optional(z.array(z.string())),
98
+ }))
99
+ .describe("Existing user memory facts for context and deduplication")),
100
+ }),
101
+ outputSchema: z.object({
102
+ newFacts: z
103
+ .array(z.object({
104
+ label: z.string().describe("Short, semantic label for the fact (must be unique)"),
105
+ fact: z.string().describe("The extracted fact content"),
106
+ confidence: optional(z.number().min(0).max(1).describe("Confidence score (0-1)")),
107
+ tags: optional(z.array(z.string()).describe("Classification tags")),
108
+ }))
109
+ .describe("Facts to add or update in user memory. Only include facts that are new or need updates. Do not include unchanged facts."),
110
+ removeFacts: optional(z.array(z.string()).describe("Labels of facts to remove from user memory")),
111
+ }),
112
+ instructions: EXTRACTOR_INSTRUCTIONS,
113
+ taskRenderMode: "hide",
114
+ ...omitBy(options ?? {}, (v) => isNil(v)),
115
+ session: {
116
+ mode: "disabled",
117
+ },
118
+ });
119
+ }
120
+ }
@@ -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,7 +8,8 @@ export async function getHistories(agent) {
8
8
  if (!historyModule)
9
9
  return [];
10
10
  const history = (await afs.list(historyModule.path, {
11
- limit: agent.historyConfig?.maxItems || 10,
11
+ filter,
12
+ limit: 10,
12
13
  orderBy: [["createdAt", "desc"]],
13
14
  })).data;
14
15
  return history
@@ -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,17 @@
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, ChatModelInput, ChatModelInputMessage } from "../agents/chat-model.js";
5
5
  import { type FileUnionContent } from "../agents/model.js";
6
+ import type { Context } from "../aigne/context.js";
7
+ import { AgentSession } from "./agent-session.js";
6
8
  import { ChatMessagesTemplate } from "./template.js";
7
9
  export interface PromptBuilderOptions {
8
10
  instructions?: string | ChatMessagesTemplate;
9
11
  workingDir?: string;
10
12
  }
11
- export interface PromptBuildOptions extends Partial<Pick<AgentInvokeOptions, "context">> {
13
+ export interface PromptBuildOptions {
14
+ context?: Context;
12
15
  agent?: AIAgent;
13
16
  input?: Message;
14
17
  model?: ChatModel;
@@ -26,7 +29,9 @@ export declare class PromptBuilder {
26
29
  instructions?: string | ChatMessagesTemplate;
27
30
  workingDir?: string;
28
31
  copy(): PromptBuilder;
29
- build(options: PromptBuildOptions): Promise<ChatModelInput & {
32
+ build(options: PromptBuildOptions): Promise<Omit<ChatModelInput, "messages"> & {
33
+ session: AgentSession;
34
+ userMessage: ChatModelInputMessage;
30
35
  toolAgents?: Agent[];
31
36
  }>;
32
37
  buildPrompt(options: Pick<PromptBuildOptions, "input" | "context"> & {
@@ -37,11 +42,8 @@ export declare class PromptBuilder {
37
42
  }>;
38
43
  private getTemplateVariables;
39
44
  private buildMessages;
40
- getHistories(options: PromptBuildOptions): Promise<{
41
- role: "user" | "agent";
42
- content: unknown;
43
- }[]>;
44
- private refineMessages;
45
+ private mergeMessages;
46
+ protected deprecatedMemories(message: string | undefined, options: PromptBuildOptions): Promise<ChatModelInputMessage[]>;
45
47
  private convertMemoriesToMessages;
46
48
  private buildResponseFormat;
47
49
  private buildTools;
@@ -1,5 +1,5 @@
1
- import { AFSHistory } from "@aigne/afs-history";
2
1
  import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
+ import { v7 } from "@aigne/uuid";
3
3
  import { stringify } from "yaml";
4
4
  import { ZodObject } from "zod";
5
5
  import { zodToJsonSchema } from "zod-to-json-schema";
@@ -8,9 +8,9 @@ import { DEFAULT_OUTPUT_FILE_KEY, DEFAULT_OUTPUT_KEY } from "../agents/ai-agent.
8
8
  import { fileUnionContentsSchema } from "../agents/model.js";
9
9
  import { optionalize } from "../loader/schema.js";
10
10
  import { outputSchemaToResponseFormatSchema } from "../utils/json-schema.js";
11
- import { checkArguments, flat, isNonNullable, isRecord, partition, unique, } from "../utils/type-utils.js";
11
+ import { checkArguments, flat, isRecord, partition, unique } from "../utils/type-utils.js";
12
+ import { AgentSession } from "./agent-session.js";
12
13
  import { createPromptBuilderContext } from "./context/index.js";
13
- import { AFS_EXECUTABLE_TOOLS_PROMPT_TEMPLATE, getAFSSystemPrompt, } from "./prompts/afs-builtin-prompt.js";
14
14
  import { MEMORY_MESSAGE_TEMPLATE } from "./prompts/memory-message-template.js";
15
15
  import { STRUCTURED_STREAM_INSTRUCTIONS } from "./prompts/structured-stream-instructions.js";
16
16
  import { getAFSSkills } from "./skills/afs/index.js";
@@ -37,10 +37,10 @@ export class PromptBuilder {
37
37
  content = i.content.text;
38
38
  else if (i.content.type === "resource") {
39
39
  const { resource } = i.content;
40
- if (typeof resource.text === "string") {
40
+ if ("text" in resource && typeof resource.text === "string") {
41
41
  content = resource.text;
42
42
  }
43
- else if (typeof resource.blob === "string") {
43
+ else if ("blob" in resource && typeof resource.blob === "string") {
44
44
  content = [{ type: "url", url: resource.blob }];
45
45
  }
46
46
  }
@@ -70,13 +70,28 @@ export class PromptBuilder {
70
70
  });
71
71
  }
72
72
  async build(options) {
73
+ let { userId, sessionId } = options.context?.userContext || {};
74
+ const agentId = options.agent?.name;
75
+ const afs = options.agent?.afs;
76
+ sessionId ||= v7();
77
+ const session = new AgentSession({
78
+ agentId,
79
+ userId,
80
+ sessionId,
81
+ afs,
82
+ ...options.agent?.session,
83
+ });
84
+ const { systemMessage, userMessage } = await this.buildMessages(options);
85
+ if (systemMessage)
86
+ await session.setSystemMessages(systemMessage);
73
87
  return {
74
- messages: await this.buildMessages(options),
88
+ userMessage,
75
89
  responseFormat: options.agent?.structuredStreamMode
76
90
  ? undefined
77
91
  : this.buildResponseFormat(options),
78
92
  outputFileType: options.agent?.outputFileType,
79
93
  ...(await this.buildTools(options)),
94
+ session,
80
95
  };
81
96
  }
82
97
  async buildPrompt(options) {
@@ -99,85 +114,41 @@ export class PromptBuilder {
99
114
  const { input } = options;
100
115
  const inputKey = options.agent?.inputKey;
101
116
  const message = inputKey && typeof input?.[inputKey] === "string" ? input[inputKey] : undefined;
102
- const [messages, otherCustomMessages] = partition((await (typeof this.instructions === "string"
117
+ const template = typeof this.instructions === "string"
103
118
  ? ChatMessagesTemplate.from([SystemMessageTemplate.from(this.instructions)])
104
- : this.instructions)?.format(this.getTemplateVariables(options), { workingDir: this.workingDir })) ?? [], (i) => i.role === "system");
119
+ : this.instructions;
120
+ const [systemMessages, userMessages] = partition((await template?.format(this.getTemplateVariables(options), {
121
+ workingDir: this.workingDir,
122
+ })) ?? [], (i) => i.role === "system");
105
123
  const inputFileKey = options.agent?.inputFileKey;
106
124
  const files = flat(inputFileKey
107
125
  ? checkArguments("Check input files", optionalize(fileUnionContentsSchema), input?.[inputFileKey])
108
126
  : null);
109
- const memories = [];
110
- if (options.agent && options.context) {
111
- memories.push(...(await options.agent.retrieveMemories({ search: message }, { context: options.context })));
112
- }
113
- if (options.agent?.useMemoriesFromContext && options.context?.memories?.length) {
114
- memories.push(...options.context.memories);
115
- }
116
- const afs = options.agent?.afs;
117
- if (afs && options.agent?.historyConfig?.disabled !== true) {
118
- const historyModule = (await afs.listModules()).find((m) => m.module instanceof AFSHistory);
119
- messages.push(await SystemMessageTemplate.from(await getAFSSystemPrompt(afs)).format({}));
120
- if (historyModule) {
121
- const history = await afs.list(historyModule.path, {
122
- limit: options.agent?.maxRetrieveMemoryCount || 10,
123
- orderBy: [["createdAt", "desc"]],
124
- });
125
- memories.push(...history.data
126
- .reverse()
127
- .filter((i) => isNonNullable(i.content)));
128
- if (message) {
129
- const result = (await afs.search("/", message)).data;
130
- const ms = result
131
- .map((entry) => {
132
- if (entry.metadata?.execute)
133
- return null;
134
- const content = entry.content || entry.summary;
135
- if (!content)
136
- return null;
137
- return {
138
- content,
139
- description: entry.description,
140
- };
141
- })
142
- .filter(isNonNullable);
143
- memories.push(...ms);
144
- const executable = result.filter((i) => !!i.metadata?.execute);
145
- if (executable.length) {
146
- messages.push({
147
- role: "system",
148
- content: await PromptTemplate.from(AFS_EXECUTABLE_TOOLS_PROMPT_TEMPLATE).format({
149
- tools: executable.map((entry) => ({
150
- path: entry.path,
151
- name: entry.metadata.execute.name,
152
- description: entry.metadata.execute.description,
153
- inputSchema: entry.metadata.execute.inputSchema,
154
- outputSchema: entry.metadata.execute.outputSchema,
155
- })),
156
- }),
157
- });
158
- }
159
- }
160
- }
127
+ if (options.agent?.memories.length || options.context?.memories.length) {
128
+ const deprecatedMemories = await this.deprecatedMemories(message, options);
129
+ if (deprecatedMemories.length)
130
+ systemMessages.push(...deprecatedMemories);
161
131
  }
162
- if (memories.length)
163
- messages.push(...(await this.convertMemoriesToMessages(memories, options)));
164
132
  // if the agent is using structured stream mode, add the instructions
165
133
  const { structuredStreamMode, outputSchema } = options.agent || {};
166
134
  if (structuredStreamMode && outputSchema) {
167
135
  const instructions = options.agent?.customStructuredStreamInstructions?.instructions ||
168
136
  PromptBuilder.from(STRUCTURED_STREAM_INSTRUCTIONS.instructions);
169
- messages.push(...(await instructions.buildMessages({
170
- input: {
171
- ...input,
172
- outputJsonSchema: zodToJsonSchema(outputSchema),
173
- },
174
- })));
137
+ systemMessages.push({
138
+ role: "system",
139
+ content: (await instructions.buildPrompt({
140
+ input: {
141
+ ...input,
142
+ outputJsonSchema: zodToJsonSchema(outputSchema),
143
+ },
144
+ })).prompt,
145
+ });
175
146
  }
176
147
  if (message || files.length) {
177
148
  const content = [];
178
149
  if (message &&
179
- // avoid duplicate user messages: developer may have already included the message in the custom user messages
180
- !otherCustomMessages.some((i) => i.role === "user" &&
150
+ // avoid duplicate user messages: developer may have already included the message in the messages
151
+ !userMessages.some((i) => i.role === "user" &&
181
152
  (typeof i.content === "string"
182
153
  ? i.content.includes(message)
183
154
  : i.content?.some((c) => c.type === "text" && c.text.includes(message))))) {
@@ -186,63 +157,51 @@ export class PromptBuilder {
186
157
  if (files.length)
187
158
  content.push(...files);
188
159
  if (content.length) {
189
- messages.push({ role: "user", content });
160
+ userMessages.push({ role: "user", content });
190
161
  }
191
162
  }
192
- messages.push(...otherCustomMessages);
193
- return this.refineMessages(options, messages);
163
+ let systemMessage = this.mergeMessages(systemMessages, "system");
164
+ if (!systemMessage.content?.length)
165
+ systemMessage = undefined;
166
+ let userMessage = this.mergeMessages(userMessages, "user");
167
+ if (!userMessage.content?.length) {
168
+ userMessage = { role: "user", content: systemMessage?.content };
169
+ systemMessage = undefined;
170
+ }
171
+ if (!userMessage.content?.length)
172
+ throw new Error("User message cannot be empty.");
173
+ return {
174
+ systemMessage,
175
+ userMessage,
176
+ };
194
177
  }
195
- async getHistories(options) {
196
- const { agent } = options;
197
- const afs = agent?.afs;
198
- if (!afs)
199
- return [];
200
- const historyModule = (await afs.listModules()).find((m) => m.module instanceof AFSHistory);
201
- if (!historyModule)
202
- return [];
203
- const history = (await afs.list(historyModule.path, {
204
- limit: agent.historyConfig?.maxItems || 10,
205
- orderBy: [["createdAt", "desc"]],
206
- })).data;
207
- return history
208
- .reverse()
209
- .map((i) => {
210
- if (!i.content)
211
- return;
212
- const { input, output } = i.content;
213
- if (!input || !output)
214
- return;
215
- return [
216
- { role: "user", content: input },
217
- { role: "agent", content: output },
218
- ];
219
- })
220
- .filter(isNonNullable)
221
- .flat();
178
+ mergeMessages(messages, role) {
179
+ const content = [];
180
+ for (const message of messages) {
181
+ if (typeof message.content === "string") {
182
+ content.push({ type: "text", text: message.content });
183
+ }
184
+ else if (Array.isArray(message.content)) {
185
+ content.push(...message.content);
186
+ }
187
+ else if (message.content) {
188
+ throw new Error(`Unsupported message content type: ${typeof message.content}`);
189
+ }
190
+ }
191
+ return { role, content };
222
192
  }
223
- refineMessages(options, messages) {
224
- const { autoReorderSystemMessages, autoMergeSystemMessages } = options.agent ?? {};
225
- if (!autoReorderSystemMessages && !autoMergeSystemMessages)
226
- return messages;
227
- const [systemMessages, otherMessages] = partition(messages, (m) => m.role === "system");
228
- if (!autoMergeSystemMessages) {
229
- return systemMessages.concat(otherMessages);
193
+ async deprecatedMemories(message, options) {
194
+ const messages = [];
195
+ const memories = [];
196
+ if (options.agent && options.context) {
197
+ memories.push(...(await options.agent.retrieveMemories({ search: message }, { context: options.context })));
230
198
  }
231
- const result = [];
232
- if (systemMessages.length) {
233
- result.push({
234
- role: "system",
235
- content: systemMessages
236
- .map((i) => typeof i.content === "string"
237
- ? i.content
238
- : i.content
239
- ?.map((c) => (c.type === "text" ? c.text : null))
240
- .filter(isNonNullable)
241
- .join("\n"))
242
- .join("\n"),
243
- });
199
+ if (options.agent?.useMemoriesFromContext && options.context?.memories?.length) {
200
+ memories.push(...options.context.memories);
244
201
  }
245
- return result.concat(otherMessages);
202
+ if (memories.length)
203
+ messages.push(...(await this.convertMemoriesToMessages(memories, options)));
204
+ return messages;
246
205
  }
247
206
  async convertMemoriesToMessages(memories, options) {
248
207
  const messages = [];
@@ -0,0 +1,19 @@
1
+ import { Agent, type AgentOptions, type Message } from "../../../../agents/agent.js";
2
+ import type { PromiseOrValue } from "../../../../utils/type-utils.js";
3
+ import type { Skill } from "./skill-loader.js";
4
+ export interface SkillToolInput extends Message {
5
+ skill: string;
6
+ args?: string;
7
+ }
8
+ export interface SkillToolOutput extends Message {
9
+ result: string;
10
+ }
11
+ export interface SkillToolOptions extends AgentOptions<SkillToolInput, SkillToolOutput> {
12
+ agentSkills: Skill[];
13
+ }
14
+ export declare class AgentSkill extends Agent<SkillToolInput, SkillToolOutput> {
15
+ formatOutput(output: SkillToolOutput | Message): PromiseOrValue<string>;
16
+ constructor(options: SkillToolOptions);
17
+ private agentSkills;
18
+ process(input: SkillToolInput): Promise<SkillToolOutput>;
19
+ }
@@ -0,0 +1,65 @@
1
+ import { z } from "zod";
2
+ import { Agent } from "../../../../agents/agent.js";
3
+ const skillToolInputSchema = z.object({
4
+ skill: z.string().describe("The name of the skill agent to invoke."),
5
+ args: z.string().optional().describe("The arguments to pass to the skill."),
6
+ });
7
+ export class AgentSkill extends Agent {
8
+ formatOutput(output) {
9
+ if ("result" in output && typeof output.result === "string") {
10
+ return output.result;
11
+ }
12
+ return super.formatOutput(output);
13
+ }
14
+ constructor(options) {
15
+ super({
16
+ name: "Skill",
17
+ taskTitle: "Invoke {{skill}}: {{args}}",
18
+ ...options,
19
+ description: `\
20
+ Execute a skill within the main conversation
21
+
22
+ <skills_instructions>
23
+ When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
24
+
25
+ When users ask you to run a "slash command" or reference "/" (e.g., "/commit", "/review-pr"), they are referring to a skill. Use this tool to invoke the corresponding skill.
26
+
27
+ User: "run /commit" Assistant: [Calls Skill tool with skill: "commit"]
28
+ How to invoke:
29
+
30
+ Use this tool with the skill name and optional arguments
31
+
32
+ Important:
33
+
34
+ When a skill is relevant, you must invoke this tool IMMEDIATELY as your first action
35
+ NEVER just announce or mention a skill in your text response without actually calling this tool
36
+ This is a BLOCKING REQUIREMENT: invoke the relevant Skill tool BEFORE generating any other response about the task
37
+ Only use skills listed in <available_skills> below
38
+ Do not invoke a skill that is already running
39
+ Do not use this tool for built-in CLI commands (like /help, /clear, etc.)
40
+ </skills_instructions>
41
+
42
+ <available_skills>
43
+ ${options.agentSkills.map((s) => `${s.name}: ${s.description}`).join("\n\n")}
44
+ </available_skills>
45
+ `,
46
+ inputSchema: skillToolInputSchema,
47
+ });
48
+ this.agentSkills = options.agentSkills;
49
+ }
50
+ agentSkills;
51
+ async process(input) {
52
+ const skill = this.agentSkills.find((s) => s.name === input.skill);
53
+ if (!skill)
54
+ throw new Error(`Skill not found: ${input.skill}`);
55
+ return {
56
+ result: `\
57
+ Base directory for this skill: ${skill.path}
58
+
59
+ ${skill.content}
60
+
61
+ ${input.args ? `ARGUMENTS: ${input.args ?? "None"}` : ""}
62
+ `,
63
+ };
64
+ }
65
+ }
@@ -0,0 +1,12 @@
1
+ import type { AFS } from "@aigne/afs";
2
+ import { AgentSkill } from "./agent-skill.js";
3
+ export interface Skill {
4
+ path: string;
5
+ name: string;
6
+ description: string;
7
+ content: string;
8
+ }
9
+ export declare function discoverSkillsFromAFS(afs: AFS): Promise<Skill[]>;
10
+ export declare function loadAgentSkillFromAFS({ afs, }: {
11
+ afs: AFS;
12
+ }): Promise<AgentSkill | undefined>;
@@ -0,0 +1,43 @@
1
+ import fm from "front-matter";
2
+ import { AgentSkill } from "./agent-skill.js";
3
+ function parseSkill(content, path) {
4
+ const meta = fm(content);
5
+ return {
6
+ path,
7
+ name: meta.attributes.name,
8
+ description: meta.attributes.description,
9
+ content: meta.body,
10
+ };
11
+ }
12
+ export async function discoverSkillsFromAFS(afs) {
13
+ const modules = await afs.listModules();
14
+ const filtered = modules.filter(({ module: m }) => m.agentSkills === true);
15
+ if (!filtered.length)
16
+ return [];
17
+ const skills = [];
18
+ for (const module of filtered) {
19
+ const data = (await afs
20
+ .list(module.path, {
21
+ pattern: "**/SKILL.md",
22
+ maxDepth: 10,
23
+ })
24
+ .catch(() => ({ data: [] }))).data;
25
+ for (const entry of data) {
26
+ const { data: file } = await afs.read(entry.path);
27
+ if (typeof file?.content !== "string")
28
+ continue;
29
+ const dirname = entry.path.split("/").slice(0, -1).join("/");
30
+ const skill = parseSkill(file.content, dirname);
31
+ skills.push(skill);
32
+ }
33
+ }
34
+ return skills;
35
+ }
36
+ export async function loadAgentSkillFromAFS({ afs, }) {
37
+ const skills = await discoverSkillsFromAFS(afs);
38
+ if (!skills.length)
39
+ return;
40
+ return new AgentSkill({
41
+ agentSkills: skills,
42
+ });
43
+ }
@@ -4,15 +4,27 @@ export class AFSDeleteAgent extends AFSSkillBase {
4
4
  constructor(options) {
5
5
  super({
6
6
  name: "afs_delete",
7
- description: "Permanently delete files or directories. Use when removing unwanted files or cleaning up temporary data.",
7
+ description: `Permanently delete files or directories from the Agentic File System (AFS)
8
+ - Removes files or directories at the specified AFS path
9
+ - Supports recursive deletion for directories with contents
10
+ - Use with caution as deletion is permanent
11
+
12
+ Usage:
13
+ - The path must be an absolute AFS path starting with "/" (e.g., "/docs/old-file.md", "/temp")
14
+ - This is NOT a local system file path - it operates within the AFS virtual file system
15
+ - To delete a directory, you MUST set recursive=true
16
+ - Deleting a non-empty directory without recursive=true will fail
17
+ - This operation cannot be undone`,
8
18
  ...options,
9
19
  inputSchema: z.object({
10
- path: z.string().describe("Absolute file or directory path to delete"),
20
+ path: z
21
+ .string()
22
+ .describe("Absolute AFS path to delete (e.g., '/docs/old-file.md', '/temp'). Must start with '/'"),
11
23
  recursive: z
12
24
  .boolean()
13
25
  .optional()
14
26
  .default(false)
15
- .describe("Allow directory deletion (default: false, required for directories)"),
27
+ .describe("MUST be set to true to delete directories. Default: false (files only)"),
16
28
  }),
17
29
  outputSchema: z.object({
18
30
  status: z.string(),