@0xmaxma/claude-gateway 1.6.8 → 1.7.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 (223) hide show
  1. package/README.md +149 -20
  2. package/config.template.json +59 -1
  3. package/dist/agent/dreaming/accept.d.ts +43 -0
  4. package/dist/agent/dreaming/accept.d.ts.map +1 -0
  5. package/dist/agent/dreaming/accept.js +230 -0
  6. package/dist/agent/dreaming/accept.js.map +1 -0
  7. package/dist/agent/dreaming/applier.d.ts +77 -0
  8. package/dist/agent/dreaming/applier.d.ts.map +1 -0
  9. package/dist/agent/dreaming/applier.js +406 -0
  10. package/dist/agent/dreaming/applier.js.map +1 -0
  11. package/dist/agent/dreaming/audit.d.ts +27 -0
  12. package/dist/agent/dreaming/audit.d.ts.map +1 -0
  13. package/dist/agent/dreaming/audit.js +100 -0
  14. package/dist/agent/dreaming/audit.js.map +1 -0
  15. package/dist/agent/dreaming/compactor.d.ts +66 -0
  16. package/dist/agent/dreaming/compactor.d.ts.map +1 -0
  17. package/dist/agent/dreaming/compactor.js +306 -0
  18. package/dist/agent/dreaming/compactor.js.map +1 -0
  19. package/dist/agent/dreaming/config.d.ts +19 -0
  20. package/dist/agent/dreaming/config.d.ts.map +1 -0
  21. package/dist/agent/dreaming/config.js +107 -0
  22. package/dist/agent/dreaming/config.js.map +1 -0
  23. package/dist/agent/dreaming/episodic.d.ts +42 -0
  24. package/dist/agent/dreaming/episodic.d.ts.map +1 -0
  25. package/dist/agent/dreaming/episodic.js +116 -0
  26. package/dist/agent/dreaming/episodic.js.map +1 -0
  27. package/dist/agent/dreaming/gather.d.ts +31 -0
  28. package/dist/agent/dreaming/gather.d.ts.map +1 -0
  29. package/dist/agent/dreaming/gather.js +55 -0
  30. package/dist/agent/dreaming/gather.js.map +1 -0
  31. package/dist/agent/dreaming/index.d.ts +87 -0
  32. package/dist/agent/dreaming/index.d.ts.map +1 -0
  33. package/dist/agent/dreaming/index.js +377 -0
  34. package/dist/agent/dreaming/index.js.map +1 -0
  35. package/dist/agent/dreaming/migrate-cli.d.ts +17 -0
  36. package/dist/agent/dreaming/migrate-cli.d.ts.map +1 -0
  37. package/dist/agent/dreaming/migrate-cli.js +42 -0
  38. package/dist/agent/dreaming/migrate-cli.js.map +1 -0
  39. package/dist/agent/dreaming/migrate.d.ts +72 -0
  40. package/dist/agent/dreaming/migrate.d.ts.map +1 -0
  41. package/dist/agent/dreaming/migrate.js +374 -0
  42. package/dist/agent/dreaming/migrate.js.map +1 -0
  43. package/dist/agent/dreaming/report.d.ts +39 -0
  44. package/dist/agent/dreaming/report.d.ts.map +1 -0
  45. package/dist/agent/dreaming/report.js +121 -0
  46. package/dist/agent/dreaming/report.js.map +1 -0
  47. package/dist/agent/dreaming/reviewer.d.ts +43 -0
  48. package/dist/agent/dreaming/reviewer.d.ts.map +1 -0
  49. package/dist/agent/dreaming/reviewer.js +249 -0
  50. package/dist/agent/dreaming/reviewer.js.map +1 -0
  51. package/dist/agent/dreaming/staleness.d.ts +68 -0
  52. package/dist/agent/dreaming/staleness.d.ts.map +1 -0
  53. package/dist/agent/dreaming/staleness.js +281 -0
  54. package/dist/agent/dreaming/staleness.js.map +1 -0
  55. package/dist/agent/dreaming/types.d.ts +130 -0
  56. package/dist/agent/dreaming/types.d.ts.map +1 -0
  57. package/dist/agent/dreaming/types.js +10 -0
  58. package/dist/agent/dreaming/types.js.map +1 -0
  59. package/dist/agent/knowledge/archive-db.d.ts +114 -0
  60. package/dist/agent/knowledge/archive-db.d.ts.map +1 -0
  61. package/dist/agent/knowledge/archive-db.js +474 -0
  62. package/dist/agent/knowledge/archive-db.js.map +1 -0
  63. package/dist/agent/knowledge/chunk.d.ts +21 -0
  64. package/dist/agent/knowledge/chunk.d.ts.map +1 -0
  65. package/dist/agent/knowledge/chunk.js +67 -0
  66. package/dist/agent/knowledge/chunk.js.map +1 -0
  67. package/dist/agent/knowledge/config.d.ts +20 -0
  68. package/dist/agent/knowledge/config.d.ts.map +1 -0
  69. package/dist/agent/knowledge/config.js +143 -0
  70. package/dist/agent/knowledge/config.js.map +1 -0
  71. package/dist/agent/knowledge/graph-demo.d.ts +25 -0
  72. package/dist/agent/knowledge/graph-demo.d.ts.map +1 -0
  73. package/dist/agent/knowledge/graph-demo.js +132 -0
  74. package/dist/agent/knowledge/graph-demo.js.map +1 -0
  75. package/dist/agent/knowledge/index.d.ts +24 -0
  76. package/dist/agent/knowledge/index.d.ts.map +1 -0
  77. package/dist/agent/knowledge/index.js +50 -0
  78. package/dist/agent/knowledge/index.js.map +1 -0
  79. package/dist/agent/knowledge/indexer.d.ts +34 -0
  80. package/dist/agent/knowledge/indexer.d.ts.map +1 -0
  81. package/dist/agent/knowledge/indexer.js +271 -0
  82. package/dist/agent/knowledge/indexer.js.map +1 -0
  83. package/dist/agent/knowledge/lifecycle.d.ts +91 -0
  84. package/dist/agent/knowledge/lifecycle.d.ts.map +1 -0
  85. package/dist/agent/knowledge/lifecycle.js +205 -0
  86. package/dist/agent/knowledge/lifecycle.js.map +1 -0
  87. package/dist/agent/knowledge/provenance.d.ts +15 -0
  88. package/dist/agent/knowledge/provenance.d.ts.map +1 -0
  89. package/dist/agent/knowledge/provenance.js +37 -0
  90. package/dist/agent/knowledge/provenance.js.map +1 -0
  91. package/dist/agent/knowledge/reindex-cli.d.ts +13 -0
  92. package/dist/agent/knowledge/reindex-cli.d.ts.map +1 -0
  93. package/dist/agent/knowledge/reindex-cli.js +53 -0
  94. package/dist/agent/knowledge/reindex-cli.js.map +1 -0
  95. package/dist/agent/knowledge/reindex-spawn.d.ts +30 -0
  96. package/dist/agent/knowledge/reindex-spawn.d.ts.map +1 -0
  97. package/dist/agent/knowledge/reindex-spawn.js +97 -0
  98. package/dist/agent/knowledge/reindex-spawn.js.map +1 -0
  99. package/dist/agent/knowledge/shared-promote.d.ts +18 -0
  100. package/dist/agent/knowledge/shared-promote.d.ts.map +1 -0
  101. package/dist/agent/knowledge/shared-promote.js +29 -0
  102. package/dist/agent/knowledge/shared-promote.js.map +1 -0
  103. package/dist/agent/knowledge/shared-writer.d.ts +24 -0
  104. package/dist/agent/knowledge/shared-writer.d.ts.map +1 -0
  105. package/dist/agent/knowledge/shared-writer.js +97 -0
  106. package/dist/agent/knowledge/shared-writer.js.map +1 -0
  107. package/dist/agent/knowledge/types.d.ts +83 -0
  108. package/dist/agent/knowledge/types.d.ts.map +1 -0
  109. package/dist/agent/knowledge/types.js +12 -0
  110. package/dist/agent/knowledge/types.js.map +1 -0
  111. package/dist/agent/knowledge/wiki.d.ts +107 -0
  112. package/dist/agent/knowledge/wiki.d.ts.map +1 -0
  113. package/dist/agent/knowledge/wiki.js +415 -0
  114. package/dist/agent/knowledge/wiki.js.map +1 -0
  115. package/dist/agent/memory-metrics.d.ts +48 -0
  116. package/dist/agent/memory-metrics.d.ts.map +1 -0
  117. package/dist/agent/memory-metrics.js +156 -0
  118. package/dist/agent/memory-metrics.js.map +1 -0
  119. package/dist/agent/runner.d.ts +24 -1
  120. package/dist/agent/runner.d.ts.map +1 -1
  121. package/dist/agent/runner.js +82 -6
  122. package/dist/agent/runner.js.map +1 -1
  123. package/dist/agent/skill-learning/config.d.ts +18 -0
  124. package/dist/agent/skill-learning/config.d.ts.map +1 -0
  125. package/dist/agent/skill-learning/config.js +71 -0
  126. package/dist/agent/skill-learning/config.js.map +1 -0
  127. package/dist/agent/skill-learning/curator.d.ts +45 -0
  128. package/dist/agent/skill-learning/curator.d.ts.map +1 -0
  129. package/dist/agent/skill-learning/curator.js +171 -0
  130. package/dist/agent/skill-learning/curator.js.map +1 -0
  131. package/dist/agent/skill-learning/index.d.ts +79 -0
  132. package/dist/agent/skill-learning/index.d.ts.map +1 -0
  133. package/dist/agent/skill-learning/index.js +308 -0
  134. package/dist/agent/skill-learning/index.js.map +1 -0
  135. package/dist/agent/skill-learning/metrics.d.ts +13 -0
  136. package/dist/agent/skill-learning/metrics.d.ts.map +1 -0
  137. package/dist/agent/skill-learning/metrics.js +109 -0
  138. package/dist/agent/skill-learning/metrics.js.map +1 -0
  139. package/dist/agent/skill-learning/notifier.d.ts +111 -0
  140. package/dist/agent/skill-learning/notifier.d.ts.map +1 -0
  141. package/dist/agent/skill-learning/notifier.js +398 -0
  142. package/dist/agent/skill-learning/notifier.js.map +1 -0
  143. package/dist/agent/skill-learning/reviewer.d.ts +61 -0
  144. package/dist/agent/skill-learning/reviewer.d.ts.map +1 -0
  145. package/dist/agent/skill-learning/reviewer.js +271 -0
  146. package/dist/agent/skill-learning/reviewer.js.map +1 -0
  147. package/dist/agent/skill-learning/telemetry.d.ts +22 -0
  148. package/dist/agent/skill-learning/telemetry.d.ts.map +1 -0
  149. package/dist/agent/skill-learning/telemetry.js +90 -0
  150. package/dist/agent/skill-learning/telemetry.js.map +1 -0
  151. package/dist/agent/skill-learning/trigger.d.ts +23 -0
  152. package/dist/agent/skill-learning/trigger.d.ts.map +1 -0
  153. package/dist/agent/skill-learning/trigger.js +44 -0
  154. package/dist/agent/skill-learning/trigger.js.map +1 -0
  155. package/dist/agent/skill-learning/types.d.ts +165 -0
  156. package/dist/agent/skill-learning/types.d.ts.map +1 -0
  157. package/dist/agent/skill-learning/types.js +11 -0
  158. package/dist/agent/skill-learning/types.js.map +1 -0
  159. package/dist/agent/skill-learning/writer.d.ts +37 -0
  160. package/dist/agent/skill-learning/writer.d.ts.map +1 -0
  161. package/dist/agent/skill-learning/writer.js +223 -0
  162. package/dist/agent/skill-learning/writer.js.map +1 -0
  163. package/dist/agent/workspace-loader.d.ts +83 -6
  164. package/dist/agent/workspace-loader.d.ts.map +1 -1
  165. package/dist/agent/workspace-loader.js +207 -14
  166. package/dist/agent/workspace-loader.js.map +1 -1
  167. package/dist/api/gateway-router.d.ts +2 -0
  168. package/dist/api/gateway-router.d.ts.map +1 -1
  169. package/dist/api/gateway-router.js +293 -6
  170. package/dist/api/gateway-router.js.map +1 -1
  171. package/dist/api/share-router.d.ts.map +1 -1
  172. package/dist/api/share-router.js +27 -1
  173. package/dist/api/share-router.js.map +1 -1
  174. package/dist/api/skills-router.d.ts +2 -2
  175. package/dist/api/skills-router.d.ts.map +1 -1
  176. package/dist/api/skills-router.js +61 -1
  177. package/dist/api/skills-router.js.map +1 -1
  178. package/dist/apps/agent-manager.d.ts +7 -0
  179. package/dist/apps/agent-manager.d.ts.map +1 -1
  180. package/dist/apps/agent-manager.js +13 -1
  181. package/dist/apps/agent-manager.js.map +1 -1
  182. package/dist/config/migrator.d.ts +2 -0
  183. package/dist/config/migrator.d.ts.map +1 -1
  184. package/dist/config/migrator.js +72 -1
  185. package/dist/config/migrator.js.map +1 -1
  186. package/dist/history/cleanup.d.ts +7 -0
  187. package/dist/history/cleanup.d.ts.map +1 -1
  188. package/dist/history/cleanup.js +27 -0
  189. package/dist/history/cleanup.js.map +1 -1
  190. package/dist/history/db.d.ts +65 -0
  191. package/dist/history/db.d.ts.map +1 -1
  192. package/dist/history/db.js +261 -0
  193. package/dist/history/db.js.map +1 -1
  194. package/dist/index.js +122 -20
  195. package/dist/index.js.map +1 -1
  196. package/dist/session/process.d.ts +10 -0
  197. package/dist/session/process.d.ts.map +1 -1
  198. package/dist/session/process.js +86 -1
  199. package/dist/session/process.js.map +1 -1
  200. package/dist/share/share-store.d.ts +11 -0
  201. package/dist/share/share-store.d.ts.map +1 -1
  202. package/dist/share/share-store.js +3 -1
  203. package/dist/share/share-store.js.map +1 -1
  204. package/dist/skills/loader.d.ts +8 -0
  205. package/dist/skills/loader.d.ts.map +1 -1
  206. package/dist/skills/loader.js +29 -1
  207. package/dist/skills/loader.js.map +1 -1
  208. package/dist/types.d.ts +116 -0
  209. package/dist/types.d.ts.map +1 -1
  210. package/dist/ui/web-ui.d.ts.map +1 -1
  211. package/dist/ui/web-ui.js +1126 -3
  212. package/dist/ui/web-ui.js.map +1 -1
  213. package/mcp/package.json +2 -1
  214. package/mcp/server.ts +2 -0
  215. package/mcp/tools/image/module.ts +114 -9
  216. package/mcp/tools/memory/archive-reader.test.ts +212 -0
  217. package/mcp/tools/memory/archive-reader.ts +253 -0
  218. package/mcp/tools/memory/module.ts +152 -0
  219. package/mcp/tools/memory/retrieval-recorder.ts +66 -0
  220. package/mcp/tools/shared/share-client.ts +20 -1
  221. package/mcp/tools/skills/module.ts +28 -0
  222. package/package.json +1 -1
  223. package/resource/claude_gateway.svg +1038 -112
