foliko 1.0.77 → 1.0.78

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 (36) hide show
  1. package/.agent/data/default.json +31559 -0
  2. package/.agent/data/plugins-state.json +10 -1
  3. package/.claude/settings.local.json +13 -2
  4. package/.env.example +54 -54
  5. package/cli/src/commands/chat.js +1 -1
  6. package/examples/basic.js +1 -1
  7. package/package.json +5 -3
  8. package/plugins/ai-plugin.js +1 -1
  9. package/plugins/ambient-agent/index.js +1 -1
  10. package/plugins/audit-plugin.js +1 -1
  11. package/plugins/default-plugins.js +92 -209
  12. package/plugins/email/index.js +1 -1
  13. package/plugins/extension-executor-plugin.js +326 -0
  14. package/plugins/feishu-plugin.js +1 -1
  15. package/plugins/file-system-plugin.js +57 -6
  16. package/plugins/gate-trading.js +747 -0
  17. package/plugins/install-plugin.js +1 -1
  18. package/plugins/python-executor-plugin.js +1 -1
  19. package/plugins/python-plugin-loader.js +275 -105
  20. package/plugins/rules-plugin.js +1 -1
  21. package/plugins/scheduler-plugin.js +1 -1
  22. package/plugins/session-plugin.js +132 -7
  23. package/plugins/shell-executor-plugin.js +1 -1
  24. package/plugins/storage-plugin.js +24 -1
  25. package/plugins/subagent-plugin.js +2 -2
  26. package/plugins/telegram-plugin.js +1 -1
  27. package/plugins/think-plugin.js +10 -10
  28. package/plugins/tools-plugin.js +1 -1
  29. package/plugins/web-plugin.js +49 -18
  30. package/plugins/weixin-plugin.js +1 -1
  31. package/skills/foliko-dev/SKILL.md +583 -500
  32. package/skills/python-plugin-dev/SKILL.md +238 -266
  33. package/src/core/agent-chat.js +103 -4
  34. package/src/core/agent.js +85 -19
  35. package/src/core/plugin-base.js +43 -0
  36. package/src/executors/mcp-executor.js +126 -22
