@geminixiang/mikan 1.0.0-beta.20 → 1.0.0-beta.22

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 (326) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +18 -11
  3. package/dist/adapters/buffered-responder.d.ts +60 -0
  4. package/dist/adapters/buffered-responder.d.ts.map +1 -0
  5. package/dist/adapters/buffered-responder.js +201 -0
  6. package/dist/adapters/buffered-responder.js.map +1 -0
  7. package/dist/adapters/discord/context.d.ts.map +1 -1
  8. package/dist/adapters/discord/context.js +42 -177
  9. package/dist/adapters/discord/context.js.map +1 -1
  10. package/dist/adapters/github/bot.d.ts +14 -76
  11. package/dist/adapters/github/bot.d.ts.map +1 -1
  12. package/dist/adapters/github/bot.js +52 -443
  13. package/dist/adapters/github/bot.js.map +1 -1
  14. package/dist/adapters/github/client.d.ts +4 -0
  15. package/dist/adapters/github/client.d.ts.map +1 -1
  16. package/dist/adapters/github/client.js +5 -0
  17. package/dist/adapters/github/client.js.map +1 -1
  18. package/dist/adapters/github/context.d.ts.map +1 -1
  19. package/dist/adapters/github/context.js +32 -104
  20. package/dist/adapters/github/context.js.map +1 -1
  21. package/dist/adapters/github/github-ops.d.ts +90 -0
  22. package/dist/adapters/github/github-ops.d.ts.map +1 -0
  23. package/dist/adapters/github/github-ops.js +405 -0
  24. package/dist/adapters/github/github-ops.js.map +1 -0
  25. package/dist/adapters/github/repo.d.ts +2 -0
  26. package/dist/adapters/github/repo.d.ts.map +1 -1
  27. package/dist/adapters/github/repo.js +5 -0
  28. package/dist/adapters/github/repo.js.map +1 -1
  29. package/dist/adapters/github/tools/checks.d.ts.map +1 -1
  30. package/dist/adapters/github/tools/checks.js +6 -17
  31. package/dist/adapters/github/tools/checks.js.map +1 -1
  32. package/dist/adapters/github/tools/issue.d.ts.map +1 -1
  33. package/dist/adapters/github/tools/issue.js +6 -17
  34. package/dist/adapters/github/tools/issue.js.map +1 -1
  35. package/dist/adapters/github/tools/pr.d.ts.map +1 -1
  36. package/dist/adapters/github/tools/pr.js +6 -17
  37. package/dist/adapters/github/tools/pr.js.map +1 -1
  38. package/dist/adapters/github/tools/read.d.ts.map +1 -1
  39. package/dist/adapters/github/tools/read.js +6 -17
  40. package/dist/adapters/github/tools/read.js.map +1 -1
  41. package/dist/adapters/github/tools/review-reply.d.ts.map +1 -1
  42. package/dist/adapters/github/tools/review-reply.js +6 -17
  43. package/dist/adapters/github/tools/review-reply.js.map +1 -1
  44. package/dist/adapters/github/tools/sync.d.ts.map +1 -1
  45. package/dist/adapters/github/tools/sync.js +6 -17
  46. package/dist/adapters/github/tools/sync.js.map +1 -1
  47. package/dist/adapters/intake.d.ts.map +1 -1
  48. package/dist/adapters/intake.js +5 -0
  49. package/dist/adapters/intake.js.map +1 -1
  50. package/dist/adapters/slack/bot.d.ts.map +1 -1
  51. package/dist/adapters/slack/bot.js +14 -3
  52. package/dist/adapters/slack/bot.js.map +1 -1
  53. package/dist/adapters/slack/types.d.ts +2 -0
  54. package/dist/adapters/slack/types.d.ts.map +1 -1
  55. package/dist/adapters/slack/types.js.map +1 -1
  56. package/dist/adapters/telegram/context.d.ts.map +1 -1
  57. package/dist/adapters/telegram/context.js +33 -151
  58. package/dist/adapters/telegram/context.js.map +1 -1
  59. package/dist/agent.d.ts +0 -1
  60. package/dist/agent.d.ts.map +1 -1
  61. package/dist/agent.js +50 -43
  62. package/dist/agent.js.map +1 -1
  63. package/dist/cli/arg-grammar.d.ts +25 -0
  64. package/dist/cli/arg-grammar.d.ts.map +1 -0
  65. package/dist/cli/arg-grammar.js +59 -0
  66. package/dist/cli/arg-grammar.js.map +1 -0
  67. package/dist/cli/boot.d.ts +16 -0
  68. package/dist/cli/boot.d.ts.map +1 -0
  69. package/dist/cli/boot.js +119 -0
  70. package/dist/cli/boot.js.map +1 -0
  71. package/dist/cli/ext.js +9 -11
  72. package/dist/cli/ext.js.map +1 -1
  73. package/dist/commands/extensions.js +2 -2
  74. package/dist/commands/extensions.js.map +1 -1
  75. package/dist/commands/login.d.ts.map +1 -1
  76. package/dist/commands/login.js +5 -2
  77. package/dist/commands/login.js.map +1 -1
  78. package/dist/commands/model.d.ts.map +1 -1
  79. package/dist/commands/model.js +8 -8
  80. package/dist/commands/model.js.map +1 -1
  81. package/dist/commands/sandbox.d.ts.map +1 -1
  82. package/dist/commands/sandbox.js +7 -5
  83. package/dist/commands/sandbox.js.map +1 -1
  84. package/dist/config.d.ts +0 -2
  85. package/dist/config.d.ts.map +1 -1
  86. package/dist/config.js +8 -41
  87. package/dist/config.js.map +1 -1
  88. package/dist/env-manifest.d.ts +35 -0
  89. package/dist/env-manifest.d.ts.map +1 -0
  90. package/dist/env-manifest.js +225 -0
  91. package/dist/env-manifest.js.map +1 -0
  92. package/dist/events.d.ts +0 -10
  93. package/dist/events.d.ts.map +1 -1
  94. package/dist/events.js +9 -82
  95. package/dist/events.js.map +1 -1
  96. package/dist/execution-resolver.d.ts +5 -5
  97. package/dist/execution-resolver.d.ts.map +1 -1
  98. package/dist/execution-resolver.js +84 -118
  99. package/dist/execution-resolver.js.map +1 -1
  100. package/dist/harness/event-format.d.ts +66 -0
  101. package/dist/harness/event-format.d.ts.map +1 -0
  102. package/dist/harness/event-format.js +112 -0
  103. package/dist/harness/event-format.js.map +1 -0
  104. package/dist/harness/extensions/loader.d.ts +9 -6
  105. package/dist/harness/extensions/loader.d.ts.map +1 -1
  106. package/dist/harness/extensions/loader.js +35 -50
  107. package/dist/harness/extensions/loader.js.map +1 -1
  108. package/dist/harness/extensions/types.d.ts +10 -14
  109. package/dist/harness/extensions/types.d.ts.map +1 -1
  110. package/dist/harness/extensions/types.js.map +1 -1
  111. package/dist/harness/index.d.ts +2 -1
  112. package/dist/harness/index.d.ts.map +1 -1
  113. package/dist/harness/index.js +1 -0
  114. package/dist/harness/index.js.map +1 -1
  115. package/dist/harness/runner.d.ts +20 -0
  116. package/dist/harness/runner.d.ts.map +1 -1
  117. package/dist/harness/runner.js +42 -2
  118. package/dist/harness/runner.js.map +1 -1
  119. package/dist/harness/subagent-runner.d.ts +21 -3
  120. package/dist/harness/subagent-runner.d.ts.map +1 -1
  121. package/dist/harness/subagent-runner.js +165 -47
  122. package/dist/harness/subagent-runner.js.map +1 -1
  123. package/dist/harness/types.d.ts +11 -0
  124. package/dist/harness/types.d.ts.map +1 -1
  125. package/dist/harness/types.js.map +1 -1
  126. package/dist/index.d.ts +2 -2
  127. package/dist/index.d.ts.map +1 -1
  128. package/dist/index.js +2 -2
  129. package/dist/index.js.map +1 -1
  130. package/dist/main.js +69 -121
  131. package/dist/main.js.map +1 -1
  132. package/dist/observability/instrument.js +6 -4
  133. package/dist/observability/instrument.js.map +1 -1
  134. package/dist/observability/sentry.d.ts.map +1 -1
  135. package/dist/observability/sentry.js +3 -2
  136. package/dist/observability/sentry.js.map +1 -1
  137. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  138. package/dist/runtime/conversation-runtime.js +33 -79
  139. package/dist/runtime/conversation-runtime.js.map +1 -1
  140. package/dist/runtime/session-lifecycle.d.ts +19 -0
  141. package/dist/runtime/session-lifecycle.d.ts.map +1 -0
  142. package/dist/runtime/session-lifecycle.js +76 -0
  143. package/dist/runtime/session-lifecycle.js.map +1 -0
  144. package/dist/runtime/types.d.ts +9 -0
  145. package/dist/runtime/types.d.ts.map +1 -1
  146. package/dist/runtime/types.js.map +1 -1
  147. package/dist/sandbox/cloudflare.d.ts +1 -0
  148. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  149. package/dist/sandbox/cloudflare.js +5 -1
  150. package/dist/sandbox/cloudflare.js.map +1 -1
  151. package/dist/sandbox/container.d.ts +1 -0
  152. package/dist/sandbox/container.d.ts.map +1 -1
  153. package/dist/sandbox/container.js +5 -1
  154. package/dist/sandbox/container.js.map +1 -1
  155. package/dist/sandbox/firecracker.d.ts +1 -0
  156. package/dist/sandbox/firecracker.d.ts.map +1 -1
  157. package/dist/sandbox/firecracker.js +5 -1
  158. package/dist/sandbox/firecracker.js.map +1 -1
  159. package/dist/sandbox/gondolin-bootstrap.d.ts +26 -0
  160. package/dist/sandbox/gondolin-bootstrap.d.ts.map +1 -0
  161. package/dist/sandbox/gondolin-bootstrap.js +27 -0
  162. package/dist/sandbox/gondolin-bootstrap.js.map +1 -0
  163. package/dist/sandbox/gondolin-contract.d.ts +125 -0
  164. package/dist/sandbox/gondolin-contract.d.ts.map +1 -0
  165. package/dist/sandbox/gondolin-contract.js +155 -0
  166. package/dist/sandbox/gondolin-contract.js.map +1 -0
  167. package/dist/sandbox/gondolin-fleet.d.ts +1 -1
  168. package/dist/sandbox/gondolin-fleet.d.ts.map +1 -1
  169. package/dist/sandbox/gondolin-fleet.js +1 -1
  170. package/dist/sandbox/gondolin-fleet.js.map +1 -1
  171. package/dist/sandbox/gondolin-gateway.d.ts +1 -0
  172. package/dist/sandbox/gondolin-gateway.d.ts.map +1 -1
  173. package/dist/sandbox/gondolin-gateway.js +13 -6
  174. package/dist/sandbox/gondolin-gateway.js.map +1 -1
  175. package/dist/sandbox/gondolin-inventory.d.ts +2 -16
  176. package/dist/sandbox/gondolin-inventory.d.ts.map +1 -1
  177. package/dist/sandbox/gondolin-inventory.js +2 -12
  178. package/dist/sandbox/gondolin-inventory.js.map +1 -1
  179. package/dist/sandbox/gondolin-placement.d.ts +0 -2
  180. package/dist/sandbox/gondolin-placement.d.ts.map +1 -1
  181. package/dist/sandbox/gondolin-placement.js +0 -6
  182. package/dist/sandbox/gondolin-placement.js.map +1 -1
  183. package/dist/sandbox/gondolin-recovery.d.ts +33 -0
  184. package/dist/sandbox/gondolin-recovery.d.ts.map +1 -0
  185. package/dist/sandbox/gondolin-recovery.js +39 -0
  186. package/dist/sandbox/gondolin-recovery.js.map +1 -0
  187. package/dist/sandbox/gondolin-remote.d.ts +2 -5
  188. package/dist/sandbox/gondolin-remote.d.ts.map +1 -1
  189. package/dist/sandbox/gondolin-remote.js +12 -39
  190. package/dist/sandbox/gondolin-remote.js.map +1 -1
  191. package/dist/sandbox/gondolin-worker-client.d.ts +8 -12
  192. package/dist/sandbox/gondolin-worker-client.d.ts.map +1 -1
  193. package/dist/sandbox/gondolin-worker-client.js +13 -15
  194. package/dist/sandbox/gondolin-worker-client.js.map +1 -1
  195. package/dist/sandbox/gondolin-worker.d.ts +2 -40
  196. package/dist/sandbox/gondolin-worker.d.ts.map +1 -1
  197. package/dist/sandbox/gondolin-worker.js.map +1 -1
  198. package/dist/sandbox/gondolin.d.ts +1 -0
  199. package/dist/sandbox/gondolin.d.ts.map +1 -1
  200. package/dist/sandbox/gondolin.js +9 -4
  201. package/dist/sandbox/gondolin.js.map +1 -1
  202. package/dist/sandbox/host.d.ts +1 -0
  203. package/dist/sandbox/host.d.ts.map +1 -1
  204. package/dist/sandbox/host.js +4 -0
  205. package/dist/sandbox/host.js.map +1 -1
  206. package/dist/sandbox/identity.d.ts +11 -16
  207. package/dist/sandbox/identity.d.ts.map +1 -1
  208. package/dist/sandbox/identity.js +32 -26
  209. package/dist/sandbox/identity.js.map +1 -1
  210. package/dist/sandbox/image.d.ts.map +1 -1
  211. package/dist/sandbox/image.js +1 -0
  212. package/dist/sandbox/image.js.map +1 -1
  213. package/dist/sandbox/index.d.ts +10 -1
  214. package/dist/sandbox/index.d.ts.map +1 -1
  215. package/dist/sandbox/index.js +20 -0
  216. package/dist/sandbox/index.js.map +1 -1
  217. package/dist/sandbox/types.d.ts +10 -0
  218. package/dist/sandbox/types.d.ts.map +1 -1
  219. package/dist/sandbox/types.js.map +1 -1
  220. package/dist/sandbox/utils.d.ts +1 -0
  221. package/dist/sandbox/utils.d.ts.map +1 -1
  222. package/dist/sandbox/utils.js +5 -2
  223. package/dist/sandbox/utils.js.map +1 -1
  224. package/dist/sessions/chat-history-sync.d.ts +32 -0
  225. package/dist/sessions/chat-history-sync.d.ts.map +1 -0
  226. package/dist/sessions/{agent-memory-file-manager.js → chat-history-sync.js} +24 -83
  227. package/dist/sessions/chat-history-sync.js.map +1 -0
  228. package/dist/sessions/conversation-log.d.ts +13 -0
  229. package/dist/sessions/conversation-log.d.ts.map +1 -0
  230. package/dist/sessions/conversation-log.js +47 -0
  231. package/dist/sessions/conversation-log.js.map +1 -0
  232. package/dist/sessions/rotation.d.ts +2 -0
  233. package/dist/sessions/rotation.d.ts.map +1 -0
  234. package/dist/sessions/rotation.js +39 -0
  235. package/dist/sessions/rotation.js.map +1 -0
  236. package/dist/sessions/session-key.d.ts +9 -0
  237. package/dist/sessions/session-key.d.ts.map +1 -1
  238. package/dist/sessions/session-key.js +50 -2
  239. package/dist/sessions/session-key.js.map +1 -1
  240. package/dist/sessions/store.d.ts.map +1 -1
  241. package/dist/sessions/store.js +40 -18
  242. package/dist/sessions/store.js.map +1 -1
  243. package/dist/sessions/types.d.ts +2 -2
  244. package/dist/sessions/types.d.ts.map +1 -1
  245. package/dist/sessions/types.js.map +1 -1
  246. package/dist/settings-mutation.d.ts +23 -0
  247. package/dist/settings-mutation.d.ts.map +1 -0
  248. package/dist/settings-mutation.js +42 -0
  249. package/dist/settings-mutation.js.map +1 -0
  250. package/dist/tools/event.d.ts.map +1 -1
  251. package/dist/tools/event.js +27 -46
  252. package/dist/tools/event.js.map +1 -1
  253. package/dist/tools/host-fn-tool.d.ts +21 -0
  254. package/dist/tools/host-fn-tool.d.ts.map +1 -0
  255. package/dist/tools/host-fn-tool.js +32 -0
  256. package/dist/tools/host-fn-tool.js.map +1 -0
  257. package/dist/tools/index.d.ts +1 -0
  258. package/dist/tools/index.d.ts.map +1 -1
  259. package/dist/tools/index.js +1 -0
  260. package/dist/tools/index.js.map +1 -1
  261. package/dist/tools/react.d.ts.map +1 -1
  262. package/dist/tools/react.js +6 -17
  263. package/dist/tools/react.js.map +1 -1
  264. package/dist/tools/read.d.ts.map +1 -1
  265. package/dist/tools/read.js +7 -26
  266. package/dist/tools/read.js.map +1 -1
  267. package/dist/tools/sandbox.d.ts.map +1 -1
  268. package/dist/tools/sandbox.js +5 -2
  269. package/dist/tools/sandbox.js.map +1 -1
  270. package/dist/tools/subagent-slots.d.ts +28 -0
  271. package/dist/tools/subagent-slots.d.ts.map +1 -0
  272. package/dist/tools/subagent-slots.js +72 -0
  273. package/dist/tools/subagent-slots.js.map +1 -0
  274. package/dist/tools/subagent.d.ts +17 -1
  275. package/dist/tools/subagent.d.ts.map +1 -1
  276. package/dist/tools/subagent.js +159 -110
  277. package/dist/tools/subagent.js.map +1 -1
  278. package/dist/tools/types.d.ts +10 -9
  279. package/dist/tools/types.d.ts.map +1 -1
  280. package/dist/tools/types.js.map +1 -1
  281. package/dist/types.d.ts +12 -24
  282. package/dist/types.d.ts.map +1 -1
  283. package/dist/types.js.map +1 -1
  284. package/dist/utils/date.d.ts +1 -1
  285. package/dist/utils/date.js +1 -1
  286. package/dist/utils/date.js.map +1 -1
  287. package/dist/vault/disabled.d.ts.map +1 -1
  288. package/dist/vault/disabled.js +0 -1
  289. package/dist/vault/disabled.js.map +1 -1
  290. package/dist/vault/index.d.ts +0 -2
  291. package/dist/vault/index.d.ts.map +1 -1
  292. package/dist/vault/index.js +0 -10
  293. package/dist/vault/index.js.map +1 -1
  294. package/dist/vault/injection.d.ts +10 -0
  295. package/dist/vault/injection.d.ts.map +1 -0
  296. package/dist/vault/injection.js +30 -0
  297. package/dist/vault/injection.js.map +1 -0
  298. package/dist/vault/types.d.ts +4 -2
  299. package/dist/vault/types.d.ts.map +1 -1
  300. package/dist/vault/types.js.map +1 -1
  301. package/dist/web/admin/portal.d.ts +3 -3
  302. package/dist/web/admin/portal.d.ts.map +1 -1
  303. package/dist/web/admin/portal.js +41 -39
  304. package/dist/web/admin/portal.js.map +1 -1
  305. package/dist/web/admin/types.d.ts +3 -0
  306. package/dist/web/admin/types.d.ts.map +1 -1
  307. package/dist/web/admin/types.js.map +1 -1
  308. package/dist/web/agent-events/portal.d.ts.map +1 -1
  309. package/dist/web/agent-events/portal.js +2 -1
  310. package/dist/web/agent-events/portal.js.map +1 -1
  311. package/dist/workspace-projection/index.d.ts +5 -0
  312. package/dist/workspace-projection/index.d.ts.map +1 -0
  313. package/dist/workspace-projection/index.js +71 -0
  314. package/dist/workspace-projection/index.js.map +1 -0
  315. package/dist/workspace-projection/types.d.ts +7 -0
  316. package/dist/workspace-projection/types.d.ts.map +1 -0
  317. package/dist/workspace-projection/types.js +2 -0
  318. package/dist/workspace-projection/types.js.map +1 -0
  319. package/package.json +1 -1
  320. package/dist/sessions/agent-memory-file-manager.d.ts +0 -19
  321. package/dist/sessions/agent-memory-file-manager.d.ts.map +0 -1
  322. package/dist/sessions/agent-memory-file-manager.js.map +0 -1
  323. package/dist/vault/routing.d.ts +0 -4
  324. package/dist/vault/routing.d.ts.map +0 -1
  325. package/dist/vault/routing.js +0 -6
  326. package/dist/vault/routing.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -9,6 +9,66 @@ any release.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [1.0.0-beta.22]