@@ -0,0 +1,398 @@
1
+ "use strict";
2
+ /**
3
+ * Skill notifier — tells the operator when the closed loop writes a skill.
4
+ *
5
+ * Two independent surfaces (per user request, 2026-08-17):
6
+ * 1. **Diary** (`SKILLS_LEARNED.md` in the workspace) — ALWAYS appended, an
7
+ * immutable audit log of every auto-write. Cheap, offline, never throttled.
8
+ * 2. **Channel push** (Telegram) — a short "learned a skill" ping, gated by
9
+ * `cfg.notify`. Throttled: up to NOTIFY_THROTTLE_MAX immediate pings per
10
+ * rolling window, then further writes coalesce into one digest so a burst
11
+ * cannot spam the chat.
12
+ *
13
+ * Everything here is best-effort and MUST NOT throw into the review path.
14
+ * The channel transport (`send`) is injected — the manager builds the real
15
+ * Telegram sender; tests pass a capturing stub. Diary + throttle logic are
16
+ * transport-agnostic and fully unit-testable with an injected clock.
17
+ */
18
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ var desc = Object.getOwnPropertyDescriptor(m, k);
21
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
22
+ desc = { enumerable: true, get: function() { return m[k]; } };
23
+ }
24
+ Object.defineProperty(o, k2, desc);
25
+ }) : (function(o, m, k, k2) {
26
+ if (k2 === undefined) k2 = k;
27
+ o[k2] = m[k];
28
+ }));
29
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
30
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
31
+ }) : function(o, v) {
32
+ o["default"] = v;
33
+ });
34
+ var __importStar = (this && this.__importStar) || (function () {
35
+ var ownKeys = function(o) {
36
+ ownKeys = Object.getOwnPropertyNames || function (o) {
37
+ var ar = [];
38
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
39
+ return ar;
40
+ };
41
+ return ownKeys(o);
42
+ };
43
+ return function (mod) {
44
+ if (mod && mod.__esModule) return mod;
45
+ var result = {};
46
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
47
+ __setModuleDefault(result, mod);
48
+ return result;
49
+ };
50
+ })();
51
+ Object.defineProperty(exports, "__esModule", { value: true });
52
+ exports.SkillNotifier = exports.NOTIFY_THROTTLE_MAX = exports.NOTIFY_THROTTLE_WINDOW_MS = exports.DIARY_FILENAME = void 0;
53
+ exports.readTelegramRecipients = readTelegramRecipients;
54
+ exports.pushTelegram = pushTelegram;
55
+ exports.buildTelegramSend = buildTelegramSend;
56
+ exports.readDiscordRecipients = readDiscordRecipients;
57
+ exports.pushDiscordDM = pushDiscordDM;
58
+ exports.buildDiscordSend = buildDiscordSend;
59
+ exports.readLineRecipients = readLineRecipients;
60
+ exports.pushLine = pushLine;
61
+ exports.buildLineSend = buildLineSend;
62
+ exports.buildChannelSend = buildChannelSend;
63
+ const fs = __importStar(require("fs"));
64
+ const path = __importStar(require("path"));
65
+ exports.DIARY_FILENAME = 'SKILLS_LEARNED.md';
66
+ /** Rolling window for the burst throttle. */
67
+ exports.NOTIFY_THROTTLE_WINDOW_MS = 10 * 60000; // 10 minutes
68
+ /** Immediate pings allowed per window before writes coalesce into a digest. */
69
+ exports.NOTIFY_THROTTLE_MAX = 3;
70
+ const TELEGRAM_API_BASE = 'https://api.telegram.org';
71
+ /**
72
+ * Read the Telegram allowlist for a workspace — the chat_ids authorized to talk
73
+ * to this agent, which are exactly the right recipients for its notifications
74
+ * (Telegram DM chat_id == user_id). Best-effort: any error ⇒ no recipients.
75
+ */
76
+ function readTelegramRecipients(workspaceDir) {
77
+ return readAllowFrom(workspaceDir, '.telegram-state');
78
+ }
79
+ /**
80
+ * Throw a descriptive error when an HTTP push failed, so the per-recipient
81
+ * `catch` in the transport logs a real status (an HTTP 401/429 is NOT a fetch
82
+ * throw). The response body is consumed either way (avoids leaking the undici
83
+ * stream). Bodies never contain the bot token (it lives in the URL path, not
84
+ * the payload), so this is safe to log.
85
+ */
86
+ async function ensureOk(resp, channel) {
87
+ if (resp.ok) {
88
+ await resp.text().catch(() => '');
89
+ return;
90
+ }
91
+ const body = await resp.text().catch(() => '');
92
+ throw new Error(`${channel} HTTP ${resp.status}${body ? ` ${body.slice(0, 200)}` : ''}`);
93
+ }
94
+ /** POST a Telegram sendMessage. Throws on HTTP failure so the caller can log it. */
95
+ async function pushTelegram(botToken, chatId, text) {
96
+ const resp = await fetch(`${TELEGRAM_API_BASE}/bot${botToken}/sendMessage`, {
97
+ method: 'POST',
98
+ headers: { 'Content-Type': 'application/json' },
99
+ body: JSON.stringify({ chat_id: chatId, text }),
100
+ signal: AbortSignal.timeout(PUSH_TIMEOUT_MS),
101
+ });
102
+ await ensureOk(resp, 'telegram');
103
+ }
104
+ /**
105
+ * Build the channel `send` transport for an agent from its Telegram bot token.
106
+ * Returns `undefined` when no token is configured (diary-only). The returned fn
107
+ * resolves recipients fresh on each call (allowlist may change at runtime) and
108
+ * fans out best-effort — one failed recipient never blocks the others.
109
+ */
110
+ function buildTelegramSend(workspaceDir, botToken, logger) {
111
+ if (!botToken)
112
+ return undefined;
113
+ return async (text) => {
114
+ const recipients = readTelegramRecipients(workspaceDir);
115
+ for (const chatId of recipients) {
116
+ try {
117
+ await pushTelegram(botToken, chatId, text);
118
+ }
119
+ catch (err) {
120
+ logger?.warn?.(`[skill-learning] telegram notify failed for ${chatId}: ${err.message}`);
121
+ }
122
+ }
123
+ };
124
+ }
125
+ // ---------------------------------------------------------------------------
126
+ // Multi-channel transport registry
127
+ // ---------------------------------------------------------------------------
128
+ //
129
+ // The notifier is channel-agnostic: it calls one `send(text)` that fans out to
130
+ // every channel the agent has configured. Each channel is a small factory that
131
+ // returns a `SendFn` (or `undefined` when the agent has no token for it). To add
132
+ // a channel, implement its `build<Channel>Send` and register it in
133
+ // TRANSPORT_REGISTRY — nothing else (SkillNotifier, the manager) changes.
134
+ //
135
+ // Recipient resolution is a per-channel concern: Telegram/Discord/Line each read
136
+ // their own `.<channel>-state/access.json` `allowFrom` allowlist (the owners
137
+ // authorized to talk to the agent — exactly who should get its notifications).
138
+ const DISCORD_API_BASE = 'https://discord.com/api/v10';
139
+ const DISCORD_MAX_MESSAGE_LENGTH = 2000;
140
+ const LINE_API_BASE = 'https://api.line.me';
141
+ const LINE_MAX_MESSAGE_LENGTH = 5000; // LINE hard-limits a text message to 5000 chars.
142
+ const PUSH_TIMEOUT_MS = 10000;
143
+ /**
144
+ * Read an `allowFrom` string[] from `<workspace>/<stateDir>/access.json`.
145
+ * Shared shape across Telegram/Discord/LINE. Best-effort: any error ⇒ [].
146
+ */
147
+ function readAllowFrom(workspaceDir, stateDir) {
148
+ try {
149
+ const accessPath = path.join(workspaceDir, stateDir, 'access.json');
150
+ const parsed = JSON.parse(fs.readFileSync(accessPath, 'utf-8'));
151
+ if (!Array.isArray(parsed.allowFrom))
152
+ return [];
153
+ return parsed.allowFrom.filter((x) => typeof x === 'string' && x.length > 0);
154
+ }
155
+ catch {
156
+ return [];
157
+ }
158
+ }
159
+ /** Split text into chunks no longer than `limit`, preferring paragraph/line/space breaks. */
160
+ function chunkText(text, limit) {
161
+ if (text.length <= limit)
162
+ return text.length === 0 ? [] : [text];
163
+ const chunks = [];
164
+ let rest = text;
165
+ while (rest.length > limit) {
166
+ const para = rest.lastIndexOf('\n\n', limit);
167
+ const nl = rest.lastIndexOf('\n', limit);
168
+ const sp = rest.lastIndexOf(' ', limit);
169
+ const cut = para > limit / 2 ? para : nl > limit / 2 ? nl : sp > 0 ? sp : limit;
170
+ chunks.push(rest.slice(0, cut));
171
+ rest = rest.slice(cut).replace(/^\n+/, '');
172
+ }
173
+ if (rest)
174
+ chunks.push(rest);
175
+ return chunks;
176
+ }
177
+ // ---- Discord ---------------------------------------------------------------
178
+ /** Discord recipients: user_ids from `.discord-state/access.json` `allowFrom`. */
179
+ function readDiscordRecipients(workspaceDir) {
180
+ return readAllowFrom(workspaceDir, '.discord-state');
181
+ }
182
+ /**
183
+ * Open (or fetch the existing) DM channel with a user and return its channel id.
184
+ * Discord push targets a channel, not a user — a proactive DM needs this hop
185
+ * first (unlike Telegram where chat_id == user_id). Returns undefined on failure.
186
+ */
187
+ async function openDiscordDmChannel(botToken, userId) {
188
+ const resp = await fetch(`${DISCORD_API_BASE}/users/@me/channels`, {
189
+ method: 'POST',
190
+ headers: { 'Content-Type': 'application/json', Authorization: `Bot ${botToken}` },
191
+ body: JSON.stringify({ recipient_id: userId }),
192
+ signal: AbortSignal.timeout(PUSH_TIMEOUT_MS),
193
+ });
194
+ await ensureOk(resp, 'discord dm-open');
195
+ const ch = (await resp.json());
196
+ if (typeof ch.id !== 'string')
197
+ throw new Error('discord dm-open: response had no channel id');
198
+ return ch.id;
199
+ }
200
+ /** DM a Discord user (open DM channel, then post chunked). Throws on HTTP failure. */
201
+ async function pushDiscordDM(botToken, userId, text) {
202
+ const channelId = await openDiscordDmChannel(botToken, userId);
203
+ for (const content of chunkText(text, DISCORD_MAX_MESSAGE_LENGTH)) {
204
+ const resp = await fetch(`${DISCORD_API_BASE}/channels/${channelId}/messages`, {
205
+ method: 'POST',
206
+ headers: { 'Content-Type': 'application/json', Authorization: `Bot ${botToken}` },
207
+ body: JSON.stringify({ content }),
208
+ signal: AbortSignal.timeout(PUSH_TIMEOUT_MS),
209
+ });
210
+ await ensureOk(resp, 'discord');
211
+ }
212
+ }
213
+ /** Build the Discord `send` transport. undefined when no bot token is configured. */
214
+ function buildDiscordSend(workspaceDir, botToken, logger) {
215
+ if (!botToken)
216
+ return undefined;
217
+ return async (text) => {
218
+ for (const userId of readDiscordRecipients(workspaceDir)) {
219
+ try {
220
+ await pushDiscordDM(botToken, userId, text);
221
+ }
222
+ catch (err) {
223
+ logger?.warn?.(`[skill-learning] discord notify failed for ${userId}: ${err.message}`);
224
+ }
225
+ }
226
+ };
227
+ }
228
+ // ---- LINE ------------------------------------------------------------------
229
+ /** LINE recipients: user_ids from `.line-state/access.json` `allowFrom`. */
230
+ function readLineRecipients(workspaceDir) {
231
+ return readAllowFrom(workspaceDir, '.line-state');
232
+ }
233
+ /** Push a LINE text message to a user. Throws on HTTP failure so the caller can log it. */
234
+ async function pushLine(accessToken, to, text) {
235
+ const resp = await fetch(`${LINE_API_BASE}/v2/bot/message/push`, {
236
+ method: 'POST',
237
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}` },
238
+ body: JSON.stringify({ to, messages: [{ type: 'text', text: text.slice(0, LINE_MAX_MESSAGE_LENGTH) }] }),
239
+ signal: AbortSignal.timeout(PUSH_TIMEOUT_MS),
240
+ });
241
+ await ensureOk(resp, 'line');
242
+ }
243
+ /**
244
+ * Build the LINE `send` transport. undefined when no channel access token is
245
+ * configured — so LINE stays dormant until an agent has LINE set up, at which
246
+ * point it self-activates with no code change ("just open it": the token flows
247
+ * in from AgentConfig.line.channelAccessToken at boot).
248
+ */
249
+ function buildLineSend(workspaceDir, accessToken, logger) {
250
+ if (!accessToken)
251
+ return undefined;
252
+ return async (text) => {
253
+ for (const to of readLineRecipients(workspaceDir)) {
254
+ try {
255
+ await pushLine(accessToken, to, text);
256
+ }
257
+ catch (err) {
258
+ logger?.warn?.(`[skill-learning] line notify failed for ${to}: ${err.message}`);
259
+ }
260
+ }
261
+ };
262
+ }
263
+ /**
264
+ * Registered channel transports. Add a channel by appending one entry — the
265
+ * notifier and manager need no changes.
266
+ *
267
+ * NOT registered: the web/`api` channel is request/response only and has no
268
+ * proactive push transport (no SSE/WS/queue for agent-initiated messages).
269
+ * Wiring it needs new delivery infrastructure — tracked as a separate issue.
270
+ */
271
+ const TRANSPORT_REGISTRY = [
272
+ (ws, t, log) => buildTelegramSend(ws, t.telegramBotToken, log),
273
+ (ws, t, log) => buildDiscordSend(ws, t.discordBotToken, log),
274
+ (ws, t, log) => buildLineSend(ws, t.lineAccessToken, log),
275
+ ];
276
+ /**
277
+ * Build a single fan-out `send` across every configured channel transport.
278
+ * Returns undefined when the agent has no push channel wired (diary-only).
279
+ * Fan-out is best-effort and concurrent — one channel failing never blocks the
280
+ * others, and no rejection escapes into the review path.
281
+ */
282
+ function buildChannelSend(workspaceDir, tokens, logger) {
283
+ const sends = TRANSPORT_REGISTRY.map((build) => build(workspaceDir, tokens, logger)).filter((s) => Boolean(s));
284
+ if (sends.length === 0)
285
+ return undefined;
286
+ return async (text) => {
287
+ await Promise.all(sends.map(async (send) => {
288
+ try {
289
+ await send(text);
290
+ }
291
+ catch (err) {
292
+ logger?.warn?.(`[skill-learning] channel notify failed: ${err.message}`);
293
+ }
294
+ }));
295
+ };
296
+ }
297
+ class SkillNotifier {
298
+ constructor(opts) {
299
+ /** Timestamps of immediate pings still inside the current window. */
300
+ this.recent = [];
301
+ /** Coalesced writes awaiting a digest flush. */
302
+ this.buffer = [];
303
+ this.workspaceDir = opts.workspaceDir;
304
+ this.notify = opts.notify;
305
+ this.send = opts.send;
306
+ this.logger = opts.logger;
307
+ }
308
+ /**
309
+ * Record a live skill write. Appends the diary (always) and, if channel push
310
+ * is on, either pings immediately or buffers for a digest. Never throws.
311
+ */
312
+ onSkillWritten(ev) {
313
+ this.appendDiary(ev);
314
+ if (!this.notify || !this.send)
315
+ return;
316
+ try {
317
+ this.pushThrottled(ev);
318
+ }
319
+ catch (err) {
320
+ this.logger?.warn?.(`[skill-learning] notify failed: ${err.message}`);
321
+ }
322
+ }
323
+ /** Emit any buffered digest now (shutdown / test seam / timer callback). */
324
+ flushPending() {
325
+ if (this.flushTimer) {
326
+ clearTimeout(this.flushTimer);
327
+ this.flushTimer = undefined;
328
+ }
329
+ const n = this.buffer.length;
330
+ if (n > 0 && this.send) {
331
+ const list = this.buffer.join(', ');
332
+ this.buffer = [];
333
+ void this.safeSend(`🧠 ${n} more skill${n === 1 ? '' : 's'} learned (auto): ${list}`);
334
+ }
335
+ // Do NOT reset `recent` here: pushThrottled already rolls the window forward
336
+ // by timestamp on every write, so wiping it would let a mid-life flush (or a
337
+ // shutdown flush) incorrectly re-open the immediate-ping budget.
338
+ }
339
+ // ---- internals -------------------------------------------------------------
340
+ pushThrottled(ev) {
341
+ const now = ev.now;
342
+ // Roll the window forward.
343
+ this.recent = this.recent.filter((t) => now - t < exports.NOTIFY_THROTTLE_WINDOW_MS);
344
+ const verb = ev.action === 'create' ? 'created' : 'updated';
345
+ if (this.recent.length < exports.NOTIFY_THROTTLE_MAX) {
346
+ this.recent.push(now);
347
+ void this.safeSend(`🧠 Skill ${verb} (auto): ${ev.name} · session ${shortId(ev.sessionId)} · /skill-metrics`);
348
+ }
349
+ else {
350
+ // Over the burst limit — coalesce into a digest.
351
+ this.buffer.push(`${verb} ${ev.name}`);
352
+ this.armFlush(now);
353
+ }
354
+ }
355
+ armFlush(now) {
356
+ if (this.flushTimer)
357
+ return;
358
+ const oldest = this.recent[0] ?? now;
359
+ const delay = Math.max(0, exports.NOTIFY_THROTTLE_WINDOW_MS - (now - oldest));
360
+ this.flushTimer = setTimeout(() => this.flushPending(), delay);
361
+ if (typeof this.flushTimer.unref === 'function') {
362
+ this.flushTimer.unref();
363
+ }
364
+ }
365
+ async safeSend(text) {
366
+ if (!this.send)
367
+ return;
368
+ try {
369
+ await this.send(text);
370
+ }
371
+ catch (err) {
372
+ this.logger?.warn?.(`[skill-learning] notify send failed: ${err.message}`);
373
+ }
374
+ }
375
+ appendDiary(ev) {
376
+ try {
377
+ const file = path.join(this.workspaceDir, exports.DIARY_FILENAME);
378
+ const iso = new Date(ev.now).toISOString();
379
+ let prefix = '';
380
+ if (!fs.existsSync(file)) {
381
+ prefix =
382
+ '# Skills Learned (auto)\n\n' +
383
+ 'Audit log of skills the self-improvement loop wrote automatically. ' +
384
+ 'One line per write; newest at the bottom.\n\n';
385
+ }
386
+ const line = `- ${iso} — **${ev.action}** \`${ev.name}\` (session ${shortId(ev.sessionId)})\n`;
387
+ fs.appendFileSync(file, prefix + line, 'utf-8');
388
+ }
389
+ catch (err) {
390
+ this.logger?.warn?.(`[skill-learning] diary append failed: ${err.message}`);
391
+ }
392
+ }
393
+ }
394
+ exports.SkillNotifier = SkillNotifier;
395
+ function shortId(sessionId) {
396
+ return sessionId.length > 8 ? sessionId.slice(0, 8) : sessionId;
397
+ }
398
+ //# sourceMappingURL=notifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifier.js","sourceRoot":"","sources":["../../../src/agent/skill-learning/notifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Background reviewer — turns a finished transcript into a skill *proposal*.
3
+ *
4
+ * Safety-critical properties (planning-62, deviation #2 + #3):
5
+ * - print-only: `claude -p`, NO tools, NO `--dangerously-skip-permissions`.
6
+ * The transcript is untrusted input (prompt-injection surface); the model
7
+ * can only emit a JSON proposal — the gateway (writer.ts), not the model,
8
+ * writes any file.
9
+ * - async spawn only (mirror `recoveryTriageSpawn`); NEVER `spawnSync`
10
+ * (freezes the daemon event loop).
11
+ *
12
+ * The spawn is injectable so unit/e2e tests can drive a scripted reviewer with
13
+ * no real model.
14
+ */
15
+ import type { ReviewProposal, ResolvedSkillLearningCfg } from './types';
16
+ /** Hard ceiling on a reviewer spawn — it must never wedge. */
17
+ export declare const REVIEWER_TIMEOUT_MS = 120000;
18
+ export interface ReviewerInput {
19
+ /** The finished transcript to analyze (already trimmed by the caller). */
20
+ transcript: string;
21
+ /** Existing skills the agent already has (name + description), for dedup/priority. */
22
+ existingSkills: Array<{
23
+ name: string;
24
+ description: string;
25
+ }>;
26
+ }
27
+ export interface ReviewerResult {
28
+ proposal: ReviewProposal;
29
+ /** Tokens the reviewer spent (input+output), for the net-token ledger. 0 if unknown. */
30
+ tokensSpent: number;
31
+ timedOut?: boolean;
32
+ }
33
+ /**
34
+ * Injectable spawn: given argv + stdin, resolve with the process stdout.
35
+ *
36
+ * `failureReason` / `stderrTail` are diagnostics (#353): previously every failure
37
+ * mode collapsed into a bare `timedOut` and the child's stderr was discarded, so
38
+ * an `outcome=error` dream run was undebuggable. These optional fields let a caller
39
+ * log WHY a spawn failed (timeout vs spawn-error vs non-zero exit + the API error on
40
+ * stderr) without changing the success contract — consumers that only read
41
+ * `{ stdout, timedOut }` are unaffected.
42
+ */
43
+ export type ClaudeSpawnFn = (args: string[], stdin: string) => Promise<{
44
+ stdout: string;
45
+ timedOut?: boolean;
46
+ failureReason?: string;
47
+ stderrTail?: string;
48
+ }>;
49
+ /**
50
+ * Default async claude spawn — print-only, structured (`--output-format json`
51
+ * so we also get token usage), cheap model. Mirrors `recoveryTriageSpawn` but
52
+ * adds PATH hardening from the compactor idiom. NO tools, NO skip-permissions.
53
+ */
54
+ export declare function makeClaudeSpawn(reviewModel: string): ClaudeSpawnFn;
55
+ /** Validate a parsed object into a safe ReviewProposal. Any deviation ⇒ `none`. */
56
+ export declare function coerceProposal(raw: unknown): ReviewProposal;
57
+ /**
58
+ * Run the reviewer. Never throws — any failure resolves to a `none` proposal.
59
+ */
60
+ export declare function runReviewer(input: ReviewerInput, cfg: ResolvedSkillLearningCfg, spawnFn?: ClaudeSpawnFn): Promise<ReviewerResult>;
61
+ //# sourceMappingURL=reviewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewer.d.ts","sourceRoot":"","sources":["../../../src/agent/skill-learning/reviewer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAExE,8DAA8D;AAC9D,eAAO,MAAM,mBAAmB,SAAU,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,sFAAsF;IACtF,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,CAAC;IACzB,wFAAwF;IACxF,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,MAAM,KACV,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAmClG;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAiFlE;AAuCD,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAe3D;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,wBAAwB,EAC7B,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,cAAc,CAAC,CA6BzB"}