myagent-ai 1.0.0 → 1.2.0

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 (78) hide show
  1. package/README.md +41 -22
  2. package/agents/__pycache__/main_agent.cpython-312.pyc +0 -0
  3. package/agents/base.py +57 -0
  4. package/agents/main_agent.py +153 -44
  5. package/agents/memory_agent.py +25 -5
  6. package/communication/manager.py +20 -0
  7. package/core/__init__.py +2 -8
  8. package/core/config_broadcast.py +48 -17
  9. package/core/config_validator.py +740 -0
  10. package/core/context_manager.py +506 -0
  11. package/core/llm.py +48 -4
  12. package/core/permissions.py +372 -0
  13. package/core/task_persistence.py +359 -0
  14. package/core/task_queue.py +78 -0
  15. package/core/update_manager.py +895 -0
  16. package/core/version.py +49 -0
  17. package/departments/__init__.py +4 -0
  18. package/departments/__pycache__/__init__.cpython-312.pyc +0 -0
  19. package/departments/__pycache__/manager.cpython-312.pyc +0 -0
  20. package/departments/manager.py +954 -0
  21. package/executor/engine.py +172 -26
  22. package/install/install.ps1 +221 -0
  23. package/install/install.sh +308 -0
  24. package/main.py +386 -82
  25. package/organization/__pycache__/manager.cpython-312.pyc +0 -0
  26. package/organization/manager.py +20 -4
  27. package/package.json +12 -4
  28. package/requirements.txt +21 -8
  29. package/setup.py +14 -3
  30. package/skills/base.py +2 -0
  31. package/skills/registry.py +80 -17
  32. package/skills/system_skill.py +10 -1
  33. package/start.sh +161 -25
  34. package/web/__pycache__/api_server.cpython-312.pyc +0 -0
  35. package/web/api_server.py +1439 -45
  36. package/web/tts_handler.py +196 -0
  37. package/web/ui/chat.html +2131 -34
  38. package/web/ui/index.html +537 -221
  39. package/Dockerfile +0 -30
  40. package/skills/LLM/LICENSE.txt +0 -21
  41. package/skills/LLM/SKILL.md +0 -856
  42. package/skills/LLM/scripts/chat.ts +0 -32
  43. package/skills/agent-browser/SKILL.md +0 -328
  44. package/skills/auto-target-tracker/SKILL.md +0 -317
  45. package/skills/content-strategy/SKILL.md +0 -181
  46. package/skills/content-strategy/_meta.json +0 -6
  47. package/skills/dream-interpreter/SKILL.md +0 -88
  48. package/skills/dream-interpreter/assets/example_asset.txt +0 -24
  49. package/skills/dream-interpreter/references/api_reference.md +0 -34
  50. package/skills/dream-interpreter/references/interpretation-guide.md +0 -83
  51. package/skills/dream-interpreter/references/output-schema.md +0 -65
  52. package/skills/dream-interpreter/references/questioning-strategy.md +0 -62
  53. package/skills/dream-interpreter/references/visual-mapping.md +0 -81
  54. package/skills/dream-interpreter/scripts/__pycache__/example.cpython-312.pyc +0 -0
  55. package/skills/dream-interpreter/scripts/example.py +0 -19
  56. package/skills/dream-interpreter/skill.json +0 -7
  57. package/skills/fullstack-dev/SKILL.md +0 -205
  58. package/skills/get-fortune-analysis/SKILL.md +0 -370
  59. package/skills/get-fortune-analysis/lunar_python.py +0 -91
  60. package/skills/gift-evaluator/SKILL.md +0 -83
  61. package/skills/gift-evaluator/__pycache__/html_tools.cpython-312.pyc +0 -0
  62. package/skills/gift-evaluator/html_tools.py +0 -268
  63. package/skills/mindfulness-meditation/SKILL.md +0 -65
  64. package/skills/mindfulness-meditation/_meta.json +0 -6
  65. package/skills/seo-content-writer/SKILL.md +0 -661
  66. package/skills/seo-content-writer/_meta.json +0 -6
  67. package/skills/seo-content-writer/references/content-structure-templates.md +0 -875
  68. package/skills/seo-content-writer/references/title-formulas.md +0 -339
  69. package/skills/skill-finder-cn/SKILL.md +0 -66
  70. package/skills/skill-finder-cn/_meta.json +0 -6
  71. package/skills/skill-finder-cn/package.json +0 -5
  72. package/skills/skill-finder-cn/scripts/search.sh +0 -15
  73. package/skills/web-reader/LICENSE.txt +0 -21
  74. package/skills/web-reader/SKILL.md +0 -1140
  75. package/skills/web-reader/scripts/web-reader.ts +0 -37
  76. package/skills/web-search/LICENSE.txt +0 -21
  77. package/skills/web-search/SKILL.md +0 -912
  78. package/skills/web-search/scripts/web_search.ts +0 -44