@@ -1,266 +1,238 @@
1
- ---
2
- name: python-plugin-dev
3
- description: Python 插件开发指南。当用户说"创建 Python 插件"、"用 Python 写插件"时立即调用此 skill。
4
- allowed-tools: Read, Write, Edit, Glob, Grep, Bash, python_plugin
5
- ---
6
-
7
- # Python 插件开发
8
-
9
- ## 概述
10
-
11
- Python 插件允许用 Python 语言开发工具,插件代码运行在 Python 环境中,可以调用所有 Python 库。
12
-
13
- ## 插件存放位置
14
-
15
- `.agent/plugins/*.py`
16
-
17
- ## 插件结构
18
-
19
- ```python
20
- # .agent/plugins/my-plugin.py
21
-
22
- plugin_info = {
23
- "name": "my-plugin",
24
- "version": "1.0.0",
25
- "description": "我的 Python 插件",
26
- "tools": [
27
- {
28
- "name": "hello",
29
- "description": "问候工具",
30
- "params": {"name": "string"}
31
- },
32
- {
33
- "name": "calc",
34
- "description": "计算器工具",
35
- "params": {"a": "number", "b": "number", "op": "string"}
36
- }
37
- ]
38
- }
39
-
40
- def execute_tool(tool_name, params):
41
- """
42
- 执行工具 - 框架调用入口
43
- tool_name: 工具名称
44
- params: 参数字典
45
- """
46
- if tool_name == "hello":
47
- return hello_tool(params)
48
- elif tool_name == "calc":
49
- return calc_tool(params)
50
- else:
51
- return {"success": False, "error": f"Unknown tool: {tool_name}"}
52
-
53
- # === 工具实现 ===
54
-
55
- def hello_tool(params):
56
- """简单问候工具"""
57
- name = params.get("name", "World")
58
- return {
59
- "success": True,
60
- "result": f"Hello, {name}!"
61
- }
62
-
63
- def calc_tool(params):
64
- """计算器工具"""
65
- a = params.get("a", 0)
66
- b = params.get("b", 0)
67
- op = params.get("op", "add")
68
-
69
- if op == "add":
70
- result = a + b
71
- elif op == "sub":
72
- result = a - b
73
- elif op == "mul":
74
- result = a * b
75
- elif op == "div":
76
- if b == 0:
77
- return {"success": False, "error": "Division by zero"}
78
- result = a / b
79
- else:
80
- return {"success": False, "error": f"Unknown operation: {op}"}
81
-
82
- return {"success": True, "result": result}
83
- ```
84
-
85
- **重要**:`tools` 字段定义了插件提供的所有工具。框架会自动:
86
-
87
- 1. 将这些工具注册到系统中
88
- 2. 附加到系统提示词,LLM 可以直接调用
89
-
90
- ## 工具函数规范
91
-
92
- | 规范 | 说明 |
93
- | ------------ | -------------------- |
94
- | 返回 dict | 必须是 Python 字典 |
95
- | success 字段 | `true` `false` |
96
- | result 字段 | 成功时返回的结果 |
97
- | error 字段 | 失败时返回的错误信息 |
98
-
99
- ## 返回值格式
100
-
101
- ```python
102
- # 成功
103
- {"success": True, "result": "返回值"}
104
-
105
- # 失败
106
- {"success": False, "error": "错误信息"}
107
-
108
- # 返回复杂数据
109
- {"success": True, "result": {"key": "value", "list": [1, 2, 3]}}
110
- ```
111
-
112
- ## 使用方式
113
-
114
- 创建插件后,使用 `python_plugin` 工具调用:
115
-
116
- ```python
117
- python_plugin({
118
- plugin: "my-plugin", # 插件名称(不含 .py)
119
- tool: "hello", # 工具名称
120
- params: { name: "Foliko" } # 工具参数
121
- })
122
- ```
123
-
124
- ## 示例:天气查询插件
125
-
126
- ```python
127
- # .agent/plugins/weather.py
128
-
129
- plugin_info = {
130
- "name": "weather",
131
- "version": "1.0.0",
132
- "description": "天气查询插件"
133
- }
134
-
135
- def execute_tool(tool_name, params):
136
- if tool_name == "get_weather":
137
- return get_weather(params)
138
- else:
139
- return {"success": False, "error": f"Unknown tool: {tool_name}"}
140
-
141
- def get_weather(params):
142
- import requests
143
-
144
- city = params.get("city", "北京")
145
-
146
- try:
147
- # 这里使用免费的天气 API
148
- response = requests.get(
149
- f"https://wttr.in/{city}?format=j1",
150
- timeout=5
151
- )
152
- data = response.json()
153
-
154
- return {
155
- "success": True,
156
- "result": {
157
- "city": city,
158
- "temperature": data["current_condition"][0]["temp_C"],
159
- "weather": data["current_condition"][0]["weatherDesc"][0]["value"]
160
- }
161
- }
162
- except Exception as e:
163
- return {"success": False, "error": str(e)}
164
- ```
165
-
166
- ## 示例:文件处理插件
167
-
168
- ```python
169
- # .agent/plugins/file_processor.py
170
-
171
- plugin_info = {
172
- "name": "file_processor",
173
- "version": "1.0.0",
174
- "description": "文件处理插件"
175
- }
176
-
177
- def execute_tool(tool_name, params):
178
- if tool_name == "read_file":
179
- return read_file_tool(params)
180
- elif tool_name == "write_file":
181
- return write_file_tool(params)
182
- else:
183
- return {"success": False, "error": f"Unknown tool: {tool_name}"}
184
-
185
- def read_file_tool(params):
186
- import os
187
-
188
- filepath = params.get("path")
189
- if not filepath:
190
- return {"success": False, "error": "path is required"}
191
-
192
- try:
193
- with open(filepath, "r", encoding="utf-8") as f:
194
- content = f.read()
195
- return {"success": True, "result": content}
196
- except Exception as e:
197
- return {"success": False, "error": str(e)}
198
-
199
- def write_file_tool(params):
200
- filepath = params.get("path")
201
- content = params.get("content", "")
202
-
203
- if not filepath:
204
- return {"success": False, "error": "path is required"}
205
-
206
- try:
207
- os.makedirs(os.path.dirname(filepath), exist_ok=True)
208
- with open(filepath, "w", encoding="utf-8") as f:
209
- f.write(content)
210
- return {"success": True, "result": f"Written to {filepath}"}
211
- except Exception as e:
212
- return {"success": False, "error": str(e)}
213
- ```
214
-
215
- ## 依赖管理
216
-
217
- 如果需要第三方库,可以在插件中用 `pip` 安装:
218
-
219
- ```python
220
- def execute_tool(tool_name, params):
221
- if tool_name == "install_package":
222
- return install_package(params)
223
- ...
224
-
225
- def install_package(params):
226
- import subprocess
227
- import sys
228
-
229
- package = params.get("package")
230
- if not package:
231
- return {"success": False, "error": "package name is required"}
232
-
233
- try:
234
- subprocess.check_call([sys.executable, "-m", "pip", "install", package])
235
- return {"success": True, "result": f"Installed {package}"}
236
- except Exception as e:
237
- return {"success": False, "error": str(e)}
238
- ```
239
-
240
- ## 注意事项
241
-
242
- 1. **不要创建目录** - 必须是单个 `.py` 文件
243
- 2. **必须定义 `execute_tool`** - 这是框架调用的入口
244
- 3. **必须定义 `plugin_info`** - 插件元信息
245
- 4. **异常处理** - 所有可能出错的地方都要 try-except
246
- 5. **编码** - 文件使用 UTF-8 编码
247
-
248
- ## 开发流程
249
-
250
- 1. **编写插件代码** - 在 `.agent/plugins/` 下创建 `.py` 文件
251
- 2. **调用工具** - 使用 `python_plugin` 工具执行
252
- 3. **调试** - 查看返回的 `success` 和 `error` 信息
253
-
254
- ## 常用 Python 库
255
-
256
- 插件中可以使用的常用库:
257
-
258
- | 库 | 用途 | 导入方式 |
259
- | -------- | ---------- | ------------------------------- |
260
- | requests | HTTP 请求 | `import requests` |
261
- | json | JSON 处理 | `import json` |
262
- | os | 系统操作 | `import os` |
263
- | pathlib | 路径处理 | `from pathlib import Path` |
264
- | datetime | 日期时间 | `from datetime import datetime` |
265
- | re | 正则表达式 | `import re` |
266
- | csv | CSV 处理 | `import csv` |
1
+ ---
2
+ name: python-plugin-dev
3
+ description: Python 插件开发指南。当用户说"创建 Python 插件"、"用 Python 写插件"时立即调用此 skill。
4
+ allowed-tools: Read, Write, Edit, Glob, Grep, Bash
5
+ ---
6
+
7
+ # Python 插件开发
8
+
9
+ ## 概述
10
+
11
+ Python 插件允许用 Python 语言开发工具,插件代码运行在 Python 环境中,可以调用所有 Python 库。
12
+
13
+ ## 插件存放位置
14
+
15
+ `.agent/plugins/*.py`
16
+
17
+ ## 插件结构
18
+
19
+ ```python
20
+ # .agent/plugins/my-plugin.py
21
+
22
+ PLUGIN = {
23
+ "name": "my-plugin",
24
+ "version": "1.0.0",
25
+ "description": "我的 Python 插件"
26
+ }
27
+
28
+ TOOLS = [
29
+ {
30
+ "name": "hello",
31
+ "description": "问候工具",
32
+ "params": {"name": "string"}
33
+ },
34
+ {
35
+ "name": "calc",
36
+ "description": "计算器工具",
37
+ "params": {"a": "number", "b": "number", "op": "string"}
38
+ }
39
+ ]
40
+
41
+ # === 工具实现 ===
42
+
43
+ def hello(params):
44
+ """问候工具"""
45
+ name = params.get("name", "World")
46
+ return {"success": True, "result": f"Hello, {name}!"}
47
+
48
+ def calc(params):
49
+ """计算器工具"""
50
+ a = params.get("a", 0)
51
+ b = params.get("b", 0)
52
+ op = params.get("op", "add")
53
+
54
+ if op == "add":
55
+ result = a + b
56
+ elif op == "sub":
57
+ result = a - b
58
+ elif op == "mul":
59
+ result = a * b
60
+ elif op == "div":
61
+ if b == 0:
62
+ return {"success": False, "error": "Division by zero"}
63
+ result = a / b
64
+ else:
65
+ return {"success": False, "error": f"Unknown operation: {op}"}
66
+
67
+ return {"success": True, "result": result}
68
+ ```
69
+
70
+ **重要**:`tools` 字段定义了插件提供的所有工具。框架会自动:
71
+
72
+ 1. 将这些工具注册到 ExtensionExecutor
73
+ 2. 附加到系统提示词【Extensions 扩展工具】部分
74
+ 3. 可通过 `ext_call` 调用
75
+
76
+ ## 工具函数规范
77
+
78
+ | 规范 | 说明 |
79
+ | ------------ | -------------------- |
80
+ | 返回 dict | 必须是 Python 字典 |
81
+ | success 字段 | `true` 或 `false` |
82
+ | result 字段 | 成功时返回的结果 |
83
+ | error 字段 | 失败时返回的错误信息 |
84
+
85
+ ## 返回值格式
86
+
87
+ ```python
88
+ # 成功
89
+ {"success": True, "result": "返回值"}
90
+
91
+ # 失败
92
+ {"success": False, "error": "错误信息"}
93
+
94
+ # 返回复杂数据
95
+ {"success": True, "result": {"key": "value", "list": [1, 2, 3]}}
96
+ ```
97
+
98
+ ## 使用方式
99
+
100
+ 通过 `ext_call` 调用:
101
+
102
+ ```javascript
103
+ ext_call({
104
+ plugin: 'my-plugin', // 插件名称
105
+ tool: 'hello', // 工具名称
106
+ args: { name: 'Foliko' }, // 工具参数
107
+ });
108
+ ```
109
+
110
+ 工具会自动出现在系统提示词的【Extensions 扩展工具】部分。
111
+
112
+ ## 示例:天气查询插件
113
+
114
+ ```python
115
+ # .agent/plugins/weather.py
116
+
117
+ PLUGIN = {
118
+ "name": "weather",
119
+ "version": "1.0.0",
120
+ "description": "天气查询插件"
121
+ }
122
+
123
+ TOOLS = [
124
+ {
125
+ "name": "get_weather",
126
+ "description": "查询天气",
127
+ "params": {"city": "string"}
128
+ }
129
+ ]
130
+
131
+ def get_weather(params):
132
+ import requests
133
+
134
+ city = params.get("city", "北京")
135
+
136
+ try:
137
+ response = requests.get(
138
+ f"https://wttr.in/{city}?format=j1",
139
+ timeout=5
140
+ )
141
+ data = response.json()
142
+
143
+ return {
144
+ "success": True,
145
+ "result": {
146
+ "city": city,
147
+ "temperature": data["current_condition"][0]["temp_C"],
148
+ "weather": data["current_condition"][0]["weatherDesc"][0]["value"]
149
+ }
150
+ }
151
+ except Exception as e:
152
+ return {"success": False, "error": str(e)}
153
+ ```
154
+
155
+ ## 示例:文件处理插件
156
+
157
+ ```python
158
+ # .agent/plugins/file_processor.py
159
+
160
+ PLUGIN = {
161
+ "name": "file_processor",
162
+ "version": "1.0.0",
163
+ "description": "文件处理插件"
164
+ }
165
+
166
+ TOOLS = [
167
+ {
168
+ "name": "read_file",
169
+ "description": "读取文件",
170
+ "params": {"path": "string"}
171
+ },
172
+ {
173
+ "name": "write_file",
174
+ "description": "写入文件",
175
+ "params": {"path": "string", "content": "string"}
176
+ }
177
+ ]
178
+
179
+ def read_file(params):
180
+ import os
181
+
182
+ filepath = params.get("path")
183
+ if not filepath:
184
+ return {"success": False, "error": "path is required"}
185
+
186
+ try:
187
+ with open(filepath, "r", encoding="utf-8") as f:
188
+ content = f.read()
189
+ return {"success": True, "result": content}
190
+ except Exception as e:
191
+ return {"success": False, "error": str(e)}
192
+
193
+ def write_file(params):
194
+ import os
195
+
196
+ filepath = params.get("path")
197
+ content = params.get("content", "")
198
+
199
+ if not filepath:
200
+ return {"success": False, "error": "path is required"}
201
+
202
+ try:
203
+ os.makedirs(os.path.dirname(filepath), exist_ok=True)
204
+ with open(filepath, "w", encoding="utf-8") as f:
205
+ f.write(content)
206
+ return {"success": True, "result": f"Written to {filepath}"}
207
+ except Exception as e:
208
+ return {"success": False, "error": str(e)}
209
+ ```
210
+
211
+ ## 注意事项
212
+
213
+ 1. **文件格式** - 必须是单个 `.py` 文件
214
+ 2. **必须定义 `PLUGIN`** - 插件元信息
215
+ 3. **必须定义 `TOOLS`** - 工具列表
216
+ 4. **工具函数名** - 必须与 TOOLS 中定义的 name 一致
217
+ 5. **异常处理** - 所有可能出错的地方都要 try-except
218
+ 6. **编码** - 文件使用 UTF-8 编码
219
+
220
+ ## 开发流程
221
+
222
+ 1. **编写插件代码** - 在 `.agent/plugins/` 下创建 `.py` 文件
223
+ 2. **自动加载** - 框架启动时自动注册工具
224
+ 3. **调试** - 查看返回的 `success` 和 `error` 信息
225
+
226
+ ## 常用 Python 库
227
+
228
+ 插件中可以使用的常用库:
229
+
230
+ | 库 | 用途 | 导入方式 |
231
+ | -------- | ---------- | ------------------------------- |
232
+ | requests | HTTP 请求 | `import requests` |
233
+ | json | JSON 处理 | `import json` |
234
+ | os | 系统操作 | `import os` |
235
+ | pathlib | 路径处理 | `from pathlib import Path` |
236
+ | datetime | 日期时间 | `from datetime import datetime` |
237
+ | re | 正则表达式 | `import re` |
238
+ | csv | CSV 处理 | `import csv` |
@@ -119,6 +119,59 @@ class AgentChatHandler extends EventEmitter {
119
119
  // 初始化编码器
120
120
  // 使用纯 JS tokenizer
121
121
  this._encoder = _globalTokenizer;
122
+
123
+ // Session 历史存储配置
124
+ this._sessionHistoryKey = config.sessionHistoryKey || 'chat_history';
125
+ }
126
+
127
+ /**
128
+ * 从 session 存储加载聊天历史
129
+ * @param {string} sessionId - 会话 ID
130
+ * @returns {Array} 消息数组
131
+ * @private
132
+ */
133
+ _loadHistoryFromSession(sessionId) {
134
+ if (!sessionId || !this.agent?.framework) return [];
135
+
136
+ try {
137
+ const sessionPlugin = this.agent.framework.pluginManager.get('session');
138
+ if (!sessionPlugin) return [];
139
+
140
+ const messages = sessionPlugin.getHistory(sessionId);
141
+ return messages || [];
142
+ } catch (err) {
143
+ // 忽略加载错误
144
+ }
145
+ return [];
146
+ }
147
+
148
+ /**
149
+ * 保存聊天历史到 session 存储
150
+ * @param {string} sessionId - 会话 ID
151
+ * @param {Array} messages - 消息数组
152
+ * @private
153
+ */
154
+ _saveHistoryToSession(sessionId, messages) {
155
+ if (!sessionId || !this.agent?.framework || !messages || messages.length === 0) return;
156
+
157
+ try {
158
+ const sessionPlugin = this.agent.framework.pluginManager.get('session');
159
+ if (!sessionPlugin) return;
160
+
161
+ // 获取 session 中已有消息数量,避免重复添加
162
+ const existingHistory = sessionPlugin.getHistory(sessionId);
163
+ const existingCount = existingHistory.length;
164
+
165
+ // 只添加新消息(避免重复添加已加载的历史)
166
+ const simpleMessages = messages.filter((m) => m.role === 'user' || m.role === 'assistant');
167
+
168
+ const newMessages = simpleMessages.slice(existingCount);
169
+ for (const msg of newMessages) {
170
+ sessionPlugin.addMessage(sessionId, msg);
171
+ }
172
+ } catch (err) {
173
+ // 忽略保存错误
174
+ }
122
175
  }