13
+
14
+ ### Added
15
+
16
+ - Let subagents opt into a normalized reference snapshot of the active parent conversation with `parentContext`, while keeping fresh isolated context as the default.
17
+
18
+ ### Changed
19
+
20
+ - Raise default subagent limits from 8 to 100 turns, $0.50 to $10, and 2 to 10 minutes; return concrete budget or timeout reasons.
21
+ - Apply the process-wide subagent concurrency ceiling to both tool- and extension-initiated runs, with cancellation while queued and usage attribution only to the active parent run.
22
+ - Use collision-safe hashed keys for credential authorization and runtime resources. Exact legacy host and shared-container credentials remain readable, but users of ambiguous legacy managed-sandbox keys may need to run `/login` again.
23
+ - Reject unsupported vault file mounts and mounts that overlap workspace or other credential targets.
24
+ - Extract workspace projection and session lifecycle responsibilities into focused modules without intended runtime behavior changes.
25
+
26
+ ### Fixed
27
+
28
+ - Reject session keys belonging to another conversation and unsafe conversation or thread identifiers.
29
+ - Prevent session pointers and thread files from escaping their owning directory or resolving through symlinks.
30
+ - Process Slack messages posted by known human users through user tokens even when Slack includes the posting app's `bot_id`; bot-authored messages remain ignored.
31
+ - Clearly report subagent budget exhaustion instead of returning a generic incomplete status.
32
+
33
+ ### Tests
34
+
35
+ - Add Slack E2E coverage for DMs, idle stop, thread isolation, busy queues, images, and multi-file uploads.
36
+ - Add unit coverage for session identity and path safety, collision-safe sandbox identities, vault mount policy, session lifecycle, normalized subagent context, shared slots, cancellation, and budget reporting.
37
+
38
+ ## [1.0.0-beta.21]
39
+
40
+ ### Added
41
+
42
+ - Fold each subagent's tokens and cost into the parent run's tally (`MikanAgentSession.recordExternalUsage`), so delegated spend counts against the parent run's budget instead of staying invisible to it.
43
+ - Add a process-wide subagent fan-out ceiling shared across conversations.
44
+ - Add `mikan --help` and a generated `mikan env` inventory from the daemon environment manifest.
45
+ - Make the working-directory argument optional, defaulting to `<state-dir>/workspace`.
46
+ - Add a public Gondolin runtime bootstrap seam for embedders.
47
+
48
+ ### Changed
49
+
50
+ - Subagent runs never reject: request validation failures (unknown tools, invalid budgets, empty tasks, nested runs) now resolve to `failed` results, so one bad request in a `tasks`/`dag` batch can no longer orphan in-flight sibling subagents.
51
+ - Cap parallel `tasks[]` batches at 4 concurrent subagents (the same limit as DAG mode) and run DAG waves through a slot pool instead of chunk barriers.
52
+ - Report a subagent final response with no text as `failed` instead of `completed` with empty output.
53
+ - Enforce parent budgets immediately when subagent usage is folded into the parent run.
54
+ - Apply one state-directory precedence rule across boot, extensions, Sentry, and runtime readers.
55
+ - Refuse model and sandbox settings updates while a conversation is busy, and invalidate affected cached runners consistently.
56
+ - Drive PM2 environment configuration from `~/.mikan/mikan.env` instead of inline secrets.
57
+
58
+ ### Fixed
59
+
60
+ - Harden Gondolin dial-home reconnects after transport failures and stale sessions.
61
+ - Reject invalid one-shot event timestamps consistently across all event writers.
62
+ - Apply global model settings to idle conversations and report busy conversations as stale.
63
+
64
+ ### Security
65
+
66
+ - Keep PM2 secrets in an owner-only file outside repository trees and make the PM2 template supervision-only.
67
+
68
+ ### Tests
69
+
70
+ - Add coverage for subagent accounting and concurrency, CLI and environment manifests, settings mutation, Gondolin reconnects and contracts, event formatting, and worker state layout.
71
+
12
72
  ## [1.0.0-beta.20]