package/README.md CHANGED
@@ -105,26 +105,45 @@ myagent/
105
105
 
106
106
  ---
107
107
 
108
- ## 🚀 快速开始
108
+ ## 🚀 一键安装
109
109
 
110
- ### 1. 安装依赖
110
+ > 💡 **无需手动安装 Python、Node.js 等依赖**,脚本会自动检测并安装所有前置条件。仅复制一行命令即可完成安装。
111
+
112
+ ### Windows(PowerShell)
113
+
114
+ ```powershell
115
+ powershell -c "irm https://raw.githubusercontent.com/ctz168/myagent/main/install/install.ps1 | iex"
116
+ ```
117
+
118
+ > 首次运行时如果 PowerShell 提示执行策略限制,先执行:`Set-ExecutionPolicy RemoteSigned -Scope CurrentUser`
119
+
120
+ ### macOS / Linux
111
121
 
112
122
  ```bash
113
- # 克隆项目
114
- git clone https://github.com/ctz168/myagent.git
115
- cd myagent
116
-
117
- # 安装核心依赖
118
- pip install -r requirements.txt
119
-
120
- # 按需安装聊天平台
121
- pip install python-telegram-bot # Telegram
122
- pip install discord.py # Discord
123
- pip install playwright && playwright install chromium # 浏览器
124
- pip install anthropic # Claude
123
+ curl -fsSL https://raw.githubusercontent.com/ctz168/myagent/main/install/install.sh | bash
125
124
  ```
126
125
 
127
- ### 2. 配置 LLM
126
+ ---
127
+
128
+ ## 📋 系统要求
129
+
130
+ | 项目 | 最低要求 | 推荐配置 |
131
+ |------|----------|----------|
132
+ | **操作系统** | Windows 10 / macOS 12 / Ubuntu 20.04 | Windows 11 / macOS 14 / Ubuntu 24.04 |
133
+ | **Python** | 3.10+ | 3.12+ |
134
+ | **Node.js** | 18+ (LTS) | 20+ (LTS) |
135
+ | **内存** | 512 MB | 2 GB+ |
136
+ | **磁盘空间** | 200 MB | 500 MB+ |
137
+
138
+ > 安装脚本会自动检测并安装 Python 3.12 和 Node.js 20 LTS,你不需要提前准备任何环境。
139
+
140
+ ---
141
+
142
+ ## ⚙️ 安装后配置
143
+
144
+ 安装完成后,运行 `myagent-ai` 即可启动。首次运行会自动创建配置文件 `~/.myagent/config.json`。
145
+
146
+ ### 1. 配置 LLM
128
147
 
129
148
  **方式一: 环境变量 (推荐)**
130
149
  ```bash
@@ -135,7 +154,7 @@ export MYAGENT_LLM_MODEL="gpt-4"
135
154
  **方式二: 配置文件**
136
155
  ```bash
137
156
  # 首次运行会自动创建 ~/.myagent/config.json
138
- python main.py
157
+ myagent-ai
139
158
  ```
140
159
 
141
160
  **方式三: 使用 Ollama 本地模型 (免费)**
@@ -155,23 +174,23 @@ export MYAGENT_LLM_PROVIDER="anthropic"
155
174
  export MYAGENT_ANTHROPIC_API_KEY="sk-ant-..."
156
175
  ```
157
176
 
158
- ### 3. 运行
177
+ ### 2. 运行
159
178
 
160
179
  ```bash
161
180
  # 交互式命令行
162
- python main.py
181
+ myagent-ai
163
182
 
164
183
  # 系统托盘后台运行
165
- python main.py --tray
184
+ myagent-ai --tray
166
185
 
167
186
  # 调试模式
168
- python main.py --debug
187
+ myagent-ai --debug
169
188
 
170
189
  # 设置开机自启
171
- python main.py --autostart
190
+ myagent-ai --autostart
172
191
  ```
