foliko 2.0.17 → 2.0.19
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/.claude/settings.local.json +1 -3
- package/.editorconfig +56 -56
- package/.lintstagedrc +7 -7
- package/.prettierignore +29 -29
- package/.prettierrc +11 -11
- package/Dockerfile +63 -63
- package/install.ps1 +129 -129
- package/install.sh +121 -121
- package/package.json +1 -1
- package/plugins/ambient/ExplorerLoop.js +10 -10
- package/plugins/core/scheduler/index.js +1 -1
- package/plugins/core/session/index.js +1 -0
- package/plugins/core/workflow/context.js +941 -941
- package/plugins/core/workflow/engine.js +66 -66
- package/plugins/core/workflow/js-runner.js +318 -318
- package/plugins/core/workflow/json-runner.js +323 -323
- package/plugins/core/workflow/stages/choice.js +74 -74
- package/plugins/core/workflow/stages/each.js +123 -123
- package/plugins/core/workflow/stages/parallel.js +69 -69
- package/plugins/messaging/feishu/index.js +5 -5
- package/plugins/messaging/qq/index.js +3 -3
- package/plugins/messaging/telegram/index.js +3 -3
- package/plugins/messaging/weixin/index.js +5 -5
- package/skills/find-skills/SKILL.md +133 -133
- package/src/agent/chat.js +124 -15
- package/src/session/session.js +2 -1
- package/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.jsonl +36 -0
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: find-skills
|
|
3
|
-
description: Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Find Skills
|
|
7
|
-
|
|
8
|
-
This skill helps you discover and install skills from the open agent skills ecosystem.
|
|
9
|
-
|
|
10
|
-
## When to Use This Skill
|
|
11
|
-
|
|
12
|
-
Use this skill when the user:
|
|
13
|
-
|
|
14
|
-
- Asks "how do I do X" where X might be a common task with an existing skill
|
|
15
|
-
- Says "find a skill for X" or "is there a skill for X"
|
|
16
|
-
- Asks "can you do X" where X is a specialized capability
|
|
17
|
-
- Expresses interest in extending agent capabilities
|
|
18
|
-
- Wants to search for tools, templates, or workflows
|
|
19
|
-
- Mentions they wish they had help with a specific domain (design, testing, deployment, etc.)
|
|
20
|
-
|
|
21
|
-
## What is the Skills CLI?
|
|
22
|
-
|
|
23
|
-
The Skills CLI (`npx skills`) is the package manager for the open agent skills ecosystem. Skills are modular packages that extend agent capabilities with specialized knowledge, workflows, and tools.
|
|
24
|
-
|
|
25
|
-
**Key commands:**
|
|
26
|
-
|
|
27
|
-
- `npx skills find [query]` - Search for skills interactively or by keyword
|
|
28
|
-
- `npx skills add <package>` - Install a skill from GitHub or other sources
|
|
29
|
-
- `npx skills check` - Check for skill updates
|
|
30
|
-
- `npx skills update` - Update all installed skills
|
|
31
|
-
|
|
32
|
-
**Browse skills at:** https://skills.sh/
|
|
33
|
-
|
|
34
|
-
## How to Help Users Find Skills
|
|
35
|
-
|
|
36
|
-
### Step 1: Understand What They Need
|
|
37
|
-
|
|
38
|
-
When a user asks for help with something, identify:
|
|
39
|
-
|
|
40
|
-
1. The domain (e.g., React, testing, design, deployment)
|
|
41
|
-
2. The specific task (e.g., writing tests, creating animations, reviewing PRs)
|
|
42
|
-
3. Whether this is a common enough task that a skill likely exists
|
|
43
|
-
|
|
44
|
-
### Step 2: Search for Skills
|
|
45
|
-
|
|
46
|
-
Run the find command with a relevant query:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
npx skills find [query]
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
For example:
|
|
53
|
-
|
|
54
|
-
- User asks "how do I make my React app faster?" → `npx skills find react performance`
|
|
55
|
-
- User asks "can you help me with PR reviews?" → `npx skills find pr review`
|
|
56
|
-
- User asks "I need to create a changelog" → `npx skills find changelog`
|
|
57
|
-
|
|
58
|
-
The command will return results like:
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
Install with npx skills add <owner/repo@skill>
|
|
62
|
-
|
|
63
|
-
vercel-labs/agent-skills@vercel-react-best-practices
|
|
64
|
-
└ https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Step 3: Present Options to the User
|
|
68
|
-
|
|
69
|
-
When you find relevant skills, present them to the user with:
|
|
70
|
-
|
|
71
|
-
1. The skill name and what it does
|
|
72
|
-
2. The install command they can run
|
|
73
|
-
3. A link to learn more at skills.sh
|
|
74
|
-
|
|
75
|
-
Example response:
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
I found a skill that might help! The "vercel-react-best-practices" skill provides
|
|
79
|
-
React and Next.js performance optimization guidelines from Vercel Engineering.
|
|
80
|
-
|
|
81
|
-
To install it:
|
|
82
|
-
npx skills add vercel-labs/agent-skills@vercel-react-best-practices
|
|
83
|
-
|
|
84
|
-
Learn more: https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Step 4: Offer to Install
|
|
88
|
-
|
|
89
|
-
If the user wants to proceed, you can install the skill for them:
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
npx skills add <owner/repo@skill> -g -y
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
The `-g` flag installs globally (user-level) and `-y` skips confirmation prompts.
|
|
96
|
-
|
|
97
|
-
## Common Skill Categories
|
|
98
|
-
|
|
99
|
-
When searching, consider these common categories:
|
|
100
|
-
|
|
101
|
-
| Category | Example Queries |
|
|
102
|
-
| --------------- | ---------------------------------------- |
|
|
103
|
-
| Web Development | react, nextjs, typescript, css, tailwind |
|
|
104
|
-
| Testing | testing, jest, playwright, e2e |
|
|
105
|
-
| DevOps | deploy, docker, kubernetes, ci-cd |
|
|
106
|
-
| Documentation | docs, readme, changelog, api-docs |
|
|
107
|
-
| Code Quality | review, lint, refactor, best-practices |
|
|
108
|
-
| Design | ui, ux, design-system, accessibility |
|
|
109
|
-
| Productivity | workflow, automation, git |
|
|
110
|
-
|
|
111
|
-
## Tips for Effective Searches
|
|
112
|
-
|
|
113
|
-
1. **Use specific keywords**: "react testing" is better than just "testing"
|
|
114
|
-
2. **Try alternative terms**: If "deploy" doesn't work, try "deployment" or "ci-cd"
|
|
115
|
-
3. **Check popular sources**: Many skills come from `vercel-labs/agent-skills` or `ComposioHQ/awesome-claude-skills`
|
|
116
|
-
|
|
117
|
-
## When No Skills Are Found
|
|
118
|
-
|
|
119
|
-
If no relevant skills exist:
|
|
120
|
-
|
|
121
|
-
1. Acknowledge that no existing skill was found
|
|
122
|
-
2. Offer to help with the task directly using your general capabilities
|
|
123
|
-
3. Suggest the user could create their own skill with `npx skills init`
|
|
124
|
-
|
|
125
|
-
Example:
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
I searched for skills related to "xyz" but didn't find any matches.
|
|
129
|
-
I can still help you with this task directly! Would you like me to proceed?
|
|
130
|
-
|
|
131
|
-
If this is something you do often, you could create your own skill:
|
|
132
|
-
npx skills init my-xyz-skill
|
|
133
|
-
```
|
|
1
|
+
---
|
|
2
|
+
name: find-skills
|
|
3
|
+
description: Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Find Skills
|
|
7
|
+
|
|
8
|
+
This skill helps you discover and install skills from the open agent skills ecosystem.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
Use this skill when the user:
|
|
13
|
+
|
|
14
|
+
- Asks "how do I do X" where X might be a common task with an existing skill
|
|
15
|
+
- Says "find a skill for X" or "is there a skill for X"
|
|
16
|
+
- Asks "can you do X" where X is a specialized capability
|
|
17
|
+
- Expresses interest in extending agent capabilities
|
|
18
|
+
- Wants to search for tools, templates, or workflows
|
|
19
|
+
- Mentions they wish they had help with a specific domain (design, testing, deployment, etc.)
|
|
20
|
+
|
|
21
|
+
## What is the Skills CLI?
|
|
22
|
+
|
|
23
|
+
The Skills CLI (`npx skills`) is the package manager for the open agent skills ecosystem. Skills are modular packages that extend agent capabilities with specialized knowledge, workflows, and tools.
|
|
24
|
+
|
|
25
|
+
**Key commands:**
|
|
26
|
+
|
|
27
|
+
- `npx skills find [query]` - Search for skills interactively or by keyword
|
|
28
|
+
- `npx skills add <package>` - Install a skill from GitHub or other sources
|
|
29
|
+
- `npx skills check` - Check for skill updates
|
|
30
|
+
- `npx skills update` - Update all installed skills
|
|
31
|
+
|
|
32
|
+
**Browse skills at:** https://skills.sh/
|
|
33
|
+
|
|
34
|
+
## How to Help Users Find Skills
|
|
35
|
+
|
|
36
|
+
### Step 1: Understand What They Need
|
|
37
|
+
|
|
38
|
+
When a user asks for help with something, identify:
|
|
39
|
+
|
|
40
|
+
1. The domain (e.g., React, testing, design, deployment)
|
|
41
|
+
2. The specific task (e.g., writing tests, creating animations, reviewing PRs)
|
|
42
|
+
3. Whether this is a common enough task that a skill likely exists
|
|
43
|
+
|
|
44
|
+
### Step 2: Search for Skills
|
|
45
|
+
|
|
46
|
+
Run the find command with a relevant query:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx skills find [query]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For example:
|
|
53
|
+
|
|
54
|
+
- User asks "how do I make my React app faster?" → `npx skills find react performance`
|
|
55
|
+
- User asks "can you help me with PR reviews?" → `npx skills find pr review`
|
|
56
|
+
- User asks "I need to create a changelog" → `npx skills find changelog`
|
|
57
|
+
|
|
58
|
+
The command will return results like:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Install with npx skills add <owner/repo@skill>
|
|
62
|
+
|
|
63
|
+
vercel-labs/agent-skills@vercel-react-best-practices
|
|
64
|
+
└ https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Step 3: Present Options to the User
|
|
68
|
+
|
|
69
|
+
When you find relevant skills, present them to the user with:
|
|
70
|
+
|
|
71
|
+
1. The skill name and what it does
|
|
72
|
+
2. The install command they can run
|
|
73
|
+
3. A link to learn more at skills.sh
|
|
74
|
+
|
|
75
|
+
Example response:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
I found a skill that might help! The "vercel-react-best-practices" skill provides
|
|
79
|
+
React and Next.js performance optimization guidelines from Vercel Engineering.
|
|
80
|
+
|
|
81
|
+
To install it:
|
|
82
|
+
npx skills add vercel-labs/agent-skills@vercel-react-best-practices
|
|
83
|
+
|
|
84
|
+
Learn more: https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Step 4: Offer to Install
|
|
88
|
+
|
|
89
|
+
If the user wants to proceed, you can install the skill for them:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx skills add <owner/repo@skill> -g -y
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The `-g` flag installs globally (user-level) and `-y` skips confirmation prompts.
|
|
96
|
+
|
|
97
|
+
## Common Skill Categories
|
|
98
|
+
|
|
99
|
+
When searching, consider these common categories:
|
|
100
|
+
|
|
101
|
+
| Category | Example Queries |
|
|
102
|
+
| --------------- | ---------------------------------------- |
|
|
103
|
+
| Web Development | react, nextjs, typescript, css, tailwind |
|
|
104
|
+
| Testing | testing, jest, playwright, e2e |
|
|
105
|
+
| DevOps | deploy, docker, kubernetes, ci-cd |
|
|
106
|
+
| Documentation | docs, readme, changelog, api-docs |
|
|
107
|
+
| Code Quality | review, lint, refactor, best-practices |
|
|
108
|
+
| Design | ui, ux, design-system, accessibility |
|
|
109
|
+
| Productivity | workflow, automation, git |
|
|
110
|
+
|
|
111
|
+
## Tips for Effective Searches
|
|
112
|
+
|
|
113
|
+
1. **Use specific keywords**: "react testing" is better than just "testing"
|
|
114
|
+
2. **Try alternative terms**: If "deploy" doesn't work, try "deployment" or "ci-cd"
|
|
115
|
+
3. **Check popular sources**: Many skills come from `vercel-labs/agent-skills` or `ComposioHQ/awesome-claude-skills`
|
|
116
|
+
|
|
117
|
+
## When No Skills Are Found
|
|
118
|
+
|
|
119
|
+
If no relevant skills exist:
|
|
120
|
+
|
|
121
|
+
1. Acknowledge that no existing skill was found
|
|
122
|
+
2. Offer to help with the task directly using your general capabilities
|
|
123
|
+
3. Suggest the user could create their own skill with `npx skills init`
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
I searched for skills related to "xyz" but didn't find any matches.
|
|
129
|
+
I can still help you with this task directly! Would you like me to proceed?
|
|
130
|
+
|
|
131
|
+
If this is something you do often, you could create your own skill:
|
|
132
|
+
npx skills init my-xyz-skill
|
|
133
|
+
```
|
package/src/agent/chat.js
CHANGED
|
@@ -674,9 +674,16 @@ class AgentChatHandler extends EventEmitter {
|
|
|
674
674
|
async *chatStream(message, options = {}) {
|
|
675
675
|
const sessionId = options.sessionId || this.getDefaultSessionId();
|
|
676
676
|
const framework = this.agent.framework;
|
|
677
|
+
// ★ 提到外层作用域,catch 中要回滚 userMessage 时需要访问
|
|
678
|
+
let messageStore = null;
|
|
679
|
+
let messages = null;
|
|
680
|
+
let userMessage = null;
|
|
677
681
|
|
|
678
682
|
try {
|
|
679
|
-
const
|
|
683
|
+
const prepared = await this._prepareSession(message, sessionId);
|
|
684
|
+
messageStore = prepared.messageStore;
|
|
685
|
+
messages = prepared.messages;
|
|
686
|
+
userMessage = prepared.userMessage;
|
|
680
687
|
|
|
681
688
|
if (!this._aiClient) {
|
|
682
689
|
throw new Error('AI 客户端未配置');
|
|
@@ -806,7 +813,43 @@ class AgentChatHandler extends EventEmitter {
|
|
|
806
813
|
|
|
807
814
|
const userMsg = messages[messages.length - finishMessages.length - 1];
|
|
808
815
|
this.emit('message', { content: fullText, sessionId: sessionId, userMessage: userMsg });
|
|
816
|
+
|
|
817
|
+
// ★ 修复:成功路径才持久化。AI 失败时若 finally 也 save,
|
|
818
|
+
// 会把刚 push 的 userMessage 写盘 → 下次请求 loadHistory 再次
|
|
819
|
+
// load 到,_prepareSession 再 push 一份 → 连续两条相同 user
|
|
820
|
+
// → AI SDK 抛 "Invalid prompt: ModelMessage[] schema"
|
|
821
|
+
try {
|
|
822
|
+
await messageStore.save();
|
|
823
|
+
} catch (err) {
|
|
824
|
+
logger.error(`[${sessionId}] Failed to save message store: ${err.message}`);
|
|
825
|
+
}
|
|
809
826
|
} catch (err) {
|
|
827
|
+
// ★ 修复:失败时打印 InvalidPromptError 的 cause(含 Zod issue 路径),
|
|
828
|
+
// 方便定位是哪条消息、哪个字段不匹配 ModelMessage[] schema
|
|
829
|
+
logger.error('[AgentChat] chatStream error:', err.message);
|
|
830
|
+
if (err.cause) {
|
|
831
|
+
const cause = err.cause;
|
|
832
|
+
if (cause.issues) {
|
|
833
|
+
for (const issue of cause.issues.slice(0, 5)) {
|
|
834
|
+
logger.error(
|
|
835
|
+
`[AgentChat] schema issue: path=${JSON.stringify(issue.path)} code=${issue.code} message=${issue.message}`
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
} else {
|
|
839
|
+
logger.error(`[AgentChat] error cause: ${cause.message || String(cause)}`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// ★ 修复:失败时 ROLLBACK 掉本轮 push 的 userMessage
|
|
844
|
+
try {
|
|
845
|
+
if (messages && userMessage) {
|
|
846
|
+
const last = messages[messages.length - 1];
|
|
847
|
+
if (last && last.role === 'user' && last === userMessage) {
|
|
848
|
+
messages.pop();
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
} catch { /* ignore */ }
|
|
852
|
+
|
|
810
853
|
// 统一错误处理:提取简洁消息并通过 yield 传递
|
|
811
854
|
const friendlyMessage = err?.message?.split('\n')[0] || '未知错误';
|
|
812
855
|
|
|
@@ -815,11 +858,6 @@ class AgentChatHandler extends EventEmitter {
|
|
|
815
858
|
this.emit('message:error', { sessionId, error: friendlyMessage, originalError: err });
|
|
816
859
|
}
|
|
817
860
|
yield { type: 'error', error: friendlyMessage };
|
|
818
|
-
} finally {
|
|
819
|
-
const messageStore = this._getSessionMessageStore(sessionId);
|
|
820
|
-
messageStore.save().catch((err) => {
|
|
821
|
-
logger.error(`[${sessionId}] Failed to save message store: ${err.message}`);
|
|
822
|
-
});
|
|
823
861
|
}
|
|
824
862
|
}
|
|
825
863
|
|
|
@@ -869,9 +907,16 @@ class AgentChatHandler extends EventEmitter {
|
|
|
869
907
|
*/
|
|
870
908
|
async _processMessage(sessionId, message, options) {
|
|
871
909
|
const framework = this.agent.framework;
|
|
910
|
+
// ★ 提到外层作用域,catch 中要回滚 userMessage 时需要访问
|
|
911
|
+
let messageStore = null;
|
|
912
|
+
let messages = null;
|
|
913
|
+
let userMessage = null;
|
|
872
914
|
|
|
873
915
|
try {
|
|
874
|
-
const
|
|
916
|
+
const prepared = await this._prepareSession(message, sessionId);
|
|
917
|
+
messageStore = prepared.messageStore;
|
|
918
|
+
messages = prepared.messages;
|
|
919
|
+
userMessage = prepared.userMessage;
|
|
875
920
|
|
|
876
921
|
if (!this._aiClient) {
|
|
877
922
|
throw new Error('AI 客户端未配置');
|
|
@@ -927,28 +972,57 @@ class AgentChatHandler extends EventEmitter {
|
|
|
927
972
|
const userMsg = messages[messages.length - result.response.messages.length - 1];
|
|
928
973
|
this.emit('message', { content: result.text, sessionId: sessionId, userMessage: userMsg });
|
|
929
974
|
|
|
975
|
+
// ★ 修复:AI 调用成功后才持久化(之前 finally 总是 save,
|
|
976
|
+
// 会把刚 push 的 userMessage 写盘;下次请求 loadHistory 再次 load 到,
|
|
977
|
+
// _prepareSession 又 push 一份 → 连续两条相同 user 消息 →
|
|
978
|
+
// AI SDK 抛 "Invalid prompt: ModelMessage[] schema")
|
|
979
|
+
try {
|
|
980
|
+
await messageStore.save();
|
|
981
|
+
} catch (err) {
|
|
982
|
+
logger.error(`[${sessionId}] Failed to save message store: ${err.message}`);
|
|
983
|
+
}
|
|
984
|
+
|
|
930
985
|
return {
|
|
931
986
|
success: true,
|
|
932
987
|
message: cleanResponse(result.text || ''),
|
|
933
988
|
stepCount: result.stepCount || 1,
|
|
934
989
|
};
|
|
935
990
|
} catch (err) {
|
|
991
|
+
// ★ 修复:失败时打印 InvalidPromptError 的 cause(含 Zod issue 路径),
|
|
992
|
+
// 方便定位是哪条消息、哪个字段不匹配 ModelMessage[] schema
|
|
936
993
|
logger.error('[AgentChat] _processMessage error:', err.message);
|
|
994
|
+
if (err.cause) {
|
|
995
|
+
const cause = err.cause;
|
|
996
|
+
if (cause.issues) {
|
|
997
|
+
// ZodError:逐条打印
|
|
998
|
+
for (const issue of cause.issues.slice(0, 5)) {
|
|
999
|
+
logger.error(
|
|
1000
|
+
`[AgentChat] schema issue: path=${JSON.stringify(issue.path)} code=${issue.code} message=${issue.message}`
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
} else {
|
|
1004
|
+
logger.error(`[AgentChat] error cause: ${cause.message || String(cause)}`);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
937
1007
|
const errorMsg = err.message || String(err);
|
|
938
1008
|
|
|
1009
|
+
// ★ 修复:失败时 ROLLBACK 掉本轮 push 的 userMessage,
|
|
1010
|
+
// 避免下次请求把它从 jsonl load 出来再 push 一遍
|
|
1011
|
+
try {
|
|
1012
|
+
if (messages && userMessage) {
|
|
1013
|
+
const last = messages[messages.length - 1];
|
|
1014
|
+
if (last && last.role === 'user' && last === userMessage) {
|
|
1015
|
+
messages.pop();
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
} catch { /* ignore */ }
|
|
1019
|
+
|
|
939
1020
|
return {
|
|
940
1021
|
success: false,
|
|
941
1022
|
message: 'AI 服务暂时不可用,请稍后重试。',
|
|
942
1023
|
error: errorMsg,
|
|
943
1024
|
stepCount: 0,
|
|
944
1025
|
};
|
|
945
|
-
} finally {
|
|
946
|
-
const messageStore = this._getSessionMessageStore(sessionId);
|
|
947
|
-
try {
|
|
948
|
-
await messageStore.save();
|
|
949
|
-
} catch (err) {
|
|
950
|
-
logger.error(`[${sessionId}] Failed to save message store: ${err.message}`);
|
|
951
|
-
}
|
|
952
1026
|
}
|
|
953
1027
|
}
|
|
954
1028
|
|
|
@@ -1006,9 +1080,44 @@ class AgentChatHandler extends EventEmitter {
|
|
|
1006
1080
|
messages.length = 0;
|
|
1007
1081
|
messages.push(...validated);
|
|
1008
1082
|
}
|
|
1083
|
+
|
|
1084
|
+
// ★ 修复:去重末尾重复的 user 消息。
|
|
1085
|
+
// 场景:AI 失败时上一轮已把 userMessage 持久化到 jsonl(或留在 messageStore 内存),
|
|
1086
|
+
// 本次 _prepareSession 再次 loadHistory 又会拿到它;
|
|
1087
|
+
// 如果再 push 一次,消息数组末尾出现两条相同 user,
|
|
1088
|
+
// AI SDK 抛 "Invalid prompt: ModelMessage[] schema"。
|
|
1089
|
+
// 仅对"末尾连续 user"做去重;中间重复历史由 validateAll 处理。
|
|
1090
|
+
if (messages.length > 0) {
|
|
1091
|
+
const last = messages[messages.length - 1];
|
|
1092
|
+
if (
|
|
1093
|
+
last &&
|
|
1094
|
+
last.role === 'user' &&
|
|
1095
|
+
userMessage &&
|
|
1096
|
+
userMessage.role === 'user' &&
|
|
1097
|
+
this._isSameUserContent(last.content, userMessage.content)
|
|
1098
|
+
) {
|
|
1099
|
+
logger.debug(`[_prepareSession] 去重末尾 user 消息(避免连续两条相同 user)`);
|
|
1100
|
+
messages.pop();
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1009
1103
|
messages.push(userMessage);
|
|
1010
1104
|
|
|
1011
|
-
|
|
1105
|
+
// ★ 返回 userMessage 引用,调用方(_processMessage)失败时需 rollback
|
|
1106
|
+
return { messageStore, messages, userMessage };
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* 判断两个 user content 是否等价(用于末尾 user 去重)
|
|
1111
|
+
* @private
|
|
1112
|
+
*/
|
|
1113
|
+
_isSameUserContent(a, b) {
|
|
1114
|
+
if (a === b) return true;
|
|
1115
|
+
if (typeof a === 'string' && typeof b === 'string') return a === b;
|
|
1116
|
+
try {
|
|
1117
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
1118
|
+
} catch {
|
|
1119
|
+
return false;
|
|
1120
|
+
}
|
|
1012
1121
|
}
|
|
1013
1122
|
|
|
1014
1123
|
/**
|
package/src/session/session.js
CHANGED
|
@@ -54,7 +54,7 @@ class SessionManager {
|
|
|
54
54
|
this.labelTimestampsById = new Map();
|
|
55
55
|
this.leafId = null;
|
|
56
56
|
this.sessionId = '';
|
|
57
|
-
|
|
57
|
+
this.fileName=''
|
|
58
58
|
if (persist && sessionDir && !fs.existsSync(sessionDir)) {
|
|
59
59
|
fs.mkdirSync(sessionDir, { recursive: true });
|
|
60
60
|
}
|
|
@@ -84,6 +84,7 @@ class SessionManager {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
const header = this.fileEntries.find(e => e.type === 'session');
|
|
87
|
+
this.fileName = path.basename(sessionFile);
|
|
87
88
|
this.sessionId = header?.id || createSessionId();
|
|
88
89
|
|
|
89
90
|
this._buildIndex();
|