13
73
 
14
74
  ### Fixed
package/README.md CHANGED
@@ -9,6 +9,9 @@
9
9
 
10
10
  A multi-platform AI coding agent for Slack, Telegram, and Discord.
11
11
 
12
+ > [!WARNING]
13
+ > **Pre-1.0 status** — the overall framework stabilizes at 1.0.0. Until then, releases may change settings and on-disk data formats without migrations: upgrading between pre-1.0 versions can require resetting or manually adjusting existing state and workspace data.
14
+
12
15
  ## Architecture
13
16
 
14
17
  mikan keeps the chat record, agent session, and execution runtime separate:
@@ -59,7 +62,11 @@ npm i -g @geminixiang/mikan pm2
59
62
  # One-time setup: create the state directory and settings
60
63
  mikan --onboard --state-dir=~/.mikan
61
64
 
62
- # Grab the maintained ecosystem file, then edit `args` and `env`
65
+ # Secrets live in ~/.mikan/mikan.env (0600), outside any repo tree
66
+ curl -o ~/.mikan/mikan.env https://raw.githubusercontent.com/geminixiang/mikan/main/deploy/pm2/mikan.env.example
67
+ chmod 600 ~/.mikan/mikan.env # then fill in your tokens
68
+
69
+ # Grab the maintained ecosystem file (supervision only), edit `args`
63
70
  curl -O https://raw.githubusercontent.com/geminixiang/mikan/main/deploy/pm2/ecosystem.config.cjs