173
192
 
174
- ### 4. 配置聊天平台 (可选)
193
+ ### 3. 配置聊天平台 (可选)
175
194
 
176
195
  编辑 `~/.myagent/config.json`:
177
196
 
package/agents/base.py CHANGED
@@ -18,6 +18,21 @@ from core.utils import generate_id, timestamp
18
18
  logger = get_logger("myagent.agent")
19
19
 
20
20
 
21
+ # 全局权限管理器实例(由 main.py 初始化)
22
+ _global_permission_manager = None
23
+
24
+
25
+ def set_permission_manager(pm):
26
+ """设置全局权限管理器(应用启动时调用一次)"""
27
+ global _global_permission_manager
28
+ _global_permission_manager = pm
29
+
30
+
31
+ def get_permission_manager():
32
+ """获取全局权限管理器"""
33
+ return _global_permission_manager
34
+
35
+
21
36
  @dataclass
22
37
  class AgentContext:
23
38
  """Agent 上下文,在 Agent 之间传递"""
@@ -59,6 +74,7 @@ class BaseAgent(ABC):
59
74
  self.task_queue = task_queue
60
75
  self.config = config or {}
61
76
  self.config_broadcaster = config_broadcaster
77
+ self._permission_manager = None # 延迟绑定,在 process 时获取
62
78
  self._stats = {"total_tasks": 0, "success": 0, "failed": 0}
63
79
 
64
80
  @abstractmethod
@@ -113,3 +129,44 @@ class BaseAgent(ABC):
113
129
 
114
130
  def get_stats(self) -> Dict:
115
131
  return dict(self._stats)
132
+
133
+ # ── 权限检查 ────────────────────────────────────────────
134
+
135
+ @property
136
+ def permission_manager(self):
137
+ """获取权限管理器(延迟绑定)"""
138
+ if self._permission_manager is None:
139
+ from agents.base import get_permission_manager
140
+ self._permission_manager = get_permission_manager()
141
+ return self._permission_manager
142
+
143
+ def check_permission(self, permission: str) -> bool:
144
+ """
145
+ 检查当前 agent 是否拥有某项权限。
146
+
147
+ Args:
148
+ permission: 权限项名称 (execution/file_read/file_write/network/local_comm/remote_comm)
149
+
150
+ Returns:
151
+ True 如果权限开启,或权限管理器未初始化(向后兼容)
152
+ """
153
+ pm = self.permission_manager
154
+ if pm is None:
155
+ # 权限管理器未初始化,允许所有操作(向后兼容)
156
+ return True
157
+ return pm.check_permission(self.name, permission)
158
+
159
+ def require_permission(self, permission: str) -> bool:
160
+ """
161
+ 要求指定权限,权限不足时记录警告并返回 False。
162
+
163
+ Args:
164
+ permission: 权限项名称
165
+
166
+ Returns:
167
+ True 如果权限通过
168
+ """
169
+ pm = self.permission_manager
170
+ if pm is None:
171
+ return True
172
+ return pm.require_permission(self.name, permission)
@@ -13,6 +13,7 @@ from core.logger import get_logger
13
13
  from core.llm import LLMClient, LLMResponse, Message
14
14
  from agents.base import BaseAgent, AgentContext
15
15
  from core.utils import generate_id, timestamp, safe_json_parse, truncate_str
16
+ from core.context_manager import ContextManager, ContextConfig, estimate_tokens
16
17
 
17
18
  logger = get_logger("myagent.agent.main")
18
19
 
@@ -111,7 +112,9 @@ class MainAgent(BaseAgent):
111
112
  self._registered_task: bool = False
112
113
  # 组织上下文缓存(避免重复加载)
113
114
  self._org_context_cache: str = ""
114
- self._org_context_loaded: bool = False
115
+ self._org_context_mtime: float = 0 # 文件修改时间,用于检测变更
116
+ # Token 上下文管理器 (滚动摘要 + 预算控制)
117
+ self.context_manager = ContextManager(ContextConfig())
115
118
 
116
119
  async def process(self, context: AgentContext) -> AgentContext:
