@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.
- package/CHANGELOG.md +261 -0
- package/lib/cjs/agents/agent.d.ts +42 -11
- package/lib/cjs/agents/agent.js +34 -8
- package/lib/cjs/agents/ai-agent.d.ts +63 -4
- package/lib/cjs/agents/ai-agent.js +154 -20
- package/lib/cjs/agents/chat-model.d.ts +157 -0
- package/lib/cjs/agents/chat-model.js +71 -6
- package/lib/cjs/agents/image-agent.d.ts +17 -1
- package/lib/cjs/agents/image-agent.js +16 -0
- package/lib/cjs/agents/image-model.d.ts +12 -2
- package/lib/cjs/agents/image-model.js +1 -1
- package/lib/cjs/agents/mcp-agent.d.ts +17 -0
- package/lib/cjs/agents/mcp-agent.js +18 -0
- package/lib/cjs/agents/model.d.ts +3 -3
- package/lib/cjs/agents/model.js +2 -2
- package/lib/cjs/agents/team-agent.d.ts +55 -0
- package/lib/cjs/agents/team-agent.js +31 -0
- package/lib/cjs/agents/transform-agent.d.ts +12 -0
- package/lib/cjs/agents/transform-agent.js +13 -0
- package/lib/cjs/agents/video-model.d.ts +10 -0
- package/lib/cjs/agents/video-model.js +1 -1
- package/lib/cjs/aigne/context.js +1 -3
- package/lib/cjs/aigne/usage.d.ts +4 -0
- package/lib/cjs/aigne/usage.js +6 -0
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/loader/agent-yaml.d.ts +5 -63
- package/lib/cjs/loader/agent-yaml.js +4 -129
- package/lib/cjs/loader/agents.d.ts +4 -0
- package/lib/cjs/loader/agents.js +17 -0
- package/lib/cjs/loader/index.d.ts +16 -12
- package/lib/cjs/loader/index.js +20 -81
- package/lib/cjs/loader/schema.d.ts +21 -6
- package/lib/cjs/loader/schema.js +60 -1
- package/lib/cjs/memory/recorder.d.ts +4 -4
- package/lib/cjs/memory/retriever.d.ts +4 -4
- package/lib/cjs/prompt/agent-session.d.ts +163 -0
- package/lib/cjs/prompt/agent-session.js +1008 -0
- package/lib/cjs/prompt/compact/compactor.d.ts +7 -0
- package/lib/cjs/prompt/compact/compactor.js +52 -0
- package/lib/cjs/prompt/compact/session-memory-extractor.d.ts +7 -0
- package/lib/cjs/prompt/compact/session-memory-extractor.js +143 -0
- package/lib/cjs/prompt/compact/types.d.ts +336 -0
- package/lib/cjs/prompt/compact/types.js +53 -0
- package/lib/cjs/prompt/compact/user-memory-extractor.d.ts +7 -0
- package/lib/cjs/prompt/compact/user-memory-extractor.js +124 -0
- package/lib/cjs/prompt/context/afs/history.d.ts +5 -1
- package/lib/cjs/prompt/context/afs/history.js +3 -2
- package/lib/cjs/prompt/context/afs/index.js +8 -1
- package/lib/cjs/prompt/prompt-builder.d.ts +11 -9
- package/lib/cjs/prompt/prompt-builder.js +79 -120
- package/lib/cjs/prompt/skills/afs/agent-skill/agent-skill.d.ts +19 -0
- package/lib/cjs/prompt/skills/afs/agent-skill/agent-skill.js +69 -0
- package/lib/cjs/prompt/skills/afs/agent-skill/skill-loader.d.ts +12 -0
- package/lib/cjs/prompt/skills/afs/agent-skill/skill-loader.js +50 -0
- package/lib/cjs/prompt/skills/afs/delete.js +15 -3
- package/lib/cjs/prompt/skills/afs/edit.d.ts +6 -9
- package/lib/cjs/prompt/skills/afs/edit.js +85 -59
- package/lib/cjs/prompt/skills/afs/exec.js +17 -6
- package/lib/cjs/prompt/skills/afs/index.js +4 -1
- package/lib/cjs/prompt/skills/afs/list.d.ts +2 -0
- package/lib/cjs/prompt/skills/afs/list.js +35 -11
- package/lib/cjs/prompt/skills/afs/read.d.ts +9 -3
- package/lib/cjs/prompt/skills/afs/read.js +67 -15
- package/lib/cjs/prompt/skills/afs/rename.js +18 -4
- package/lib/cjs/prompt/skills/afs/search.js +21 -5
- package/lib/cjs/prompt/skills/afs/write.js +20 -6
- package/lib/cjs/prompt/template.d.ts +84 -9
- package/lib/cjs/prompt/template.js +46 -17
- package/lib/cjs/utils/mcp-utils.js +1 -1
- package/lib/cjs/utils/token-estimator.js +1 -1
- package/lib/dts/agents/agent.d.ts +42 -11
- package/lib/dts/agents/ai-agent.d.ts +63 -4
- package/lib/dts/agents/chat-model.d.ts +157 -0
- package/lib/dts/agents/image-agent.d.ts +17 -1
- package/lib/dts/agents/image-model.d.ts +12 -2
- package/lib/dts/agents/mcp-agent.d.ts +17 -0
- package/lib/dts/agents/model.d.ts +3 -3
- package/lib/dts/agents/team-agent.d.ts +55 -0
- package/lib/dts/agents/transform-agent.d.ts +12 -0
- package/lib/dts/agents/video-model.d.ts +10 -0
- package/lib/dts/aigne/context.d.ts +2 -2
- package/lib/dts/aigne/usage.d.ts +4 -0
- package/lib/dts/index.d.ts +1 -0
- package/lib/dts/loader/agent-yaml.d.ts +5 -63
- package/lib/dts/loader/agents.d.ts +4 -0
- package/lib/dts/loader/index.d.ts +16 -12
- package/lib/dts/loader/schema.d.ts +21 -6
- package/lib/dts/memory/recorder.d.ts +4 -4
- package/lib/dts/memory/retriever.d.ts +4 -4
- package/lib/dts/prompt/agent-session.d.ts +163 -0
- package/lib/dts/prompt/compact/compactor.d.ts +7 -0
- package/lib/dts/prompt/compact/session-memory-extractor.d.ts +7 -0
- package/lib/dts/prompt/compact/types.d.ts +336 -0
- package/lib/dts/prompt/compact/user-memory-extractor.d.ts +7 -0
- package/lib/dts/prompt/context/afs/history.d.ts +5 -1
- package/lib/dts/prompt/prompt-builder.d.ts +11 -9
- package/lib/dts/prompt/skills/afs/agent-skill/agent-skill.d.ts +19 -0
- package/lib/dts/prompt/skills/afs/agent-skill/skill-loader.d.ts +12 -0
- package/lib/dts/prompt/skills/afs/edit.d.ts +6 -9
- package/lib/dts/prompt/skills/afs/list.d.ts +2 -0
- package/lib/dts/prompt/skills/afs/read.d.ts +9 -3
- package/lib/dts/prompt/template.d.ts +84 -9
- package/lib/esm/agents/agent.d.ts +42 -11
- package/lib/esm/agents/agent.js +34 -8
- package/lib/esm/agents/ai-agent.d.ts +63 -4
- package/lib/esm/agents/ai-agent.js +154 -20
- package/lib/esm/agents/chat-model.d.ts +157 -0
- package/lib/esm/agents/chat-model.js +70 -5
- package/lib/esm/agents/image-agent.d.ts +17 -1
- package/lib/esm/agents/image-agent.js +16 -0
- package/lib/esm/agents/image-model.d.ts +12 -2
- package/lib/esm/agents/image-model.js +1 -1
- package/lib/esm/agents/mcp-agent.d.ts +17 -0
- package/lib/esm/agents/mcp-agent.js +18 -0
- package/lib/esm/agents/model.d.ts +3 -3
- package/lib/esm/agents/model.js +2 -2
- package/lib/esm/agents/team-agent.d.ts +55 -0
- package/lib/esm/agents/team-agent.js +31 -0
- package/lib/esm/agents/transform-agent.d.ts +12 -0
- package/lib/esm/agents/transform-agent.js +13 -0
- package/lib/esm/agents/video-model.d.ts +10 -0
- package/lib/esm/agents/video-model.js +1 -1
- package/lib/esm/aigne/context.d.ts +2 -2
- package/lib/esm/aigne/context.js +2 -4
- package/lib/esm/aigne/usage.d.ts +4 -0
- package/lib/esm/aigne/usage.js +6 -0
- package/lib/esm/index.d.ts +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/loader/agent-yaml.d.ts +5 -63
- package/lib/esm/loader/agent-yaml.js +4 -128
- package/lib/esm/loader/agents.d.ts +4 -0
- package/lib/esm/loader/agents.js +14 -0
- package/lib/esm/loader/index.d.ts +16 -12
- package/lib/esm/loader/index.js +21 -81
- package/lib/esm/loader/schema.d.ts +21 -6
- package/lib/esm/loader/schema.js +57 -0
- package/lib/esm/memory/recorder.d.ts +4 -4
- package/lib/esm/memory/retriever.d.ts +4 -4
- package/lib/esm/prompt/agent-session.d.ts +163 -0
- package/lib/esm/prompt/agent-session.js +968 -0
- package/lib/esm/prompt/compact/compactor.d.ts +7 -0
- package/lib/esm/prompt/compact/compactor.js +48 -0
- package/lib/esm/prompt/compact/session-memory-extractor.d.ts +7 -0
- package/lib/esm/prompt/compact/session-memory-extractor.js +139 -0
- package/lib/esm/prompt/compact/types.d.ts +336 -0
- package/lib/esm/prompt/compact/types.js +50 -0
- package/lib/esm/prompt/compact/user-memory-extractor.d.ts +7 -0
- package/lib/esm/prompt/compact/user-memory-extractor.js +120 -0
- package/lib/esm/prompt/context/afs/history.d.ts +5 -1
- package/lib/esm/prompt/context/afs/history.js +3 -2
- package/lib/esm/prompt/context/afs/index.js +8 -1
- package/lib/esm/prompt/prompt-builder.d.ts +11 -9
- package/lib/esm/prompt/prompt-builder.js +80 -121
- package/lib/esm/prompt/skills/afs/agent-skill/agent-skill.d.ts +19 -0
- package/lib/esm/prompt/skills/afs/agent-skill/agent-skill.js +65 -0
- package/lib/esm/prompt/skills/afs/agent-skill/skill-loader.d.ts +12 -0
- package/lib/esm/prompt/skills/afs/agent-skill/skill-loader.js +43 -0
- package/lib/esm/prompt/skills/afs/delete.js +15 -3
- package/lib/esm/prompt/skills/afs/edit.d.ts +6 -9
- package/lib/esm/prompt/skills/afs/edit.js +85 -59
- package/lib/esm/prompt/skills/afs/exec.js +17 -6
- package/lib/esm/prompt/skills/afs/index.js +4 -1
- package/lib/esm/prompt/skills/afs/list.d.ts +2 -0
- package/lib/esm/prompt/skills/afs/list.js +35 -11
- package/lib/esm/prompt/skills/afs/read.d.ts +9 -3
- package/lib/esm/prompt/skills/afs/read.js +67 -15
- package/lib/esm/prompt/skills/afs/rename.js +18 -4
- package/lib/esm/prompt/skills/afs/search.js +21 -5
- package/lib/esm/prompt/skills/afs/write.js +20 -6
- package/lib/esm/prompt/template.d.ts +84 -9
- package/lib/esm/prompt/template.js +46 -17
- package/lib/esm/utils/mcp-utils.js +1 -1
- package/lib/esm/utils/token-estimator.js +1 -1
- 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
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
41
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
117
|
+
const template = typeof this.instructions === "string"
|
|
103
118
|
? ChatMessagesTemplate.from([SystemMessageTemplate.from(this.instructions)])
|
|
104
|
-
: this.instructions
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
|
180
|
-
!
|
|
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
|
-
|
|
160
|
+
userMessages.push({ role: "user", content });
|
|
190
161
|
}
|
|
191
162
|
}
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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("
|
|
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(),
|