123
176
 
124
177
  /**
@@ -529,10 +582,26 @@ ${truncatedContent}${truncatedNote}
529
582
 
530
583
  /**
531
584
  * 清空对话历史
585
+ * @param {string} sessionId - 可选,指定 session 则同时清除 session 中的历史
532
586
  */
533
- clearHistory() {
587
+ clearHistory(sessionId) {
534
588
  this._messages = [];
535
589
  this._compressionCount = 0;
590
+
591
+ // 同时清除 session 中的历史
592
+ if (sessionId && this.agent?.framework) {
593
+ try {
594
+ const sessionPlugin = this.agent.framework.pluginManager.get('session');
595
+ if (sessionPlugin) {
596
+ const session = sessionPlugin.getSession(sessionId);
597
+ if (session) {
598
+ session.messages = [];
599
+ }
600
+ }
601
+ } catch (err) {
602
+ // 忽略错误
603
+ }
604
+ }
536
605
  return this;
537
606
  }
538
607
 
@@ -566,10 +635,20 @@ ${truncatedContent}${truncatedNote}
566
635
  * @param {Object} options - 选项
567
636
  */
568
637
  async chat(message, options = {}) {
569
- const context = { sessionId: options.sessionId || null, isStream: false };
638
+ const sessionId = options.sessionId || null;
639
+ const context = { sessionId, isStream: false };
570
640
  const framework = this.agent.framework;
571
641
  const self = this; // 保存引用用于回调
572
642
 
643
+ // 从 session 加载聊天历史
644
+ if (sessionId) {
645
+ const savedMessages = this._loadHistoryFromSession(sessionId);
646
+ if (savedMessages.length > 0) {
647
+ this._messages = savedMessages;
648
+ logger.info(`Loaded ${savedMessages.length} messages from session ${sessionId}`);
649
+ }
650
+ }
651
+
573
652
  // 关键:每次 chat 调用时刷新系统提示词,确保包含最新的工具/技能描述
574
653
  // 解决上下文过长时 LLM 不调用工具的问题
575
654
  this._systemPrompt = this.agent._buildSystemPrompt();
@@ -641,6 +720,11 @@ ${truncatedContent}${truncatedNote}
641
720
  logger.info(`After generation: ${afterTokens} tokens, will compress on next turn`);
642
721
  }
643
722
 
723
+ // 保存聊天历史到 session
724
+ if (sessionId) {
725
+ this._saveHistoryToSession(sessionId, this._messages);
726
+ }
727
+
644
728
  return {
645
729
  success: true,
646
730
  message: result.text || '',
@@ -659,9 +743,19 @@ ${truncatedContent}${truncatedNote}
659
743
  * @param {Object} options - 选项
660
744
  */
661
745
  async *chatStream(message, options = {}) {
662
- const context = { sessionId: options.sessionId || null, isStream: true };
746
+ const sessionId = options.sessionId || null;
747
+ const context = { sessionId, isStream: true };
663
748
  const framework = this.agent.framework;
664
749
 
750
+ // 从 session 加载聊天历史
751
+ if (sessionId) {
752
+ const savedMessages = this._loadHistoryFromSession(sessionId);
753
+ if (savedMessages.length > 0) {
754
+ this._messages = savedMessages;
755
+ logger.info(`Loaded ${savedMessages.length} messages from session ${sessionId}`);
756
+ }
757
+ }
758
+
665
759
  // 关键:每次 chat 调用时刷新系统提示词,确保包含最新的工具/技能描述
666
760
  // 解决上下文过长时 LLM 不调用工具的问题
667
761
  this._systemPrompt = this.agent._buildSystemPrompt();
@@ -669,7 +763,7 @@ ${truncatedContent}${truncatedNote}
669
763
  const { tool, ToolLoopAgent } = await this._importAI();
670
764
 
671
765
  const userMessage = typeof message === 'string' ? { role: 'user', content: message } : message;
672
- // console.log('System Prompt:', this._systemPrompt);
766
+ //console.log('System Prompt:', this._systemPrompt);
673
767
  this._messages.push(userMessage);
674
768
  // 检查是否需要压缩上下文(包括工具定义)
675
769
  const messagesTokens = this._countMessagesTokens(this._messages);
@@ -746,6 +840,11 @@ ${truncatedContent}${truncatedNote}
746
840
  // 添加 assistant 消息
747
841
  const assistantMsg = { role: 'assistant', content: fullText };
748
842
  this._messages.push(assistantMsg);
843
+
844
+ // 保存聊天历史到 session
845
+ if (sessionId) {
846
+ this._saveHistoryToSession(sessionId, this._messages);
847
+ }
749
848
  } catch (err) {
750
849
  this.emit('error', { error: err.message });
751
850
  yield { type: 'error', error: err.message };