117
120
  """
@@ -180,12 +183,15 @@ class MainAgent(BaseAgent):
180
183
 
181
184
  # 检查配置热加载广播(每次迭代前检查)
182
185
  if self.config_broadcaster:
183
- reloaded = await self.config_broadcaster.check_and_wait(task_id)
186
+ reloaded, reload_type = await self.config_broadcaster.check_and_wait(task_id)
184
187
  if reloaded:
185
- logger.info(f"[{task_id}] 🔄 配置已热更新,继续执行 (iteration={self._iteration_count})")
188
+ logger.info(f"[{task_id}] 🔄 {reload_type}已热更新,继续执行 (iteration={self._iteration_count})")
186
189
  # 更新迭代检查点
187
190
  if self.config_broadcaster._active_tasks.get(task_id):
188
191
  self.config_broadcaster._active_tasks[task_id].iteration = self._iteration_count
192
+ # 代码热更新后,LLM 客户端可能已被刷新,下次调用会自动重建
193
+ if reload_type == "code":
194
+ logger.info(f"[{task_id}] 代码模块已热更新,LLM 客户端将在下次调用时自动重建")
189
195
 
190
196
  # 构建消息列表
191
197
  messages = self._build_messages(context)
@@ -205,21 +211,22 @@ class MainAgent(BaseAgent):
205
211
 
206
212
  # 检查是否有工具调用 (OpenAI function calling)
207
213
  if response.tool_calls:
214
+ # 先将 assistant 的 tool_calls 消息加入历史(OpenAI 格式要求)
215
+ context.conversation_history.append(
216
+ Message(role="assistant",
217
+ content=response.content or "",
218
+ tool_calls=response.tool_calls)
219
+ )
220
+ # 执行工具调用
208
221
  tool_results = await self._handle_tool_calls(
209
222
  response.tool_calls, context, task_id
210
223
  )
211
- # 将工具结果加入消息历史
224
+ # 再将工具结果加入消息历史
212
225
  for tc, result in tool_results:
213
226
  context.conversation_history.append(
214
227
  Message(role="tool", content=json.dumps(result, ensure_ascii=False),
215
228
  tool_call_id=tc["id"], name=tc["name"])
216
229
  )
217
- context.conversation_history.append(
218
- Message(role="assistant",
219
- content=f"[已调用工具 {tc['name']},结果: "
220
- f"{'成功' if result.get('success') else '失败'}]",
221
- tool_calls=[tc])
222
- )
223
230
  # 继续循环,让 LLM 处理工具结果
224
231
  continue
225
232
 
@@ -275,6 +282,39 @@ class MainAgent(BaseAgent):
275
282
  final_response += "\n\n已完成: " + " → ".join(action_data["plan"])
276
283
  break
277
284
 
285
+ # 如果有超时且诊断建议不重试,强制中断循环避免无效重试
286
+ if has_timeout:
287
+ should_abort = False
288
+ abort_reasons = []
289
+ for i, r in enumerate(results, 1):
290
+ if r.get("timed_out"):
291
+ diag = r.get("timeout_diagnosis", {})
292
+ if diag.get("should_retry") is False:
293
+ should_abort = True
294
+ abort_reasons.append(
295
+ f"命令{i}: {diag.get('diagnosis', '不可恢复的超时')}"
296
+ )
297
+ if should_abort:
298
+ logger.warning(
299
+ f"[{task_id}] ⛔ 超时诊断建议不重试,终止当前执行循环 "
300
+ f"(iteration={self._iteration_count})"
301
+ )
302
+ # 构建诊断反馈,强制LLM输出final_answer
303
+ abort_msg = (
304
+ "[系统通知] 以下命令因超时被终止,且超时诊断结果表明不应重试:\n"
305
+ )
306
+ for reason in abort_reasons:
307
+ abort_msg += f"- {reason}\n"
308
+ abort_msg += (
309
+ "\n请直接以纯文本或 {\"type\": \"final_answer\", \"content\": \"...\"} "
310
+ "格式回复,告知用户任务无法完成的原因和建议的替代方案。"
311
+ "不要再尝试执行相同的命令。"
312
+ )
313
+ context.conversation_history.append(
314
+ Message(role="user", content=abort_msg)
315
+ )
316
+ continue # 让LLM生成final_answer,而不是重试命令
317
+
278
318
  continue
279
319
 
280
320
  # 单个 action
@@ -300,44 +340,59 @@ class MainAgent(BaseAgent):
300
340
 
301
341
  logger.info(f"[{task_id}] 处理完成 (迭代 {self._iteration_count} 次)")
302
342
 
303
- # 检查是否需要总结对话
343
+ # 检查是否需要增量总结对话 (使用滚动摘要,不再清空短期记忆)
304
344
  if self.memory_agent and self._iteration_count > 5:
305
345
  try:
306
- await self.memory_agent.process(AgentContext(
346
+ summary_context = AgentContext(
307
347
  session_id=context.session_id,
308
348
  metadata={"memory_action": "summarize"},
309
- ))
349
+ )
350
+ await self.memory_agent.process(summary_context)
351
+ # 将生成的摘要更新到 ContextManager 的滚动摘要缓冲
352
+ summary_result = summary_context.working_memory.get("summary_result", "")
353
+ if summary_result:
354
+ self.context_manager.summary_buffer.update_summary(
355
+ session_id=context.session_id,
356
+ new_summary=summary_result,
357
+ summarized_count=summary_context.working_memory.get("summarized_count", 0),
358
+ )
359
+ logger.info(
360
+ f"[{task_id}] 滚动摘要已更新 (session={context.session_id})"
361
+ )
310
362
  except Exception as e:
311
363
  logger.warning(f"对话总结失败: {e}")
312
364
 
313
365
  return context
314
366
 
315
367
  def _build_messages(self, context: AgentContext) -> List[Message]:
316
- """构建完整的消息列表"""
317
- messages = [Message(role="system", content=self.SYSTEM_PROMPT)]
318
-
319
- # 添加组织上下文(如果已启用)
368
+ """构建完整的消息列表 (使用 ContextManager 进行 Token 预算优化)"""
369
+ # 合并系统提示 + 组织上下文
370
+ system_prompt = self.SYSTEM_PROMPT
320
371
  org_context = self._build_org_context(context)
321
372
  if org_context:
322
- messages.append(Message(
323
- role="system",
324
- content=org_context,
325
- ))
373
+ system_prompt += "\n\n" + org_context
326
374
 
327
- # 添加记忆上下文
375
+ # 记忆上下文
328
376
  memory_ctx = context.working_memory.get("memory_context_prompt", "")
329
- if memory_ctx:
330
- messages.append(Message(
331
- role="system",
332
- content=f"[记忆上下文]\n{memory_ctx}",
333
- ))
334
-
335
- # 添加对话历史
336
- for msg in context.conversation_history[-20:]:
337
- messages.append(msg)
338
377
 
339
- # 添加当前用户消息
340
- messages.append(Message(role="user", content=context.user_message))
378
+ # 使用 ContextManager 构建优化后的消息列表
379
+ raw_messages = self.context_manager.build_context(
380
+ system_prompt=system_prompt,
381
+ memory_context=memory_ctx,
382
+ conversation_history=context.conversation_history,
383
+ user_message=context.user_message,
384
+ session_id=context.session_id,
385
+ )
386
+
387
+ # 转换回 Message 对象 (ContextManager 返回 dict)
388
+ messages = []
389
+ for m in raw_messages:
390
+ if isinstance(m, dict):
391
+ messages.append(Message(**m))
392
+ elif isinstance(m, Message):
393
+ messages.append(m)
394
+ else:
395
+ messages.append(m)
341
396
 
342
397
  return messages
343
398
 
@@ -351,6 +406,31 @@ class MainAgent(BaseAgent):
351
406
  logger.warning(f"获取工具列表失败: {e}")
352
407
  return None
353
408
 
409
+ @staticmethod
410
+ def _get_skill_permission(skill_name: str) -> Optional[str]:
411
+ """
412
+ 根据技能名称映射到对应的权限项。
413
+
414
+ Returns:
415
+ 权限项名称,或 None(不需要特殊权限的技能)
416
+ """
417
+ # 文件技能
418
+ if skill_name in ("file_write", "file_delete", "file_move"):
419
+ return "file_write"
420
+ if skill_name in ("file_read", "file_list", "file_search"):
421
+ return "file_read"
422
+ # 网络/搜索技能
423
+ if skill_name in ("web_search", "web_read", "url_read"):
424
+ return "network"
425
+ # 系统技能涉及执行
426
+ if skill_name in ("command_run",):
427
+ return "execution"
428
+ # 浏览器技能需要网络
429
+ if skill_name in ("browser_open", "browser_click", "browser_fill"):
430
+ return "network"
431
+ # 其他技能不需要特殊权限检查
432
+ return None
433
+
354
434
  async def _handle_tool_calls(
355
435
  self,
356
436
  tool_calls: List[Dict],
@@ -397,6 +477,19 @@ class MainAgent(BaseAgent):
397
477
  action_type = action.get("type", "")
398
478
 
399
479
  if action_type == "skill" and self.skills:
480
+ # 权限检查: 根据技能类别检查权限
481
+ skill_name = action.get("name", "")
482
+ skill_perm = self._get_skill_permission(skill_name)
483
+ if skill_perm and not self.check_permission(skill_perm):
484
+ from core.permissions import PermissionManager
485
+ label = PermissionManager.PERMISSION_LABELS.get(skill_perm, skill_perm)
486
+ results.append({
487
+ "success": False,
488
+ "error": f"[权限] 当前 Agent 没有'{label}'权限,操作被拒绝",
489
+ "metadata": {"permission_denied": skill_perm},
490
+ })
491
+ continue
492
+
400
493
  result = await self.skills.execute(
401
494
  action.get("name", ""),
402
495
  **action.get("params", {}),
@@ -404,6 +497,19 @@ class MainAgent(BaseAgent):
404
497
  results.append(result.to_dict())
405
498
 
406
499
  elif action_type == "code" and self.executor:
500
+ # 权限检查: execution
501
+ if not self.check_permission("execution"):
502
+ results.append({
503
+ "success": False,
504
+ "error": "[权限] 当前 Agent 没有代码执行权限,操作被拒绝",
505
+ "metadata": {"permission_denied": "execution"},
506
+ })
507
+ continue
508
+
509
+ # 注入权限检查器到 executor(用于更细粒度的检查)
510
+ self.executor.set_permission_checker(
511
+ self.check_permission, self.name
512
+ )
407
513
  # 提取 LLM 预估的超时时间
408
514
  timeout_seconds = action.get("timeout_seconds")
409
515
  if timeout_seconds is None:
@@ -636,22 +742,25 @@ class MainAgent(BaseAgent):
636
742
 
637
743
  parts = []
638
744
 
639
- # 1. 读取 organization.md(使用缓存避免重复读取)
640
- if not self._org_context_loaded:
641
- try:
642
- from config import get_config
643
- config_mgr = get_config()
644
- org_dir = config_mgr.data_dir / "organization"
645
- org_info_path = org_dir / "organization.md"
646
- if org_info_path.exists():
745
+ # 1. 读取 organization.md(使用 mtime 缓存检测变更)
746
+ try:
747
+ from config import get_config
748
+ config_mgr = get_config()
749
+ org_dir = config_mgr.data_dir / "organization"
750
+ org_info_path = org_dir / "organization.md"
751
+ if org_info_path.exists():
752
+ current_mtime = org_info_path.stat().st_mtime
753
+ if current_mtime != self._org_context_mtime:
647
754
  content = org_info_path.read_text(encoding="utf-8")
648
755
  # 检查是否仍是模板(包含占位注释)
649
756
  is_template = "<!-- 在此填写" in content
650
757
  if not is_template:
651
758
  self._org_context_cache = content
652
- except Exception as e:
653
- logger.debug(f"读取组织信息失败: {e}")
654
- self._org_context_loaded = True
759
+ else:
760
+ self._org_context_cache = ""
761
+ self._org_context_mtime = current_mtime
762
+ except Exception as e:
763
+ logger.debug(f"读取组织信息失败: {e}")
655
764
 
656
765
  if self._org_context_cache:
657
766
  parts.append("## 组织信息\n")
@@ -192,7 +192,13 @@ class MemoryAgent(BaseAgent):
192
192
  ]
193
193
 
194
194
  async def _summarize(self, context: AgentContext, session_id: str):
195
- """总结当前对话"""
195
+ """
196
+ 增量总结当前对话 (滚动摘要模式)。
197
+
198
+ 变更: 不再清空短期记忆,而是生成摘要并返回给 MainAgent,
199
+ 由 ContextManager 的 RollingSummaryBuffer 管理滚动摘要。
200
+ 同时仍将偏好和错误经验提取到长期记忆。
201
+ """
196
202
  if not self.memory or not self.llm:
197
203
  return
198
204
 
@@ -213,9 +219,12 @@ class MemoryAgent(BaseAgent):
213
219
 
214
220
  result = await self._call_llm_json(messages)
215
221
  if "error" not in result:
222
+ summary_text = result.get("summary", "")
223
+
224
+ # 保存摘要到长期记忆 (保留原有功能)
216
225
  self.memory.save_summary(
217
226
  session_id=session_id,
218
- summary=result.get("summary", ""),
227
+ summary=summary_text,
219
228
  )
220
229
 
221
230
  # 保存偏好
@@ -235,9 +244,20 @@ class MemoryAgent(BaseAgent):
235
244
  fix=err_item.get("fix", ""),
236
245
  )
237
246
 
238
- logger.info(f"对话已总结并保存到长期记忆 (session={session_id})")
239
- # 清理已总结的旧对话
240
- self.memory.clear_conversation(session_id)
247
+ # 返回摘要结果供 ContextManager 的滚动摘要缓冲使用
248
+ # (不再清空短期记忆,保持对话连续性)
249
+ key_points = result.get("key_points", [])
250
+ full_summary = summary_text
251
+ if key_points:
252
+ full_summary += "\n要点: " + "; ".join(key_points)
253
+
254
+ context.working_memory["summary_result"] = full_summary
255
+ context.working_memory["summarized_count"] = len(entries)
256
+
257
+ logger.info(
258
+ f"对话增量总结完成 (session={session_id}, "
259
+ f"entries={len(entries)}, 不再清空短期记忆)"
260
+ )
241
261
 
242
262
  async def _record_error(self, context: AgentContext, session_id: str):
243
263
  """记录错误模式"""
@@ -61,6 +61,7 @@ class CommunicationManager:
61
61
  """
