@jun133/athlete 0.0.4 → 0.0.6

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 (92) hide show
  1. package/README.md +149 -91
  2. package/dist/cli.js +26562 -19473
  3. package/dist/cli.js.map +1 -1
  4. package/package.json +6 -1
  5. package/scripts/postinstall-playwright.mjs +1 -1
  6. package/spec/README.md +49 -41
  7. package/spec//346/212/200/346/234/257/345/256/236/347/216/260/README.md +29 -0
  8. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//344/270/273/345/276/252/347/216/257/344/270/216/350/260/203/345/272/246.md +60 -0
  9. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//344/273/223/345/272/223/347/272/246/346/235/237//345/274/200/345/217/221/350/247/204/345/210/231.md +14 -0
  10. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//344/273/223/345/272/223/347/272/246/346/235/237//346/234/254/345/234/260/345/221/275/344/273/244/344/270/216/346/265/201/347/250/213.md +20 -0
  11. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//344/273/223/345/272/223/347/272/246/346/235/237//346/265/213/350/257/225/347/255/226/347/225/245.md +48 -0
  12. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//344/273/243/347/240/201/345/234/260/345/233/276//347/233/256/345/275/225/345/210/260/344/273/243/347/240/201/346/230/240/345/260/204.md +51 -0
  13. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//345/205/263/351/224/256/346/250/241/345/235/227/Telegram/347/247/201/350/201/212.md +31 -0
  14. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//345/205/263/351/224/256/346/250/241/345/235/227//344/272/244/344/272/222/347/273/210/347/253/257.md +26 -0
  15. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//345/205/263/351/224/256/346/250/241/345/235/227//345/221/275/344/273/244/350/241/214/344/272/247/345/223/201/351/235/242.md +27 -0
  16. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//345/205/263/351/224/256/346/250/241/345/235/227//345/244/232/346/231/272/350/203/275/344/275/223/350/260/203/345/272/246.md +79 -0
  17. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//345/205/263/351/224/256/346/250/241/345/235/227//345/256/277/344/270/273/350/277/220/350/241/214/350/276/271/347/225/214.md +74 -0
  18. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//345/205/263/351/224/256/346/250/241/345/235/227//345/267/245/344/275/234/345/214/272/344/270/216/345/271/266/350/241/214/351/232/224/347/246/273.md +55 -0
  19. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//345/205/263/351/224/256/346/250/241/345/235/227//345/276/256/344/277/241/347/247/201/350/201/212.md +33 -0
  20. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//345/205/263/351/224/256/346/250/241/345/235/227//346/211/251/345/261/225/346/234/272/345/210/266.md +179 -0
  21. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//345/205/263/351/224/256/346/250/241/345/235/227//346/216/247/345/210/266/351/235/242/350/264/246/346/234/254.md +32 -0
  22. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//345/205/263/351/224/256/346/250/241/345/235/227//351/205/215/347/275/256/347/263/273/347/273/237.md +31 -0
  23. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//346/200/273/344/275/223/346/236/266/346/236/204.md +115 -0
  24. package/spec//346/212/200/346/234/257/345/256/236/347/216/260//347/212/266/346/200/201/344/270/216/347/234/237/347/233/270/346/272/220.md +129 -0
  25. package/spec//347/224/250/346/210/267/345/256/241/351/230/205/README.md +27 -0
  26. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//344/272/247/345/223/201/345/256/232/344/275/215.md +78 -0
  27. package/spec/{principles/P06- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/06-}/344/270/212/344/270/213/346/226/207/350/246/201/350/203/275/345/216/213/347/274/251.md +2 -2
  28. package/spec/{principles/P08- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/08-}/346/205/242/346/223/215/344/275/234/346/224/276/345/220/216/345/217/260.md +1 -1
  29. package/spec/{principles/P13-session → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/13-session}/346/230/257/344/273/273/345/212/241/347/216/260/345/234/272.md +3 -3
  30. package/spec/{principles/P15-provider → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/15-provider}/345/277/205/351/241/273/345/217/257/346/233/277/346/215/242.md +3 -1
  31. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/17-/346/211/251/345/261/225/351/235/240/344/272/213/344/273/266/347/224/237/351/225/277.md +71 -0
  32. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/18-/344/270/273/345/276/252/347/216/257/345/222/214/346/226/207/344/273/266/351/203/275/344/270/215/350/203/275/351/225/277/350/203/226.md +37 -0
  33. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/20-/345/244/226/351/203/250/344/272/213/345/256/236/345/277/205/351/241/273/347/273/221/345/256/232/350/257/201/346/215/256.md +48 -0
  34. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/21-/346/262/241/351/252/214/350/277/207/345/260/261/344/270/215/350/203/275/346/224/266/345/217/243.md +46 -0
  35. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/22-/351/230/266/346/256/265/346/216/250/350/277/233/345/277/205/351/241/273/346/234/211/346/234/272/345/231/250/347/212/266/346/200/201.md +40 -0
  36. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/23-/346/226/207/346/234/254/351/223/276/350/267/257/345/277/205/351/241/273/347/250/263/345/256/232/345/217/257/350/257/273.md +38 -0
  37. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/24-/351/224/231/350/257/257/345/205/274/345/256/271/344/270/215/350/203/275/351/253/230/344/272/216/346/255/243/347/241/256/346/200/247.md +38 -0
  38. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/25-/346/226/260/351/241/271/347/233/256/344/270/215/344/270/272/346/227/247/346/256/213/344/275/231/344/277/235/346/264/273.md +66 -0
  39. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/README.md +37 -0
  40. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//347/263/273/347/273/237/345/205/250/346/231/257.md +57 -0
  41. package/spec//347/224/250/346/210/267/345/256/241/351/230/205//351/241/271/347/233/256/350/214/203/345/233/264.md +59 -0
  42. package/spec/adr/ADR-0001-/345/215/225/346/250/241/345/274/217/345/205/250/346/235/203/351/231/220.md +0 -16
  43. package/spec/adr/ADR-0002-/345/215/225agent/350/265/267/346/255/245/345/271/266/351/242/204/347/225/231/345/244/232agent/350/276/271/347/225/214.md +0 -19
  44. package/spec/adr/ADR-0003-openai-compatible/344/274/230/345/205/210.md +0 -16
  45. package/spec/architecture//346/200/273/344/275/223/346/236/266/346/236/204.md +0 -111
  46. package/spec/architecture//347/212/266/346/200/201/344/270/216/347/234/237/347/233/270/346/272/220.md +0 -117
  47. package/spec/architecture//350/277/220/350/241/214/346/227/266/345/276/252/347/216/257.md +0 -82
  48. package/spec/implementation/README.md +0 -17
  49. package/spec/implementation//346/250/241/345/235/227/347/272/247/345/274/200/345/217/221/344/273/273/345/212/241/345/215/225.md +0 -55
  50. package/spec/implementation//347/233/256/345/275/225/347/273/223/346/236/204/345/210/260/344/273/243/347/240/201/346/226/207/344/273/266/346/230/240/345/260/204/350/241/250.md +0 -101
  51. package/spec/interfaces/InteractionShell.md +0 -85
  52. package/spec/interfaces/ProviderAdapter.md +0 -23
  53. package/spec/interfaces/README.md +0 -17
  54. package/spec/interfaces/RuntimeLoop.md +0 -28
  55. package/spec/interfaces/SessionStore.md +0 -22
  56. package/spec/interfaces/ToolRegistry.md +0 -21
  57. package/spec/modules/config-system.md +0 -51
  58. package/spec/modules/interactive-terminal.md +0 -112
  59. package/spec/modules/lightweight-context-runtime.md +0 -63
  60. package/spec/modules/provider-adapter.md +0 -20
  61. package/spec/modules/runtime-metrics.md +0 -132
  62. package/spec/modules/runtime-rules.md +0 -33
  63. package/spec/modules/session-resume-compact.md +0 -49
  64. package/spec/modules/task-state.md +0 -34
  65. package/spec/modules/telegram-private-chat.md +0 -291
  66. package/spec/modules/tool-registry.md +0 -79
  67. package/spec/modules/weixin-private-chat.md +0 -291
  68. package/spec/modules/workspace-isolation.md +0 -24
  69. package/spec/modules//346/211/251/345/261/225/346/234/272/345/210/266.md +0 -105
  70. package/spec/overview/v0/350/214/203/345/233/264.md +0 -54
  71. package/spec/overview//344/272/247/345/223/201/345/256/232/344/271/211.md +0 -59
  72. package/spec/principles/P17-/346/211/251/345/261/225/351/235/240/344/272/213/344/273/266/347/224/237/351/225/277.md +0 -32
  73. package/spec/principles/P18-/344/270/273/345/276/252/347/216/257/345/222/214/346/226/207/344/273/266/351/203/275/344/270/215/350/203/275/351/225/277/350/203/226.md +0 -36
  74. package/spec/principles/README.md +0 -39
  75. package/spec/repo//345/274/200/345/217/221/350/247/204/345/210/231.md +0 -39
  76. package/spec/repo//346/234/254/345/234/260/345/221/275/344/273/244/344/270/216/346/265/201/347/250/213.md +0 -32
  77. package/spec/testing/fail-first-/347/254/254/344/270/200/346/211/271/346/265/213/350/257/225/345/210/227/350/241/250.md +0 -11
  78. package/spec/testing/fixtures-/350/247/204/350/214/203.md +0 -20
  79. package/spec/testing//346/265/213/350/257/225/347/255/226/347/225/245.md +0 -97
  80. /package/spec/{principles/P01- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/01-}/344/270/200/344/270/252/345/276/252/347/216/257/344/270/200/344/270/252/346/231/272/350/203/275/344/275/223.md" +0 -0
  81. /package/spec/{principles/P02- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/02-}/345/212/240/344/270/200/344/270/252/345/267/245/345/205/267/345/217/252/345/212/240/344/270/200/344/270/252/345/244/204/347/220/206/345/231/250.md" +0 -0
  82. /package/spec/{principles/P03- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/03-}/345/205/210/350/256/241/345/210/222/345/206/215/345/212/250/346/211/213.md" +0 -0
  83. /package/spec/{principles/P04- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/04-}/345/244/247/344/273/273/345/212/241/346/213/206/347/273/231/345/255/220/346/231/272/350/203/275/344/275/223.md" +0 -0
  84. /package/spec/{principles/P05- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/05-}/347/237/245/350/257/206/346/214/211/351/234/200/345/212/240/350/275/275.md" +0 -0
  85. /package/spec/{principles/P07- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/07-}/344/273/273/345/212/241/345/233/276/350/246/201/350/220/275/347/233/230.md" +0 -0
  86. /package/spec/{principles/P09- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/09-}/344/273/273/345/212/241/345/244/252/345/244/247/345/260/261/345/210/206/347/273/231/351/230/237/345/217/213.md" +0 -0
  87. /package/spec/{principles/P10- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/10-}/351/230/237/345/217/213/344/271/213/351/227/264/350/246/201/346/234/211/347/273/237/344/270/200/345/215/217/350/256/256.md" +0 -0
  88. /package/spec/{principles/P11- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/11-}/351/230/237/345/217/213/350/207/252/345/267/261/350/256/244/351/242/206/344/273/273/345/212/241.md" +0 -0
  89. /package/spec/{principles/P12- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/12-}/345/267/245/344/275/234/345/214/272/345/222/214/344/273/273/345/212/241/350/246/201/351/232/224/347/246/273.md" +0 -0
  90. /package/spec/{principles/P14- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/14-}/346/211/247/350/241/214/347/272/246/346/235/237/344/270/215/346/230/257/345/256/211/345/205/250/347/255/226/347/225/245.md" +0 -0
  91. /package/spec/{principles/P16- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/16-}/351/205/215/347/275/256/345/217/252/350/203/275/346/234/211/344/270/200/344/270/252/345/205/245/345/217/243.md" +0 -0
  92. /package/spec/{principles/P19- → /347/224/250/346/210/267/345/256/241/351/230/205//345/256/252/346/263/225/345/216/237/345/210/231/19-}/345/205/210/345/206/231/345/244/261/350/264/245/346/265/213/350/257/225/345/206/215/345/206/231/345/256/236/347/216/260.md" +0 -0
@@ -1,291 +0,0 @@
1
- # Telegram 私聊接入
2
-
3
- ## 范围
4
-
5
- - 只支持 Telegram 私聊
6
- - 不支持群聊、超级群、频道
7
- - 不做 Webhook 平台化,不做按钮系统,不做多平台网关
8
- - 只做 Telegram 通道层能力增强,不重写 Athlete 核心 runtime
9
-
10
- ## 宪法对齐
11
-
12
- 这块实现必须遵守 `spec/principles/` 里的几条核心原则:
13
-
14
- - `P01 一个循环一个智能体`
15
- Telegram 只是把私聊消息接到现有 lead turn,不重写 agent loop
16
- - `P02 加一个工具只加一个处理器`
17
- Telegram 文件回传能力通过独立工具处理器接入,不把平台分支塞进核心循环
18
- - `P16 配置只能有一个入口`
19
- Telegram 配置统一走 `src/config/store.ts` 和 `.athlete/.env`
20
- - `P17 扩展靠事件生长`
21
- Telegram 通道通过边界模块、turn display、tool registry、store 扩展,不靠在主循环里硬塞平台细节
22
- - `P18 主循环和文件都不能长胖`
23
- Telegram 服务编排、turn 执行、附件处理、日志和命令语义要按职责拆模块,避免一个大文件继续膨胀
24
-
25
- ## 模块边界
26
-
27
- Telegram 相关实现集中在 `src/telegram/`:
28
-
29
- - `types.ts`
30
- Telegram update/message 的规范化类型
31
- - `config.ts`
32
- Telegram 配置默认值、归一化、运行时目录解析
33
- - `botApiClient.ts`
34
- Telegram Bot API HTTP 客户端
35
- - `polling.ts`
36
- long polling 和 offset 提交
37
- - `offsetStore.ts`
38
- update offset 持久化
39
- - `sessionMapStore.ts`
40
- Telegram peer 到 Athlete session 的绑定
41
- - `attachmentStore.ts`
42
- Telegram 入站附件元数据持久化
43
- - `deliveryQueue.ts`
44
- 文本/文件投递队列、重试、恢复
45
- - `messageChunking.ts`
46
- 长文本分片
47
- - `localCommands.ts`
48
- Telegram 端命令语义适配
49
- - `turnDisplay.ts`
50
- Telegram 过程输出适配
51
- - `turnRunner.ts`
52
- 单个 Telegram turn 的运行编排
53
- - `service.ts`
54
- Telegram 服务总控、拉取、分发、stop、恢复
55
- - `logger.ts`
56
- 终端高层日志
57
- - `sendFileTool.ts`
58
- Telegram 文件回传工具
59
- - `processLock.ts`
60
- Telegram 服务单实例锁
61
- - `proxy.ts`
62
- Telegram 代理环境接线
63
-
64
- CLI 只负责注册命令和注入依赖,仍然放在:
65
-
66
- - `src/telegram/cli.ts`
67
- - `src/cli.ts`
68
-
69
- ## 启动方式
70
-
71
- 命令:
72
-
73
- ```powershell
74
- athlete telegram serve
75
- ```
76
-
77
- 行为:
78
-
79
- 1. 读取统一配置入口 `src/config/store.ts`
80
- 2. 解析 Telegram runtime 配置
81
- 3. 获取 Telegram 单实例锁
82
- 4. 启动 long polling
83
- 5. 把私聊消息接入现有 Athlete session / turn 体系
84
- 6. 把文本和文件回复先落盘到 delivery queue,再尝试发送
85
-
86
- 默认交互模式不会被劫持;只有显式执行 `athlete telegram serve` 才会启动 Telegram 服务。
87
-
88
- ## 配置
89
-
90
- Telegram 配置统一并入 `AppConfig.telegram` / `RuntimeConfig.telegram`。
91
-
92
- 推荐通过 `.athlete/.env` 配置:
93
-
94
- ```text
95
- ATHLETE_TELEGRAM_TOKEN=replace-with-your-bot-token
96
- ATHLETE_TELEGRAM_ALLOWED_USER_IDS=123456789
97
- ATHLETE_TELEGRAM_API_BASE_URL=https://api.telegram.org
98
- ATHLETE_TELEGRAM_PROXY_URL=http://127.0.0.1:7897
99
- ATHLETE_TELEGRAM_POLLING_TIMEOUT_SECONDS=50
100
- ATHLETE_TELEGRAM_POLLING_LIMIT=100
101
- ATHLETE_TELEGRAM_POLLING_RETRY_BACKOFF_MS=1000
102
- ATHLETE_TELEGRAM_MESSAGE_CHUNK_CHARS=3500
103
- ATHLETE_TELEGRAM_TYPING_INTERVAL_MS=4000
104
- ATHLETE_TELEGRAM_DELIVERY_MAX_RETRIES=6
105
- ATHLETE_TELEGRAM_DELIVERY_BASE_DELAY_MS=1000
106
- ATHLETE_TELEGRAM_DELIVERY_MAX_DELAY_MS=30000
107
- ```
108
-
109
- 说明:
110
-
111
- - `ATHLETE_TELEGRAM_ALLOWED_USER_IDS` 必须显式配置;空白名单等于任何人都不能控制 bot
112
- - `ATHLETE_TELEGRAM_PROXY_URL` 是 Telegram 专用代理入口,例如 Clash Verge 的 `mixed-port`
113
- - Telegram 配置仍然只走同一套配置入口,不另起平行配置系统
114
-
115
- ## 什么是本地代理入口
116
-
117
- 当用户使用 Clash Verge、Clash Meta、mihomo 这类代理工具时,常见情况是:
118
-
119
- - Telegram 域名会被 fake-ip 机制映射成 `198.18.x.x`
120
- - 应用程序自己不能直接拿这个 fake-ip 去直连
121
- - 应用程序应该把请求交给本地代理软件去转发
122
-
123
- 这里的“本地代理入口”就是:
124
-
125
- - 代理软件跑在用户自己的电脑上
126
- - 它会打开一个本地地址
127
- - 例如 `http://127.0.0.1:7897`
128
-
129
- 对于 Telegram 服务来说,真正稳定要配置的是这个本地入口,而不是 Telegram 最后被解析成的 fake-ip。
130
-
131
- ## 单实例语义
132
-
133
- Telegram 服务必须是单实例:
134
-
135
- - 同一个项目状态目录下,只允许一个 `telegram serve`
136
- - 如果已经有一个存活实例,第二个实例直接拒绝启动
137
- - 如果只剩下陈旧 `service.pid`,新实例会识别为 stale lock 并自动覆盖
138
-
139
- 这样可以避免:
140
-
141
- - 同一条消息被两个实例重复消费
142
- - 同一条最终回复被重复发送
143
- - 用户关掉一个窗口后,误以为服务还神秘存活
144
-
145
- ## 会话与恢复
146
-
147
- Telegram 状态目录位于:
148
-
149
- ```text
150
- <project-state-root>/.athlete/telegram/
151
- ```
152
-
153
- 持久化内容包括:
154
-
155
- - `offset.json`
156
- 下一个待消费的 Telegram update offset
157
- - `session-map.json`
158
- `telegram:private:<chatId>` 到 Athlete session 的映射
159
- - `attachments.json`
160
- 入站文件元数据和本地落盘路径
161
- - `delivery.json`
162
- 待发送文本/文件、重试次数和下次重试时间
163
- - `service.pid`
164
- Telegram 单实例锁文件
165
-
166
- 恢复语义:
167
-
168
- 1. 重启后先读 `offset.json`,从上次提交之后继续拉取
169
- 2. `session-map.json` 恢复同一 Telegram 私聊到同一个 Athlete session
170
- 3. `delivery.json` 在启动和轮询前后都会扫描并补发
171
- 4. `attachments.json` 让“刚发的文件”这类语义可继续工作
172
- 5. `service.pid` 如果是失效旧 pid,会被新实例覆盖
173
-
174
- ## 文件能力
175
-
176
- ### 入站文件
177
-
178
- - 用户可以在 Telegram 私聊直接发文件
179
- - Bot API 会下载文件到项目状态目录下的 Telegram 文件目录
180
- - 附件元数据会持久化
181
- - 当前 turn 会拿到这份文件的本地路径和上下文说明
182
- - 用户可以继续说“分析我刚发的文件”
183
-
184
- ### 出站文件
185
-
186
- - 用户可以要求 Athlete 查找本地文件并发回 Telegram
187
- - 用户可以要求 Athlete 生成文件并发回 Telegram
188
- - 回复形式必须是真实 Telegram document
189
- - 不允许只发本地路径或文本链接糊弄
190
-
191
- ### 恢复与边界
192
-
193
- - 文本和文件都先进入 delivery queue,再尝试发送
194
- - 失败后按指数退避重试
195
- - 服务重启后继续恢复待发送文本和文件
196
- - 文件大小要做边界校验
197
-
198
- ## `/stop`
199
-
200
- Telegram 端只保留一个停止命令:
201
-
202
- ```text
203
- /stop
204
- ```
205
-
206
- 语义:
207
-
208
- - 只停止“当前这个 Telegram 用户当前正在执行的任务”
209
- - bot 服务继续在线
210
- - 不停止整个 Telegram 服务
211
- - 不影响其他白名单用户
212
- - 停止后当前用户还能继续发下一条任务
213
-
214
- ## Telegram 端命令语义
215
-
216
- Telegram 端命令语义与本地 CLI 有明确边界:
217
-
218
- - `/session`、`/config`、`/runtime` 等查看类命令继续复用现有本地命令层
219
- - `/multi` 明确拒绝,并提示直接发送完整消息
220
- - `quit` / `reset` 不再作为 Telegram 主命令暴露
221
- - Telegram 端收到 `quit` / `reset` 时,只做提示,不执行本地终端语义
222
-
223
- ## 过程输出
224
-
225
- Telegram 过程输出要更接近终端工作感知,但遵守“可见段按事件逐条镜像”的单一语义,不能刷底层噪音。
226
-
227
- ### Telegram 聊天框里的可见消息
228
-
229
- 过程消息按实际事件顺序发送,不做机械固定轮换,也不做最终汇总:
230
-
231
- - `onToolCall` 不直接发聊天消息,避免把工具名刷进聊天框
232
- - 非 `todo_write` 的 tool result 只发送一条可见 preview,并截断到 150 个字符
233
- - `todo_write` 对应的可见 preview 一次,就发一条 todo preview 消息
234
- - `onAssistantDelta` 只作为阶段内缓冲信号,不直接发聊天消息
235
- - `onAssistantText` 表示拿到了完整 assistant 文本时,发一条 assistant 消息
236
- - `onAssistantDone` 带文本且当前 assistant 阶段尚未发出时,发一条最终 assistant 消息
237
- - 不合并多段 assistant
238
- - 不把 tool / todo / assistant 混成一条
239
-
240
- 不发送的内容:
241
-
242
- - 工具输出正文
243
- - 大段文件原文
244
- - 底层噪音日志
245
- - reasoning
246
- - `onStatus` 一类非可见噪音
247
-
248
- 格式约束:
249
-
250
- - 不额外加抬头
251
- - 过程消息保持聊天式、顺序式输出,更接近终端高层过程感知
252
-
253
- ### 终端日志
254
-
255
- 终端持续输出高层过程日志,包括:
256
-
257
- - 收到哪个 Telegram 用户的消息
258
- - 当前进入哪个 session
259
- - 当前在哪个阶段
260
- - 当前调用了哪个工具
261
- - 当前是否停止、失败、成功
262
- - 当前是否发送了文本或文件
263
-
264
- 显式信号与可靠性约束:
265
-
266
- - Weixin / Telegram 共享同一层可见事件判定与 durable turn display
267
- - durable outbound 只按事件顺序发送,不按文本去重
268
- - 如果要防重复,只能基于 event id / delivery state,不能基于文本内容
269
- - `runOnce` 必须等当前 turn 的可见输出 durable 完成后再 commit 输入
270
- - `serve` 主循环通过显式 pending-commit 队列继续轮询和处理 `/stop`,不靠时序猜测
271
- - 可见消息 durable enqueue 失败不能 silent swallow,必须继续上抛
272
-
273
- ## 串行与隔离
274
-
275
- - 同一 Telegram peer 使用 `PerPeerCommandQueue` 串行执行
276
- - 不同 peer 之间允许并发
277
- - `/stop` 与 per-peer 串行队列兼容
278
- - 同一个 peer 的 turn 不能并发破坏同一个 session
279
-
280
- ## 安全与约束
281
-
282
- - 只接受 `allowedUserIds` 白名单内的私聊用户
283
- - 非白名单用户不会进入 Athlete turn
284
- - 群聊/频道消息不会进入私聊 session
285
- - Telegram 端仍然遵守 Athlete 当前 runtime 的 `mode`、`allowedRoots` 和执行约束
286
-
287
- ## 维护约束
288
-
289
- - Telegram 平台细节不能下沉到 `src/agent/`、`src/tools/`、`src/ui/`
290
- - 只允许薄接线进入核心 runtime
291
- - 新增能力优先继续生长在 `src/telegram/`
@@ -1,79 +0,0 @@
1
- # tool registry
2
-
3
- ## 作用
4
-
5
- Tool registry 负责向模型公开动作集合,并统一管理本地工具与 MCP 动态工具。
6
-
7
- ## 当前组成
8
-
9
- - 本地内建工具
10
- - mode 过滤后的工具集
11
- - MCP 动态收集到的工具
12
-
13
- ## 当前规则
14
-
15
- 1. 新工具先注册,再暴露给模型。
16
- 2. tool handler 只做动作,不做控制面真相发明。
17
- 3. skill 不是工具替代品;skill 提供 workflow,tool 提供动作。
18
- 4. MCP 工具也必须经过统一 registry,不走旁路。
19
-
20
- ## 工具层职责
21
-
22
- `src/tools/` 当前负责:
23
-
24
- - 参数解析
25
- - 权限和状态校验
26
- - 调用下层 store / worker / utils
27
- - 把结果序列化返回给模型
28
-
29
- 不负责:
30
-
31
- - provider 请求
32
- - session 存储
33
- - 发明新的持久化真相
34
-
35
- ## 当前分组
36
-
37
- - `src/tools/files/`
38
- - `src/tools/documents/`
39
- - `src/tools/tasks/`
40
- - `src/tools/team/`
41
- - `src/tools/worktrees/`
42
- - `src/tools/background/`
43
- - `src/tools/shell/`
44
- - `src/tools/skills/`
45
-
46
- 共享层仍在 `src/tools/` 根目录:
47
-
48
- - `registry.ts`
49
- - `runtimeRegistry.ts`
50
- - `shared.ts`
51
- - `types.ts`
52
- - `changeTracking.ts`
53
-
54
- ## Playwright MCP 当前事实
55
-
56
- 当前 Playwright 浏览器工具通过 runtime registry 暴露为:
57
-
58
- - `mcp_playwright_browser_navigate`
59
- - `mcp_playwright_browser_snapshot`
60
- - `mcp_playwright_browser_click`
61
- - `mcp_playwright_browser_type`
62
- - `mcp_playwright_browser_take_screenshot`
63
- - 以及其他 `mcp_playwright_browser_*`
64
-
65
- 当前优先级策略:
66
-
67
- - runtime registry 会把 Playwright 浏览器工具排到本地文件工具和 shell 工具前面
68
- - request 级 tool priority 会在 web research / browser automation 场景继续把浏览器工具前置
69
- - `run_shell` 的网页抓取只应作为 fallback
70
-
71
- ## 当前约束
72
-
73
- 如果某个动作:
74
-
75
- - 需要明确输入输出
76
- - 不适合塞进 prompt
77
- - 不该让模型自己拼 shell
78
-
79
- 就应该做成工具,而不是继续加提示词。
@@ -1,291 +0,0 @@
1
- # Weixin 私聊接入
2
-
3
- ## 范围
4
-
5
- - 只支持 Weixin 私聊
6
- - 不支持群聊、频道式广播、多平台网关
7
- - 不做 `weixin-acp` / `codex-acp` / 外部子进程 agent 桥接
8
- - 只做 Weixin 通道层能力扩展,不重写 Athlete 核心 runtime
9
-
10
- ## 宪法对齐
11
-
12
- 这块实现必须遵守 `spec/principles/` 里的核心原则:
13
-
14
- - `P01 一个循环一个智能体`
15
- Weixin 只是把私聊消息接到现有 lead turn,不重写 agent loop
16
- - `P16 配置只能有一个入口`
17
- Weixin 配置统一走 `src/config/store.ts` 和 `.athlete/.env`
18
- - `P17 扩展靠事件生长`
19
- Weixin 通道通过边界模块、turn display、store、delivery queue 生长,不把平台细节塞进核心 runtime
20
- - `P18 主循环和文件都不能长胖`
21
- Weixin 服务编排、turn 执行、附件处理、日志、命令语义、CLI 按职责拆模块
22
- - `P19 先写失败测试再写实现`
23
- Weixin 配置、store、delivery、service、CLI、文档入口都有 fail-first 测试保护
24
-
25
- ## 模块边界
26
-
27
- Weixin 相关实现集中在 `src/weixin/`:
28
-
29
- - `types.ts`
30
- OpeniLink message shape 的 Athlete 规范化类型
31
- - `config.ts`
32
- Weixin 配置默认值、归一化、运行时状态目录解析
33
- - `client.ts`
34
- OpeniLink SDK 的 Athlete-native 包装层
35
- - `polling.ts`
36
- long polling 和 `sync_buf` 提交
37
- - `syncBufStore.ts`
38
- Weixin 拉取游标持久化
39
- - `credentialsStore.ts`
40
- 登录态持久化
41
- - `sessionMapStore.ts`
42
- Weixin peer 到 Athlete session 的绑定
43
- - `attachmentStore.ts`
44
- 入站附件元数据持久化
45
- - `contextTokenStore.ts`
46
- 每个 peer 最新 `context_token` 的持久化、刷新、失效标记
47
- - `deliveryQueue.ts`
48
- 文本 / 图片 / 视频 / 文件投递队列、重试、恢复、`context_token` fail-closed
49
- - `messageClassifier.ts`
50
- private-only 路由和消息类型判定
51
- - `inboundFiles.ts`
52
- 入站媒体下载、落盘、turn input 构造
53
- - `localCommands.ts`
54
- Weixin 端命令语义适配
55
- - `outputPort.ts`
56
- Weixin 文本输出适配
57
- - `turnDisplay.ts`
58
- Weixin 过程输出适配
59
- - `turnRunner.ts`
60
- 单个 Weixin turn 的运行编排
61
- - `service.ts`
62
- Weixin 服务总控、拉取、分发、stop、恢复
63
- - `logger.ts`
64
- 终端高层日志
65
- - `processLock.ts`
66
- Weixin 服务单实例锁
67
- - `cli.ts`
68
- `login / serve / logout` 正式命令入口
69
- - `sendFileTool.ts`
70
- Weixin 出站媒体工具桥
71
-
72
- CLI 注入仍然放在:
73
-
74
- - `src/weixin/cli.ts`
75
- - `src/cli.ts`
76
-
77
- ## 启动方式
78
-
79
- 命令:
80
-
81
- ```powershell
82
- athlete weixin login
83
- athlete weixin serve
84
- athlete weixin logout
85
- ```
86
-
87
- 行为:
88
-
89
- 1. `login` 通过 OpeniLink QR 登录并保存登录态
90
- 2. `serve` 读取统一配置入口和项目状态目录
91
- 3. 获取 Weixin 单实例锁
92
- 4. 启动 long polling
93
- 5. 把私聊消息接入现有 Athlete session / turn 体系
94
- 6. 把文本和媒体回复先落到 delivery queue,再尝试发送
95
- 7. `logout` 清理登录态、`sync_buf` 和 token 相关发送状态
96
-
97
- 默认交互模式不会被劫持;只有显式执行 `athlete weixin ...` 才会启动 Weixin 通道。
98
-
99
- ## 配置
100
-
101
- Weixin 配置统一并入 `AppConfig.weixin` / `RuntimeConfig.weixin`。
102
-
103
- 推荐通过 `.athlete/.env` 配置:
104
-
105
- ```text
106
- ATHLETE_WEIXIN_ALLOWED_USER_IDS=wxid_alice,wxid_bob
107
- ATHLETE_WEIXIN_BASE_URL=https://ilinkai.weixin.qq.com
108
- ATHLETE_WEIXIN_CDN_BASE_URL=https://novac2c.cdn.weixin.qq.com/c2c
109
- ATHLETE_WEIXIN_POLLING_TIMEOUT_MS=30000
110
- ATHLETE_WEIXIN_POLLING_RETRY_BACKOFF_MS=1000
111
- ATHLETE_WEIXIN_MESSAGE_CHUNK_CHARS=3500
112
- ATHLETE_WEIXIN_TYPING_INTERVAL_MS=4000
113
- ATHLETE_WEIXIN_QR_TIMEOUT_MS=480000
114
- ATHLETE_WEIXIN_DELIVERY_MAX_RETRIES=6
115
- ATHLETE_WEIXIN_DELIVERY_BASE_DELAY_MS=1000
116
- ATHLETE_WEIXIN_DELIVERY_MAX_DELAY_MS=30000
117
- ATHLETE_WEIXIN_ROUTE_TAG=
118
- ```
119
-
120
- 说明:
121
-
122
- - `ATHLETE_WEIXIN_ALLOWED_USER_IDS` 必须显式配置;空白名单等于任何人都不能控制
123
- - Weixin 配置仍然只走同一套配置入口,不另起平行配置系统
124
- - 登录态不放进配置文件,而是放在项目状态目录下的 `credentials.json`
125
-
126
- ## 单实例语义
127
-
128
- Weixin 服务必须是单实例:
129
-
130
- - 同一个项目状态目录下,只允许一个 `weixin serve`
131
- - 如果已经有一个存活实例,第二个实例直接拒绝启动
132
- - 如果只剩下陈旧 `service.pid`,新实例会识别为 stale lock 并自动覆盖
133
-
134
- 这样可以避免:
135
-
136
- - 同一条消息被两个实例重复消费
137
- - 同一条回复被重复发送
138
- - 用户误以为服务已经退出但实际上还有旧实例在线
139
-
140
- ## 状态与恢复
141
-
142
- Weixin 状态目录位于:
143
-
144
- ```text
145
- <project-state-root>/.athlete/weixin/
146
- ```
147
-
148
- 持久化内容包括:
149
-
150
- - `credentials.json`
151
- 扫码登录态
152
- - `sync-buf.json`
153
- 下一个拉取游标
154
- - `session-map.json`
155
- `weixin:private:<userId>` 到 Athlete session 的映射
156
- - `attachments.json`
157
- 入站图片 / 视频 / 文件 / 语音元数据和本地落盘路径
158
- - `context-token.json`
159
- 每个 peer 最新可用的 `context_token`
160
- - `delivery.json`
161
- 待发送文本 / 图片 / 视频 / 文件、重试次数、阻塞原因
162
- - `service.pid`
163
- Weixin 单实例锁
164
-
165
- 恢复语义:
166
-
167
- 1. 重启后先恢复 `sync_buf`
168
- 2. `session-map.json` 恢复同一 Weixin 私聊到同一个 Athlete session
169
- 3. `delivery.json` 在启动和轮询前后都会扫描并补发
170
- 4. `attachments.json` 让“刚发的文件 / 图片 / 语音”这类语义可继续工作
171
- 5. `context-token.json` 决定哪些待发送项现在可投递,哪些必须继续保留
172
-
173
- ## `context_token` 限制
174
-
175
- `context_token` 是 Weixin 通道的硬边界。
176
-
177
- Athlete 当前正式语义:
178
-
179
- - 每次入站消息都会捕获并更新当前 peer 可用的 `context_token`
180
- - 所有出站文本 / 图片 / 视频 / 文件都必须使用当前 token
181
- - delivery queue 会显式区分:
182
- - `missing_context_token`
183
- - `context_token_invalid`
184
- - 如果服务重启后队列里还有待发送项,但 token 缺失或失效:
185
- - 不丢消息
186
- - 不误报成功
187
- - 不 silent failure
188
- - 待发送项会保留
189
- - 只有等这个 peer 下次再发新消息、token 刷新后,队列才会再次尝试投递
190
-
191
- 这是 Weixin 和 Telegram 最关键的运行差异之一,README 和测试都必须同时体现。
192
-
193
- ## 文件能力
194
-
195
- ### 入站媒体
196
-
197
- - 支持图片、视频、文件、语音入站
198
- - OpeniLink 下载后的本地文件会进入当前 turn input 和附件上下文
199
- - 语音通过 OpeniLink 提供的下载 / 解码入口落成本地 WAV
200
- - 附件元数据会写入 `attachments.json`
201
-
202
- ### 出站媒体
203
-
204
- - 支持文本、图片、视频、文件出站
205
- - 工具桥会根据文件类型做真实路由
206
- - 不支持“只回复一个本地路径”这种伪文件发送
207
- - 不支持语音回发;该能力因为上游没有稳定发送接口而 fail-closed 拒绝
208
-
209
- ## `/stop`
210
-
211
- Weixin 端只保留一个停止命令:
212
-
213
- ```text
214
- /stop
215
- ```
216
-
217
- 语义:
218
-
219
- - 只停止“当前这个 Weixin 用户当前正在执行的任务”
220
- - 服务继续在线
221
- - 不影响其他白名单用户
222
- - 当前任务停止后,这个用户还能继续发下一条任务
223
-
224
- ## 命令语义
225
-
226
- Weixin 端命令语义与本地 CLI 有明确边界:
227
-
228
- - `/session`、`/config`、`/runtime` 等查看类命令继续复用现有本地命令层
229
- - `/multi` 明确拒绝,并提示直接发送完整消息
230
- - `quit` / `reset` / `/reset` 只做提示,不执行本地终端语义
231
-
232
- ## 过程输出
233
-
234
- Weixin 过程输出保持聊天式,但优先保证“最终结果稳定可见”,不刷底层噪音。
235
-
236
- 聊天框里的可见消息:
237
-
238
- - `todo_write` 对应的可见 preview 一次,就发一条 todo preview 消息
239
- - `onAssistantDelta` 只作为阶段内缓冲信号,不直接发聊天消息
240
- - `onAssistantText` 表示拿到了完整 assistant 文本时,发一条 assistant 消息
241
- - `onAssistantDone` 带文本且当前 assistant 阶段尚未发出时,发一条最终 assistant 消息
242
- - 不合并多段 assistant
243
- - Weixin 不再发送 tool call 聊天消息,避免挤占最终回复的可见额度
244
- - 短回复走普通文本;长回复超过阈值时直接写成 `.txt` 文件并作为文件发送
245
- - 不把 todo / assistant 混成一条
246
-
247
- 不发送:
248
-
249
- - tool call
250
- - 工具输出正文
251
- - 大段文件原文
252
- - 底层噪音日志
253
- - reasoning
254
- - `onStatus` 一类非可见噪音
255
-
256
- 终端日志持续输出高层事件:
257
-
258
- - 收到哪个 Weixin 用户的消息
259
- - 当前进入哪个 session
260
- - 当前阶段、工具、成功 / 失败 / 停止
261
- - 当前是否发送了文本或媒体
262
-
263
- 显式信号与可靠性约束:
264
-
265
- - Weixin / Telegram 共享同一层可见事件判定与 durable turn display
266
- - durable outbound 只按事件顺序发送,不按文本去重
267
- - 如果要防重复,只能基于 event id / delivery state,不能基于文本内容
268
- - `runOnce` 必须等当前 turn 的可见输出 durable 完成后再 commit 输入
269
- - `serve` 主循环通过显式 pending-commit 队列继续轮询和处理 `/stop`,不靠时序猜测
270
- - 可见消息 durable enqueue 失败不能 silent swallow,必须继续上抛
271
-
272
- ## 串行与隔离
273
-
274
- - 同一 Weixin peer 使用 per-peer queue 串行执行
275
- - 不同 peer 之间允许并发
276
- - `/stop` 与 per-peer 串行队列兼容
277
- - 同一个 peer 的 turn 不能并发破坏同一个 session
278
-
279
- ## 安全与约束
280
-
281
- - 只接受 `allowedUserIds` 白名单内的私聊用户
282
- - 非白名单用户不会进入 Athlete turn
283
- - 群聊消息不会进入 session
284
- - Weixin 端仍然遵守 Athlete 当前 runtime 的 `mode`、`allowedRoots` 和执行约束
285
-
286
- ## 非目标 / fail-closed 边界
287
-
288
- - 不做 `weixin-acp` / `codex-acp` 子进程桥接
289
- - 不做群聊路由投机实现
290
- - 不伪造语音回发能力
291
- - 不在 `src/agent/`、`src/tools/`、`src/ui/` 里散落平台特有分支
@@ -1,24 +0,0 @@
1
- # workspace / isolation
2
-
3
- ## 作用
4
-
5
- 工作区隔离负责避免并行任务互相污染。
6
-
7
- ## 当前能力
8
-
9
- - allowed roots
10
- - task 与 worktree 绑定
11
- - worktree 创建、保留、删除
12
-
13
- ## 规则
14
-
15
- 1. 共享根目录适合读和轻量改动。
16
- 2. 并行改动优先进入独立 worktree。
17
- 3. task 是逻辑隔离,worktree 是目录隔离,两者不能混为一谈。
18
-
19
- ## 下一阶段要求
20
-
21
- 总指挥层需要会判断:
22
-
23
- - 什么时候共享目录就够了
24
- - 什么时候必须派到独立 worktree