64
71
 
65
72
  pm2 start ecosystem.config.cjs
@@ -67,21 +74,19 @@ pm2 save
67
74
  pm2 startup # run the printed command to enable boot autostart
68
75
  ```
69
76
 
70
- In `ecosystem.config.cjs`, point `args` at your state dir, sandbox mode, and working directory:
77
+ Each file has one job: `settings.json` holds behavior (model, sandbox limits, reply modes — the Admin surface), `~/.mikan/mikan.env` holds secrets and platform tokens, and `ecosystem.config.cjs` holds process supervision only. In `ecosystem.config.cjs`, point `args` at your state dir, sandbox mode, and working directory (`mikan --help` documents the flags):
71
78
 
72
79
  ```js
73
80
  args: "--state-dir=/srv/mikan/state --sandbox=host /srv/mikan/workspace",
74
81
  ```
75
82
 
76
- Then set the platform tokens you need in `env`; you can run multiple platforms at once:
83
+ Set the platform tokens you need in `~/.mikan/mikan.env`; you can run multiple platforms at once. `mikan env` prints the full inventory and what is currently set:
77
84
 
78
- ```js
79
- env: {
80
- SLACK_APP_TOKEN: "xapp-...",
81
- SLACK_BOT_TOKEN: "xoxb-...",
82
- TELEGRAM_BOT_TOKEN: "123456:ABC-...",
83
- DISCORD_BOT_TOKEN: "MTI...",
84
- },
85
+ ```bash
86
+ SLACK_APP_TOKEN=xapp-...
87
+ SLACK_BOT_TOKEN=xoxb-...
88
+ TELEGRAM_BOT_TOKEN=123456:ABC-...
89
+ DISCORD_BOT_TOKEN=MTI...
85
90
  ```
86
91
 
87
92
  Tail logs with `pm2 logs mikan`; upgrade with `npm i -g @geminixiang/mikan && pm2 reload mikan`. See [the deployment guide](src/content/docs/deployment.mdx) for sandbox images, graceful shutdown, and the health endpoint.
@@ -89,9 +94,11 @@ Tail logs with `pm2 logs mikan`; upgrade with `npm i -g @geminixiang/mikan && pm
89
94
  For a one-off foreground run, the same CLI works directly:
90
95
 
91
96
  ```bash