62
62
  self.config = config
63
63
  self._data_dir = Path(data_dir) if data_dir else Path.home() / ".myagent" / "data"
64
+ self._permission_checker = None # callable(perm_name) -> bool, set by app
64
65
 
65
66
  # Identity keys (Ed25519)
66
67
  self._ed_priv: bytes = b""
@@ -255,6 +256,9 @@ class CommunicationManager:
255
256
  msg = None
256
257
  # Check if the peer is on this machine (has a local queue)
257
258
  if to_agent in LocalChannel._queues:
259
+ # 本机通信权限检查
260
+ if not self._check_permission("local_comm"):
261
+ raise RuntimeError("[权限] 当前 Agent 没有本机通信权限")
258
262
  msg = await self._local_channel.send(
259
263
  to_agent=to_agent,
260
264
  content=content,
@@ -262,6 +266,9 @@ class CommunicationManager:
262
266
  their_public_key=their_pub,
263
267
  )
264
268
  elif self._remote_channel:
269
+ # 跨电脑通信权限检查
270
+ if not self._check_permission("remote_comm"):
271
+ raise RuntimeError("[权限] 当前 Agent 没有跨电脑通信权限,请先在权限管理中开启")
265
272
  their_x_pub_hex = peer.permissions.get("x25519_public_key", "")