92
- mikan [--state-dir=~/.mikan] [--sandbox=<mode>] <working-directory>
97
+ mikan [--state-dir=~/.mikan] [--sandbox=<mode>] [<working-directory>]
93
98
  ```
94
99
 
100
+ The working directory is optional: it defaults to `<state-dir>/workspace` (so `~/.mikan/workspace` with the default state dir) and is created on first run.
101
+
95
102
  ## Platforms
96
103
 
97
104
  - **Slack** — create a Socket Mode app using [src/content/docs/slack-bot-minimal-guide.md](src/content/docs/slack-bot-minimal-guide.md). The bot responds when `@mentioned` in channels and to all DMs.
@@ -0,0 +1,60 @@
1
+ import type { ChatToolResult, ConversationResponder } from "../adapter.js";
2
+ import { type ChatResponseErrorOperation } from "./shared.js";
3
+ /**
4
+ * The per-platform verbs and policy behind one buffered responder. Everything
5
+ * else — the accumulated-text state machine, split-first-then-continuations
6
+ * posting, response-id capture, ordered-queue error choreography, typing
7
+ * interval, working indicator, delete semantics — is owned by
8
+ * {@link createBufferedResponder} and identical on every buffered platform.
9
+ * (Slack keeps its own streaming lifecycle; this covers the platforms that
10
+ * edit one response message in place.)
11
+ */
12
+ export interface BufferedResponderPlatform {
13
+ /** Log label, e.g. "Discord". */
14
+ label: string;
15
+ /** Hard message length; longer responses continue in extra messages. */
16
+ maxLength: number;
17
+ formatContinuation: (partNum: number) => string;
18
+ /** Prefix for respondDiagnostic({style:"error"}). */
19
+ errorPrefix: string;
20
+ /** Platform text normalization applied before accumulation (e.g. HTML). */
21
+ sanitize?: (text: string) => string;
22
+ /**
23
+ * Suffix rendered while the run is working (and re-rendered away by
24
+ * setWorking(false)). Platforms without one leave setWorking to typing.
25
+ */
26
+ workingIndicator?: string;
27
+ /** Enable appendResponseDelta/finishResponse over a buffered stream. */
28
+ streaming: boolean;
29
+ typing?: {
30
+ send: () => Promise<unknown>;
31
+ intervalMs: number;
32
+ /** Also stop the indicator when a message is actually sent. */
33
+ stopOnSend?: boolean;
34
+ };
35
+ formatToolResult: (result: ChatToolResult) => string;
36
+ reportError: (err: unknown, operation: ChatResponseErrorOperation, extra: Record<string, unknown>) => void;
37
+ /** Posted into the conversation after a send fails (e.g. Telegram's ⚠️). */
38
+ notifySendFailure?: (errorMessage: string) => Promise<void>;
39
+ /** Post the first response message; returns its platform id. */
40
+ post: (text: string) => Promise<string>;
41
+ /** Edit the existing response message. */
42
+ update: (id: string, text: string) => Promise<void>;
43
+ /** Post an out-of-band message (continuations, diagnostics). */
44
+ postExtra: (text: string, responseId: string | null) => Promise<unknown>;
45
+ /** Delete the response message. */
46
+ delete?: (id: string) => Promise<void>;
47
+ /** Record a bot response chunk against the response message id. */
48
+ logBotResponse?: (text: string, id: string) => void;
49
+ /** Direct upload; absent platforms fall back to uploadFallbackNote. */
50
+ uploadFile?: (filePath: string, title?: string) => Promise<void>;
51
+ /** Posted (queued) instead of uploading when uploadFile is absent. */
52
+ uploadFallbackNote?: (name: string) => string;
53
+ react?: (emoji: string) => Promise<void>;
54
+ }
55
+ /** The tool-result rendering shared by the Markdown platforms (Discord, GitHub). */
56
+ export declare function formatMarkdownToolResult(result: ChatToolResult): string;
57
+ export declare function createBufferedResponder(platform: BufferedResponderPlatform): {
58
+ responder: ConversationResponder;
59
+ };
60
+ //# sourceMappingURL=buffered-responder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffered-responder.d.ts","sourceRoot":"","sources":["../../src/adapters/buffered-responder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE3E,OAAO,EAA6B,KAAK,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAGzF;;;;;;;;GAQG;AACH,MAAM,WAAW,yBAAyB;IACxC,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE;QACP,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7B,UAAU,EAAE,MAAM,CAAC;QACnB,+DAA+D;QAC/D,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,gBAAgB,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,MAAM,CAAC;IACrD,WAAW,EAAE,CACX,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,0BAA0B,EACrC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC3B,IAAI,CAAC;IACV,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,gEAAgE;IAChE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,0CAA0C;IAC1C,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,gEAAgE;IAChE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE,mCAAmC;IACnC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,mEAAmE;IACnE,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,uEAAuE;IACvE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,oFAAoF;AACpF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CASvE;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,yBAAyB,GAAG;IAC5E,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAoPA"}
@@ -0,0 +1,201 @@
1
+ import * as log from "../log.js";
2
+ import { formatToolArgs, splitText } from "./shared.js";
3
+ import { BufferedResponseStream, OrderedResponseOperations } from "./streaming.js";
4
+ /** The tool-result rendering shared by the Markdown platforms (Discord, GitHub). */
5
+ export function formatMarkdownToolResult(result) {
6
+ const argsFormatted = formatToolArgs(result.args);
7
+ const duration = (result.durationMs / 1000).toFixed(1);
8
+ let text = `**${result.isError ? "Error" : "Done"} ${result.toolName}**`;
9
+ if (result.label)
10
+ text += `: ${result.label}`;
11
+ text += ` (${duration}s)\n`;
12
+ if (argsFormatted)
13
+ text += `\`\`\`\n${argsFormatted}\n\`\`\`\n`;
14
+ text += `**Result:**\n\`\`\`\n${result.result}\n\`\`\``;
15
+ return text;
16
+ }
17
+ export function createBufferedResponder(platform) {
18
+ let responseId = null;
19
+ let accumulatedText = "";
20
+ let isWorking = true;
21
+ const queue = new OrderedResponseOperations();
22
+ let typingInterval = null;
23
+ let typingFailureWarned = false;
24
+ const sanitize = platform.sanitize ?? ((text) => text);
25
+ function stopTyping() {
26
+ if (typingInterval !== null) {
27
+ clearInterval(typingInterval);
28
+ typingInterval = null;
29
+ }
30
+ }
31
+ /** The response text as currently displayed (working indicator applied). */
32
+ function render(text) {
33
+ return platform.workingIndicator && isWorking ? text + platform.workingIndicator : text;
34
+ }
35
+ async function postOrUpdate(displayText) {
36
+ if (responseId !== null) {
37
+ await platform.update(responseId, displayText);
38
+ return;
39
+ }
40
+ if (platform.typing?.stopOnSend)
41
+ stopTyping();
42
+ responseId = await platform.post(displayText);
43
+ }
44
+ async function postExtra(text) {
45
+ if (platform.typing?.stopOnSend)
46
+ stopTyping();
47
+ await platform.postExtra(text, responseId);
48
+ }
49
+ async function postSplit(text) {
50
+ const [head, ...tail] = splitText(text, platform.maxLength, platform.formatContinuation);
51
+ await postOrUpdate(head);
52
+ for (const part of tail) {
53
+ await postExtra(part);
54
+ }
55
+ }
56
+ const stream = new BufferedResponseStream({
57
+ flush: async (text) => {
58
+ await postSplit(render(text));
59
+ },
60
+ finish: async (text) => {
61
+ isWorking = false;
62
+ stopTyping();
63
+ await postSplit(text);
64
+ },
65
+ });
66
+ function run(label, operation, work, extra) {
67
+ return queue.run(work, async (err) => {
68
+ const message = err instanceof Error ? err.message : String(err);
69
+ log.logWarning(`${platform.label} ${label} error`, message);
70
+ platform.reportError(err, operation, extra(err));
71
+ if (platform.notifySendFailure) {
72
+ try {
73
+ await platform.notifySendFailure(message);
74
+ }
75
+ catch {
76
+ // ignore secondary failure
77
+ }
78
+ }
79
+ });
80
+ }
81
+ const responder = {
82
+ respond: async (text) => {
83
+ await run("respond", "respond", async () => {
84
+ const sanitized = sanitize(text);
85
+ accumulatedText = accumulatedText ? `${accumulatedText}\n${sanitized}` : sanitized;
86
+ stream.setText(accumulatedText);
87
+ await postSplit(render(accumulatedText));
88
+ if (responseId !== null) {
89
+ platform.logBotResponse?.(text, responseId);
90
+ }
91
+ }, () => ({
92
+ phase: responseId ? "update" : "initial_post",
93
+ textLength: text.length,
94
+ accumulatedLength: accumulatedText.length,
95
+ }));
96
+ },
97
+ ...(platform.streaming
98
+ ? {
99
+ appendResponseDelta: async (delta) => {
100
+ await run("appendResponseDelta", "respond", async () => {
101
+ await stream.append(sanitize(delta));
102
+ accumulatedText = stream.getText();
103
+ if (responseId !== null) {
104
+ platform.logBotResponse?.(delta, responseId);
105
+ }
106
+ }, () => ({ textLength: delta.length, accumulatedLength: stream.getText().length }));
107
+ },
108
+ finishResponse: async (finalText) => {
109
+ await run("finishResponse", "set_working", async () => {
110
+ await stream.finish(finalText === undefined ? undefined : sanitize(finalText));
111
+ accumulatedText = stream.getText();
112
+ }, () => ({ finalTextLength: finalText?.length }));
113
+ },
114
+ }
115
+ : {}),
116
+ replaceResponse: async (text) => {
117
+ await run("replaceResponse", "replace_response", async () => {
118
+ accumulatedText = sanitize(text);
119
+ stream.setText(accumulatedText);
120
+ await postSplit(accumulatedText);
121
+ }, () => ({
122
+ textLength: text.length,
123
+ hadExistingResponse: Boolean(responseId),
124
+ }));
125
+ },
126
+ respondDiagnostic: async (text, options) => {
127
+ await run("respondDiagnostic", "respond_diagnostic", async () => {
128
+ const prefix = options?.style === "error" ? platform.errorPrefix : "";
129
+ for (const part of splitText(sanitize(`${prefix}${text}`), platform.maxLength, platform.formatContinuation)) {
130
+ await postExtra(part);
131
+ }
132
+ }, () => ({ textLength: text.length, style: options?.style }));
133
+ },
134
+ respondToolResult: async (result) => {
135
+ await responder.respondDiagnostic(platform.formatToolResult(result));
136
+ },
137
+ setTyping: async (isTyping) => {
138
+ const typing = platform.typing;
139
+ if (!typing)
140
+ return;
141
+ const onTypingError = (err) => {
142
+ if (typingFailureWarned)
143
+ return;
144
+ typingFailureWarned = true;
145
+ log.logWarning(`${platform.label} sendTyping failed (further occurrences suppressed for this session)`, err instanceof Error ? err.message : String(err));
146
+ };
147
+ if (isTyping && typingInterval === null) {
148
+ typing.send().catch(onTypingError);
149
+ typingInterval = setInterval(() => {
150
+ typing.send().catch(onTypingError);
151
+ }, typing.intervalMs);
152
+ }
153
+ else if (!isTyping) {
154
+ stopTyping();
155
+ }
156
+ },
157
+ setWorking: async (working) => {
158
+ if (!platform.workingIndicator) {
159
+ if (!working)
160
+ stopTyping();
161
+ return;
162
+ }
163
+ await run("setWorking", "set_working", async () => {
164
+ isWorking = working;
165
+ if (!working)
166
+ stopTyping();
167
+ if (responseId !== null) {
168
+ const [head] = splitText(render(accumulatedText), platform.maxLength, platform.formatContinuation);
169
+ await platform.update(responseId, head);
170
+ }
171
+ }, () => ({ working }));
172
+ },
173
+ uploadFile: async (filePath, title) => {
174
+ if (platform.uploadFile) {
175
+ await platform.uploadFile(filePath, title);
176
+ return;
177
+ }
178
+ const note = platform.uploadFallbackNote?.(title ?? filePath);
179
+ if (note === undefined)
180
+ return;
181
+ await run("uploadFile", "respond_diagnostic", () => postExtra(note), () => ({ filePath }));
182
+ },
183
+ ...(platform.react ? { react: platform.react } : {}),
184
+ deleteResponse: async () => {
185
+ await queue.run(async () => {
186
+ stopTyping();
187
+ if (responseId !== null) {
188
+ try {
189
+ await platform.delete?.(responseId);
190
+ }
191
+ catch {
192
+ // Ignore errors
193
+ }
194
+ responseId = null;
195
+ }
196
+ });
197
+ },
198
+ };
199
+ return { responder };
200
+ }
201
+ //# sourceMappingURL=buffered-responder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffered-responder.js","sourceRoot":"","sources":["../../src/adapters/buffered-responder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAmC,MAAM,aAAa,CAAC;AACzF,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AA2DnF,oFAAoF;AACpF,MAAM,UAAU,wBAAwB,CAAC,MAAsB;IAC7D,MAAM,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACvD,IAAI,IAAI,GAAG,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC;IACzE,IAAI,MAAM,CAAC,KAAK;QAAE,IAAI,IAAI,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;IAC9C,IAAI,IAAI,KAAK,QAAQ,MAAM,CAAC;IAC5B,IAAI,aAAa;QAAE,IAAI,IAAI,WAAW,aAAa,YAAY,CAAC;IAChE,IAAI,IAAI,wBAAwB,MAAM,CAAC,MAAM,UAAU,CAAC;IACxD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,QAAmC;IAGzE,IAAI,UAAU,GAAkB,IAAI,CAAC;IACrC,IAAI,eAAe,GAAG,EAAE,CAAC;IACzB,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,MAAM,KAAK,GAAG,IAAI,yBAAyB,EAAE,CAAC;IAC9C,IAAI,cAAc,GAA0C,IAAI,CAAC;IACjE,IAAI,mBAAmB,GAAG,KAAK,CAAC;IAEhC,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAE/D,SAAS,UAAU;QACjB,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;YAC5B,aAAa,CAAC,cAAc,CAAC,CAAC;YAC9B,cAAc,GAAG,IAAI,CAAC;QACxB,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,SAAS,MAAM,CAAC,IAAY;QAC1B,OAAO,QAAQ,CAAC,gBAAgB,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1F,CAAC;IAED,KAAK,UAAU,YAAY,CAAC,WAAmB;QAC7C,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,MAAM,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;YAC/C,OAAO;QACT,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,EAAE,UAAU;YAAE,UAAU,EAAE,CAAC;QAC9C,UAAU,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,UAAU,SAAS,CAAC,IAAY;QACnC,IAAI,QAAQ,CAAC,MAAM,EAAE,UAAU;YAAE,UAAU,EAAE,CAAC;QAC9C,MAAM,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,UAAU,SAAS,CAAC,IAAY;QACnC,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;QACzF,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;YACxB,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,sBAAsB,CAAC;QACxC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACpB,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,CAAC;QACD,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACrB,SAAS,GAAG,KAAK,CAAC;YAClB,UAAU,EAAE,CAAC;YACb,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;KACF,CAAC,CAAC;IAEH,SAAS,GAAG,CACV,KAAa,EACb,SAAqC,EACrC,IAAyB,EACzB,KAAgD;QAEhD,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACnC,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,GAAG,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,IAAI,KAAK,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC5D,QAAQ,CAAC,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YACjD,IAAI,QAAQ,CAAC,iBAAiB,EAAE,CAAC;gBAC/B,IAAI,CAAC;oBACH,MAAM,QAAQ,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;gBAC5C,CAAC;gBAAC,MAAM,CAAC;oBACP,2BAA2B;gBAC7B,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,SAAS,GAA0B;QACvC,OAAO,EAAE,KAAK,EAAE,IAAY,EAAE,EAAE;YAC9B,MAAM,GAAG,CACP,SAAS,EACT,SAAS,EACT,KAAK,IAAI,EAAE;gBACT,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACjC,eAAe,GAAG,eAAe,CAAC,CAAC,CAAC,GAAG,eAAe,KAAK,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;gBACnF,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;gBAChC,MAAM,SAAS,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;gBACzC,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;oBACxB,QAAQ,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;gBAC9C,CAAC;YACH,CAAC,EACD,GAAG,EAAE,CAAC,CAAC;gBACL,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc;gBAC7C,UAAU,EAAE,IAAI,CAAC,MAAM;gBACvB,iBAAiB,EAAE,eAAe,CAAC,MAAM;aAC1C,CAAC,CACH,CAAC;QACJ,CAAC;QAED,GAAG,CAAC,QAAQ,CAAC,SAAS;YACpB,CAAC,CAAC;gBACE,mBAAmB,EAAE,KAAK,EAAE,KAAa,EAAE,EAAE;oBAC3C,MAAM,GAAG,CACP,qBAAqB,EACrB,SAAS,EACT,KAAK,IAAI,EAAE;wBACT,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;wBACrC,eAAe,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;wBACnC,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;4BACxB,QAAQ,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;wBAC/C,CAAC;oBACH,CAAC,EACD,GAAG,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,iBAAiB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,CACjF,CAAC;gBACJ,CAAC;gBAED,cAAc,EAAE,KAAK,EAAE,SAAkB,EAAE,EAAE;oBAC3C,MAAM,GAAG,CACP,gBAAgB,EAChB,aAAa,EACb,KAAK,IAAI,EAAE;wBACT,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;wBAC/E,eAAe,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;oBACrC,CAAC,EACD,GAAG,EAAE,CAAC,CAAC,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAC/C,CAAC;gBACJ,CAAC;aACF;YACH,CAAC,CAAC,EAAE,CAAC;QAEP,eAAe,EAAE,KAAK,EAAE,IAAY,EAAE,EAAE;YACtC,MAAM,GAAG,CACP,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,IAAI,EAAE;gBACT,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACjC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;gBAChC,MAAM,SAAS,CAAC,eAAe,CAAC,CAAC;YACnC,CAAC,EACD,GAAG,EAAE,CAAC,CAAC;gBACL,UAAU,EAAE,IAAI,CAAC,MAAM;gBACvB,mBAAmB,EAAE,OAAO,CAAC,UAAU,CAAC;aACzC,CAAC,CACH,CAAC;QACJ,CAAC;QAED,iBAAiB,EAAE,KAAK,EAAE,IAAY,EAAE,OAAuC,EAAE,EAAE;YACjF,MAAM,GAAG,CACP,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,IAAI,EAAE;gBACT,MAAM,MAAM,GAAG,OAAO,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtE,KAAK,MAAM,IAAI,IAAI,SAAS,CAC1B,QAAQ,CAAC,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC,EAC5B,QAAQ,CAAC,SAAS,EAClB,QAAQ,CAAC,kBAAkB,CAC5B,EAAE,CAAC;oBACF,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC,EACD,GAAG,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAC3D,CAAC;QACJ,CAAC;QAED,iBAAiB,EAAE,KAAK,EAAE,MAAsB,EAAE,EAAE;YAClD,MAAM,SAAS,CAAC,iBAAiB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;QACvE,CAAC;QAED,SAAS,EAAE,KAAK,EAAE,QAAiB,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;YAC/B,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,MAAM,aAAa,GAAG,CAAC,GAAY,EAAQ,EAAE;gBAC3C,IAAI,mBAAmB;oBAAE,OAAO;gBAChC,mBAAmB,GAAG,IAAI,CAAC;gBAC3B,GAAG,CAAC,UAAU,CACZ,GAAG,QAAQ,CAAC,KAAK,sEAAsE,EACvF,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;YACJ,CAAC,CAAC;YACF,IAAI,QAAQ,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;gBACxC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;gBACnC,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE;oBAChC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;gBACrC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;YACxB,CAAC;iBAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACrB,UAAU,EAAE,CAAC;YACf,CAAC;QACH,CAAC;QAED,UAAU,EAAE,KAAK,EAAE,OAAgB,EAAE,EAAE;YACrC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,EAAE,CAAC;gBAC/B,IAAI,CAAC,OAAO;oBAAE,UAAU,EAAE,CAAC;gBAC3B,OAAO;YACT,CAAC;YACD,MAAM,GAAG,CACP,YAAY,EACZ,aAAa,EACb,KAAK,IAAI,EAAE;gBACT,SAAS,GAAG,OAAO,CAAC;gBACpB,IAAI,CAAC,OAAO;oBAAE,UAAU,EAAE,CAAC;gBAC3B,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;oBACxB,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,CACtB,MAAM,CAAC,eAAe,CAAC,EACvB,QAAQ,CAAC,SAAS,EAClB,QAAQ,CAAC,kBAAkB,CAC5B,CAAC;oBACF,MAAM,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;gBAC1C,CAAC;YACH,CAAC,EACD,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CACpB,CAAC;QACJ,CAAC;QAED,UAAU,EAAE,KAAK,EAAE,QAAgB,EAAE,KAAc,EAAE,EAAE;YACrD,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;gBACxB,MAAM,QAAQ,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBAC3C,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAG,QAAQ,CAAC,kBAAkB,EAAE,CAAC,KAAK,IAAI,QAAQ,CAAC,CAAC;YAC9D,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO;YAC/B,MAAM,GAAG,CACP,YAAY,EACZ,oBAAoB,EACpB,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EACrB,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CACrB,CAAC;QACJ,CAAC;QAED,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAEpD,cAAc,EAAE,KAAK,IAAI,EAAE;YACzB,MAAM,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;gBACzB,UAAU,EAAE,CAAC;gBACb,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;oBACxB,IAAI,CAAC;wBACH,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,CAAC;oBACtC,CAAC;oBAAC,MAAM,CAAC;wBACP,gBAAgB;oBAClB,CAAC;oBACD,UAAU,GAAG,IAAI,CAAC;gBACpB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;IAEF,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC","sourcesContent":["import type { ChatToolResult, ConversationResponder } from \"../adapter.js\";\nimport * as log from \"../log.js\";\nimport { formatToolArgs, splitText, type ChatResponseErrorOperation } from \"./shared.js\";\nimport { BufferedResponseStream, OrderedResponseOperations } from \"./streaming.js\";\n\n/**\n * The per-platform verbs and policy behind one buffered responder. Everything\n * else — the accumulated-text state machine, split-first-then-continuations\n * posting, response-id capture, ordered-queue error choreography, typing\n * interval, working indicator, delete semantics — is owned by\n * {@link createBufferedResponder} and identical on every buffered platform.\n * (Slack keeps its own streaming lifecycle; this covers the platforms that\n * edit one response message in place.)\n */\nexport interface BufferedResponderPlatform {\n /** Log label, e.g. \"Discord\". */\n label: string;\n /** Hard message length; longer responses continue in extra messages. */\n maxLength: number;\n formatContinuation: (partNum: number) => string;\n /** Prefix for respondDiagnostic({style:\"error\"}). */\n errorPrefix: string;\n /** Platform text normalization applied before accumulation (e.g. HTML). */\n sanitize?: (text: string) => string;\n /**\n * Suffix rendered while the run is working (and re-rendered away by\n * setWorking(false)). Platforms without one leave setWorking to typing.\n */\n workingIndicator?: string;\n /** Enable appendResponseDelta/finishResponse over a buffered stream. */\n streaming: boolean;\n typing?: {\n send: () => Promise<unknown>;\n intervalMs: number;\n /** Also stop the indicator when a message is actually sent. */\n stopOnSend?: boolean;\n };\n formatToolResult: (result: ChatToolResult) => string;\n reportError: (\n err: unknown,\n operation: ChatResponseErrorOperation,\n extra: Record<string, unknown>,\n ) => void;\n /** Posted into the conversation after a send fails (e.g. Telegram's ⚠️). */\n notifySendFailure?: (errorMessage: string) => Promise<void>;\n /** Post the first response message; returns its platform id. */\n post: (text: string) => Promise<string>;\n /** Edit the existing response message. */\n update: (id: string, text: string) => Promise<void>;\n /** Post an out-of-band message (continuations, diagnostics). */\n postExtra: (text: string, responseId: string | null) => Promise<unknown>;\n /** Delete the response message. */\n delete?: (id: string) => Promise<void>;\n /** Record a bot response chunk against the response message id. */\n logBotResponse?: (text: string, id: string) => void;\n /** Direct upload; absent platforms fall back to uploadFallbackNote. */\n uploadFile?: (filePath: string, title?: string) => Promise<void>;\n /** Posted (queued) instead of uploading when uploadFile is absent. */\n uploadFallbackNote?: (name: string) => string;\n react?: (emoji: string) => Promise<void>;\n}\n\n/** The tool-result rendering shared by the Markdown platforms (Discord, GitHub). */\nexport function formatMarkdownToolResult(result: ChatToolResult): string {\n const argsFormatted = formatToolArgs(result.args);\n const duration = (result.durationMs / 1000).toFixed(1);\n let text = `**${result.isError ? \"Error\" : \"Done\"} ${result.toolName}**`;\n if (result.label) text += `: ${result.label}`;\n text += ` (${duration}s)\\n`;\n if (argsFormatted) text += `\\`\\`\\`\\n${argsFormatted}\\n\\`\\`\\`\\n`;\n text += `**Result:**\\n\\`\\`\\`\\n${result.result}\\n\\`\\`\\``;\n return text;\n}\n\nexport function createBufferedResponder(platform: BufferedResponderPlatform): {\n responder: ConversationResponder;\n} {\n let responseId: string | null = null;\n let accumulatedText = \"\";\n let isWorking = true;\n const queue = new OrderedResponseOperations();\n let typingInterval: ReturnType<typeof setInterval> | null = null;\n let typingFailureWarned = false;\n\n const sanitize = platform.sanitize ?? ((text: string) => text);\n\n function stopTyping(): void {\n if (typingInterval !== null) {\n clearInterval(typingInterval);\n typingInterval = null;\n }\n }\n\n /** The response text as currently displayed (working indicator applied). */\n function render(text: string): string {\n return platform.workingIndicator && isWorking ? text + platform.workingIndicator : text;\n }\n\n async function postOrUpdate(displayText: string): Promise<void> {\n if (responseId !== null) {\n await platform.update(responseId, displayText);\n return;\n }\n if (platform.typing?.stopOnSend) stopTyping();\n responseId = await platform.post(displayText);\n }\n\n async function postExtra(text: string): Promise<void> {\n if (platform.typing?.stopOnSend) stopTyping();\n await platform.postExtra(text, responseId);\n }\n\n async function postSplit(text: string): Promise<void> {\n const [head, ...tail] = splitText(text, platform.maxLength, platform.formatContinuation);\n await postOrUpdate(head);\n for (const part of tail) {\n await postExtra(part);\n }\n }\n\n const stream = new BufferedResponseStream({\n flush: async (text) => {\n await postSplit(render(text));\n },\n finish: async (text) => {\n isWorking = false;\n stopTyping();\n await postSplit(text);\n },\n });\n\n function run(\n label: string,\n operation: ChatResponseErrorOperation,\n work: () => Promise<void>,\n extra: (err: unknown) => Record<string, unknown>,\n ): Promise<void> {\n return queue.run(work, async (err) => {\n const message = err instanceof Error ? err.message : String(err);\n log.logWarning(`${platform.label} ${label} error`, message);\n platform.reportError(err, operation, extra(err));\n if (platform.notifySendFailure) {\n try {\n await platform.notifySendFailure(message);\n } catch {\n // ignore secondary failure\n }\n }\n });\n }\n\n const responder: ConversationResponder = {\n respond: async (text: string) => {\n await run(\n \"respond\",\n \"respond\",\n async () => {\n const sanitized = sanitize(text);\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${sanitized}` : sanitized;\n stream.setText(accumulatedText);\n await postSplit(render(accumulatedText));\n if (responseId !== null) {\n platform.logBotResponse?.(text, responseId);\n }\n },\n () => ({\n phase: responseId ? \"update\" : \"initial_post\",\n textLength: text.length,\n accumulatedLength: accumulatedText.length,\n }),\n );\n },\n\n ...(platform.streaming\n ? {\n appendResponseDelta: async (delta: string) => {\n await run(\n \"appendResponseDelta\",\n \"respond\",\n async () => {\n await stream.append(sanitize(delta));\n accumulatedText = stream.getText();\n if (responseId !== null) {\n platform.logBotResponse?.(delta, responseId);\n }\n },\n () => ({ textLength: delta.length, accumulatedLength: stream.getText().length }),\n );\n },\n\n finishResponse: async (finalText?: string) => {\n await run(\n \"finishResponse\",\n \"set_working\",\n async () => {\n await stream.finish(finalText === undefined ? undefined : sanitize(finalText));\n accumulatedText = stream.getText();\n },\n () => ({ finalTextLength: finalText?.length }),\n );\n },\n }\n : {}),\n\n replaceResponse: async (text: string) => {\n await run(\n \"replaceResponse\",\n \"replace_response\",\n async () => {\n accumulatedText = sanitize(text);\n stream.setText(accumulatedText);\n await postSplit(accumulatedText);\n },\n () => ({\n textLength: text.length,\n hadExistingResponse: Boolean(responseId),\n }),\n );\n },\n\n respondDiagnostic: async (text: string, options?: { style?: \"muted\" | \"error\" }) => {\n await run(\n \"respondDiagnostic\",\n \"respond_diagnostic\",\n async () => {\n const prefix = options?.style === \"error\" ? platform.errorPrefix : \"\";\n for (const part of splitText(\n sanitize(`${prefix}${text}`),\n platform.maxLength,\n platform.formatContinuation,\n )) {\n await postExtra(part);\n }\n },\n () => ({ textLength: text.length, style: options?.style }),\n );\n },\n\n respondToolResult: async (result: ChatToolResult) => {\n await responder.respondDiagnostic(platform.formatToolResult(result));\n },\n\n setTyping: async (isTyping: boolean) => {\n const typing = platform.typing;\n if (!typing) return;\n const onTypingError = (err: unknown): void => {\n if (typingFailureWarned) return;\n typingFailureWarned = true;\n log.logWarning(\n `${platform.label} sendTyping failed (further occurrences suppressed for this session)`,\n err instanceof Error ? err.message : String(err),\n );\n };\n if (isTyping && typingInterval === null) {\n typing.send().catch(onTypingError);\n typingInterval = setInterval(() => {\n typing.send().catch(onTypingError);\n }, typing.intervalMs);\n } else if (!isTyping) {\n stopTyping();\n }\n },\n\n setWorking: async (working: boolean) => {\n if (!platform.workingIndicator) {\n if (!working) stopTyping();\n return;\n }\n await run(\n \"setWorking\",\n \"set_working\",\n async () => {\n isWorking = working;\n if (!working) stopTyping();\n if (responseId !== null) {\n const [head] = splitText(\n render(accumulatedText),\n platform.maxLength,\n platform.formatContinuation,\n );\n await platform.update(responseId, head);\n }\n },\n () => ({ working }),\n );\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n if (platform.uploadFile) {\n await platform.uploadFile(filePath, title);\n return;\n }\n const note = platform.uploadFallbackNote?.(title ?? filePath);\n if (note === undefined) return;\n await run(\n \"uploadFile\",\n \"respond_diagnostic\",\n () => postExtra(note),\n () => ({ filePath }),\n );\n },\n\n ...(platform.react ? { react: platform.react } : {}),\n\n deleteResponse: async () => {\n await queue.run(async () => {\n stopTyping();\n if (responseId !== null) {\n try {\n await platform.delete?.(responseId);\n } catch {\n // Ignore errors\n }\n responseId = null;\n }\n });\n },\n };\n\n return { responder };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,qBAAqB,EAErB,aAAa,EACd,MAAM,kBAAkB,CAAC;AAU1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAsBlE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,mBAAmB,GACvB;IACD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;IACjC,QAAQ,EAAE,aAAa,CAAC;CACzB,CAoQA"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIlG,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAWlE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,mBAAmB,GACvB;IACD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;IACjC,QAAQ,EAAE,aAAa,CAAC;CACzB,CAmFA"}