266
273
  msg = await self._remote_channel.send(
267
274
  to_agent=to_agent,
@@ -371,6 +378,19 @@ class CommunicationManager:
371
378
  """Register a callback for incoming messages."""
372
379
  self._message_callbacks.append(callback)
373
380
 
381
+ def set_permission_checker(self, checker):
382
+ """设置权限检查回调(由应用启动时注入)"""
383
+ self._permission_checker = checker
384
+
385
+ def _check_permission(self, permission: str) -> bool:
386
+ """检查权限,无权限检查器时默认通过"""
387
+ if self._permission_checker is None:
388
+ return True
389
+ try:
390
+ return bool(self._permission_checker(permission))
391
+ except Exception:
392
+ return True
393
+
374
394
  # ==================================================================
375
395
  # Status
376
396
  # ==================================================================
package/core/__init__.py CHANGED
@@ -1,10 +1,4 @@
1
1
  """
2
- core/__init__.py - 核心模块初始化
2
+ core/__init__.py - 核心模块
3
3
  """
4
- from core.logger import setup_logger, get_logger
5
- from core.utils import (
6
- timestamp, generate_id, truncate_str,
7
- safe_json_parse, validate_json_schema
8
- )
9
- from core.llm import LLMClient, get_llm_client
10
- from core.task_queue import TaskQueue, TaskItem, TaskStatus
4
+ from core.version import __version__