@meetquinn/animactl 0.1.2 → 0.1.3

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 (289) hide show
  1. package/README.md +11 -6
  2. package/dist/server/{runtime/activity-payload.d.ts → activities/format.d.ts} +4 -1
  3. package/dist/server/activities/format.d.ts.map +1 -0
  4. package/dist/server/{runtime/activity-payload.js → activities/format.js} +16 -1
  5. package/dist/server/agents/agent-skills.d.ts +7 -3
  6. package/dist/server/agents/agent-skills.d.ts.map +1 -1
  7. package/dist/server/agents/agent-skills.js +243 -38
  8. package/dist/server/agents/agent-slack.service.d.ts +15 -0
  9. package/dist/server/agents/agent-slack.service.d.ts.map +1 -1
  10. package/dist/server/agents/agent-slack.service.js +43 -6
  11. package/dist/server/agents/default-skills.d.ts +9 -0
  12. package/dist/server/agents/default-skills.d.ts.map +1 -0
  13. package/dist/server/agents/default-skills.js +52 -0
  14. package/dist/server/cli/runtime-cli.d.ts +7 -1
  15. package/dist/server/cli/runtime-cli.d.ts.map +1 -1
  16. package/dist/server/cli/runtime-cli.js +107 -3
  17. package/dist/server/cli/services-cli.d.ts.map +1 -1
  18. package/dist/server/cli/services-cli.js +39 -0
  19. package/dist/server/inbox/slack-subscriber.d.ts +2 -0
  20. package/dist/server/inbox/slack-subscriber.d.ts.map +1 -1
  21. package/dist/server/inbox/slack-subscriber.js +28 -2
  22. package/dist/server/inbox/slack-subscription.service.d.ts.map +1 -1
  23. package/dist/server/inbox/slack-subscription.service.js +13 -1
  24. package/dist/server/inbox/wake-queue.service.d.ts +3 -1
  25. package/dist/server/inbox/wake-queue.service.d.ts.map +1 -1
  26. package/dist/server/inbox/wake-queue.service.js +5 -3
  27. package/dist/server/provider-usage/http.d.ts +1 -0
  28. package/dist/server/provider-usage/http.d.ts.map +1 -1
  29. package/dist/server/provider-usage/http.js +23 -1
  30. package/dist/server/provider-usage/provider-usage.service.d.ts +1 -4
  31. package/dist/server/provider-usage/provider-usage.service.d.ts.map +1 -1
  32. package/dist/server/provider-usage/provider-usage.service.js +1 -18
  33. package/dist/server/provider-usage/providers/claude.d.ts +1 -1
  34. package/dist/server/provider-usage/providers/claude.d.ts.map +1 -1
  35. package/dist/server/provider-usage/providers/claude.js +1 -1
  36. package/dist/server/provider-usage/providers/codex.d.ts +1 -1
  37. package/dist/server/provider-usage/providers/codex.d.ts.map +1 -1
  38. package/dist/server/provider-usage/providers/codex.js +1 -1
  39. package/dist/server/provider-usage/providers/kimi.d.ts +1 -1
  40. package/dist/server/provider-usage/providers/kimi.d.ts.map +1 -1
  41. package/dist/server/provider-usage/providers/kimi.js +4 -4
  42. package/dist/server/provider-usage/result.d.ts +5 -0
  43. package/dist/server/provider-usage/result.d.ts.map +1 -0
  44. package/dist/server/provider-usage/result.js +18 -0
  45. package/dist/server/providers/active-runtime.d.ts +1 -1
  46. package/dist/server/providers/active-runtime.d.ts.map +1 -1
  47. package/dist/server/providers/claude-events.d.ts +1 -1
  48. package/dist/server/providers/claude-events.d.ts.map +1 -1
  49. package/dist/server/providers/claude-events.js +2 -2
  50. package/dist/server/providers/claude.d.ts +1 -1
  51. package/dist/server/providers/claude.d.ts.map +1 -1
  52. package/dist/server/providers/claude.js +2 -2
  53. package/dist/server/providers/codex-app-server.d.ts +1 -1
  54. package/dist/server/providers/codex-app-server.d.ts.map +1 -1
  55. package/dist/server/providers/codex-app-server.js +1 -1
  56. package/dist/server/providers/codex-events.js +1 -1
  57. package/dist/server/providers/codex.d.ts +1 -1
  58. package/dist/server/providers/codex.d.ts.map +1 -1
  59. package/dist/server/providers/codex.js +2 -2
  60. package/dist/server/{runtime/provider-contract.d.ts → providers/contract.d.ts} +1 -1
  61. package/dist/server/providers/contract.d.ts.map +1 -0
  62. package/dist/server/providers/factory.d.ts +1 -1
  63. package/dist/server/providers/factory.d.ts.map +1 -1
  64. package/dist/server/providers/kimi-events.d.ts +1 -1
  65. package/dist/server/providers/kimi-events.d.ts.map +1 -1
  66. package/dist/server/providers/kimi-events.js +1 -1
  67. package/dist/server/providers/kimi.d.ts +1 -1
  68. package/dist/server/providers/kimi.d.ts.map +1 -1
  69. package/dist/server/providers/kimi.js +3 -2
  70. package/dist/server/providers/reasoning-events.js +1 -1
  71. package/dist/server/runtime/active-run-control.d.ts +19 -0
  72. package/dist/server/runtime/active-run-control.d.ts.map +1 -0
  73. package/dist/server/runtime/active-run-control.js +89 -0
  74. package/dist/server/runtime/activity.js +1 -1
  75. package/dist/server/runtime/agent-runner.d.ts +1 -1
  76. package/dist/server/runtime/agent-runner.d.ts.map +1 -1
  77. package/dist/server/runtime/delivery-prompt.d.ts +6 -0
  78. package/dist/server/runtime/delivery-prompt.d.ts.map +1 -1
  79. package/dist/server/runtime/delivery-prompt.js +53 -34
  80. package/dist/server/runtime/followup-appender.d.ts +20 -0
  81. package/dist/server/runtime/followup-appender.d.ts.map +1 -0
  82. package/dist/server/runtime/followup-appender.js +128 -0
  83. package/dist/server/runtime/host.d.ts +2 -0
  84. package/dist/server/runtime/host.d.ts.map +1 -1
  85. package/dist/server/runtime/host.js +8 -0
  86. package/dist/server/runtime/provider-runner.d.ts +16 -0
  87. package/dist/server/runtime/provider-runner.d.ts.map +1 -0
  88. package/dist/server/runtime/provider-runner.js +86 -0
  89. package/dist/server/runtime/runtime-bridge.d.ts +1 -1
  90. package/dist/server/runtime/runtime-bridge.d.ts.map +1 -1
  91. package/dist/server/runtime/runtime-session.service.d.ts +1 -1
  92. package/dist/server/runtime/runtime-session.service.d.ts.map +1 -1
  93. package/dist/server/runtime/runtime-session.service.js +1 -1
  94. package/dist/server/runtime/runtime-worker.d.ts +2 -10
  95. package/dist/server/runtime/runtime-worker.d.ts.map +1 -1
  96. package/dist/server/runtime/runtime-worker.js +60 -286
  97. package/dist/server/runtime-management/managed-runtime.d.ts.map +1 -0
  98. package/dist/server/runtime-management/runtime-release.d.ts.map +1 -0
  99. package/dist/server/{runtime → runtime-management}/runtime-upgrade.d.ts +1 -0
  100. package/dist/server/runtime-management/runtime-upgrade.d.ts.map +1 -0
  101. package/dist/server/{runtime → runtime-management}/runtime-upgrade.js +14 -0
  102. package/dist/server/services/system.service.d.ts +41 -0
  103. package/dist/server/services/system.service.d.ts.map +1 -0
  104. package/dist/server/services/system.service.js +154 -0
  105. package/dist/server/settings/settings.service.d.ts +2 -1
  106. package/dist/server/settings/settings.service.d.ts.map +1 -1
  107. package/dist/server/settings/settings.service.js +9 -1
  108. package/dist/server/slack/shortcuts.d.ts +0 -3
  109. package/dist/server/slack/shortcuts.d.ts.map +1 -1
  110. package/dist/server/slack/shortcuts.js +0 -3
  111. package/dist/server/slack-interactions/shortcut-ids.d.ts +4 -0
  112. package/dist/server/slack-interactions/shortcut-ids.d.ts.map +1 -0
  113. package/dist/server/slack-interactions/shortcut-ids.js +3 -0
  114. package/dist/server/slack-interactions/shortcut-views.d.ts +65 -0
  115. package/dist/server/slack-interactions/shortcut-views.d.ts.map +1 -0
  116. package/dist/server/slack-interactions/shortcut-views.js +211 -0
  117. package/dist/server/{slack → slack-interactions}/shortcut.service.d.ts +2 -47
  118. package/dist/server/slack-interactions/shortcut.service.d.ts.map +1 -0
  119. package/dist/server/slack-interactions/shortcut.service.js +227 -0
  120. package/dist/server/storage/jsonl-log.d.ts +9 -0
  121. package/dist/server/storage/jsonl-log.d.ts.map +1 -1
  122. package/dist/server/storage/jsonl-log.js +36 -1
  123. package/dist/server/storage/schema/server.store.d.ts +5 -0
  124. package/dist/server/storage/schema/server.store.d.ts.map +1 -1
  125. package/dist/server/storage/schema/server.store.js +2 -1
  126. package/dist/server/storage/schema/wake-queue.store.d.ts +15 -1
  127. package/dist/server/storage/schema/wake-queue.store.d.ts.map +1 -1
  128. package/dist/server/storage/schema/wake-queue.store.js +5 -3
  129. package/dist/server/tests/agent-config.test.d.ts +2 -0
  130. package/dist/server/tests/agent-config.test.d.ts.map +1 -0
  131. package/dist/server/tests/agent-config.test.js +229 -0
  132. package/dist/server/tests/agent-runtime.test.d.ts +2 -0
  133. package/dist/server/tests/agent-runtime.test.d.ts.map +1 -0
  134. package/dist/server/tests/agent-runtime.test.js +1110 -0
  135. package/dist/server/tests/agent-seed-memory.test.d.ts +2 -0
  136. package/dist/server/tests/agent-seed-memory.test.d.ts.map +1 -0
  137. package/dist/server/tests/agent-seed-memory.test.js +16 -0
  138. package/dist/server/tests/agent-skills.test.d.ts +2 -0
  139. package/dist/server/tests/agent-skills.test.d.ts.map +1 -0
  140. package/dist/server/tests/agent-skills.test.js +140 -0
  141. package/dist/server/tests/anima-home.d.ts +2 -0
  142. package/dist/server/tests/anima-home.d.ts.map +1 -0
  143. package/dist/server/tests/anima-home.js +4 -0
  144. package/dist/server/tests/cli-file.test.d.ts +2 -0
  145. package/dist/server/tests/cli-file.test.d.ts.map +1 -0
  146. package/dist/server/tests/cli-file.test.js +596 -0
  147. package/dist/server/tests/cli-message.test.d.ts +2 -0
  148. package/dist/server/tests/cli-message.test.d.ts.map +1 -0
  149. package/dist/server/tests/cli-message.test.js +1196 -0
  150. package/dist/server/tests/client-error-routes.test.d.ts +2 -0
  151. package/dist/server/tests/client-error-routes.test.d.ts.map +1 -0
  152. package/dist/server/tests/client-error-routes.test.js +163 -0
  153. package/dist/server/tests/config.test.d.ts +2 -0
  154. package/dist/server/tests/config.test.d.ts.map +1 -0
  155. package/dist/server/tests/config.test.js +497 -0
  156. package/dist/server/tests/default-skills.test.d.ts +2 -0
  157. package/dist/server/tests/default-skills.test.d.ts.map +1 -0
  158. package/dist/server/tests/default-skills.test.js +57 -0
  159. package/dist/server/tests/helpers/inbox.d.ts +10 -0
  160. package/dist/server/tests/helpers/inbox.d.ts.map +1 -0
  161. package/dist/server/tests/helpers/inbox.js +21 -0
  162. package/dist/server/tests/helpers/slack.d.ts +11 -0
  163. package/dist/server/tests/helpers/slack.d.ts.map +1 -0
  164. package/dist/server/tests/helpers/slack.js +17 -0
  165. package/dist/server/tests/helpers/state.d.ts +19 -0
  166. package/dist/server/tests/helpers/state.d.ts.map +1 -0
  167. package/dist/server/tests/helpers/state.js +69 -0
  168. package/dist/server/tests/inbox-slack-events.test.d.ts +2 -0
  169. package/dist/server/tests/inbox-slack-events.test.d.ts.map +1 -0
  170. package/dist/server/tests/inbox-slack-events.test.js +271 -0
  171. package/dist/server/tests/inbox.test.d.ts +2 -0
  172. package/dist/server/tests/inbox.test.d.ts.map +1 -0
  173. package/dist/server/tests/inbox.test.js +144 -0
  174. package/dist/server/tests/interactive-ask.test.d.ts +2 -0
  175. package/dist/server/tests/interactive-ask.test.d.ts.map +1 -0
  176. package/dist/server/tests/interactive-ask.test.js +387 -0
  177. package/dist/server/tests/kb.test.d.ts +2 -0
  178. package/dist/server/tests/kb.test.d.ts.map +1 -0
  179. package/dist/server/tests/kb.test.js +371 -0
  180. package/dist/server/tests/message.service.test.d.ts +2 -0
  181. package/dist/server/tests/message.service.test.d.ts.map +1 -0
  182. package/dist/server/tests/message.service.test.js +151 -0
  183. package/dist/server/tests/messages.test.d.ts +2 -0
  184. package/dist/server/tests/messages.test.d.ts.map +1 -0
  185. package/dist/server/tests/messages.test.js +12 -0
  186. package/dist/server/tests/prompt-attachments.test.d.ts +2 -0
  187. package/dist/server/tests/prompt-attachments.test.d.ts.map +1 -0
  188. package/dist/server/tests/prompt-attachments.test.js +274 -0
  189. package/dist/server/tests/provider-usage.test.d.ts +2 -0
  190. package/dist/server/tests/provider-usage.test.d.ts.map +1 -0
  191. package/dist/server/tests/provider-usage.test.js +97 -0
  192. package/dist/server/tests/reminders.test.d.ts +2 -0
  193. package/dist/server/tests/reminders.test.d.ts.map +1 -0
  194. package/dist/server/tests/reminders.test.js +383 -0
  195. package/dist/server/tests/runtime-cli.test.d.ts +2 -0
  196. package/dist/server/tests/runtime-cli.test.d.ts.map +1 -0
  197. package/dist/server/tests/runtime-cli.test.js +21 -0
  198. package/dist/server/tests/runtime-upgrade.test.d.ts +2 -0
  199. package/dist/server/tests/runtime-upgrade.test.d.ts.map +1 -0
  200. package/dist/server/tests/runtime-upgrade.test.js +307 -0
  201. package/dist/server/tests/runtime-worker.test.d.ts +2 -0
  202. package/dist/server/tests/runtime-worker.test.d.ts.map +1 -0
  203. package/dist/server/tests/runtime-worker.test.js +1021 -0
  204. package/dist/server/tests/runtime.test.d.ts +2 -0
  205. package/dist/server/tests/runtime.test.d.ts.map +1 -0
  206. package/dist/server/tests/runtime.test.js +354 -0
  207. package/dist/server/tests/services.test.d.ts +2 -0
  208. package/dist/server/tests/services.test.d.ts.map +1 -0
  209. package/dist/server/tests/services.test.js +636 -0
  210. package/dist/server/tests/slack-files.test.d.ts +2 -0
  211. package/dist/server/tests/slack-files.test.d.ts.map +1 -0
  212. package/dist/server/tests/slack-files.test.js +118 -0
  213. package/dist/server/tests/slack-shortcuts.test.d.ts +2 -0
  214. package/dist/server/tests/slack-shortcuts.test.d.ts.map +1 -0
  215. package/dist/server/tests/slack-shortcuts.test.js +310 -0
  216. package/dist/server/tests/slack.test.d.ts +2 -0
  217. package/dist/server/tests/slack.test.d.ts.map +1 -0
  218. package/dist/server/tests/slack.test.js +213 -0
  219. package/dist/server/tests/state-cache.test.d.ts +2 -0
  220. package/dist/server/tests/state-cache.test.d.ts.map +1 -0
  221. package/dist/server/tests/state-cache.test.js +184 -0
  222. package/dist/server/tests/subscriptions.test.d.ts +2 -0
  223. package/dist/server/tests/subscriptions.test.d.ts.map +1 -0
  224. package/dist/server/tests/subscriptions.test.js +292 -0
  225. package/dist/server/tests/system-service.test.d.ts +2 -0
  226. package/dist/server/tests/system-service.test.d.ts.map +1 -0
  227. package/dist/server/tests/system-service.test.js +20 -0
  228. package/dist/server/tests/url-routes.test.d.ts +2 -0
  229. package/dist/server/tests/url-routes.test.d.ts.map +1 -0
  230. package/dist/server/tests/url-routes.test.js +217 -0
  231. package/dist/server/tests/web-api.test.d.ts +2 -0
  232. package/dist/server/tests/web-api.test.d.ts.map +1 -0
  233. package/dist/server/tests/web-api.test.js +1759 -0
  234. package/dist/server/web/app.d.ts.map +1 -1
  235. package/dist/server/web/app.js +2 -0
  236. package/dist/server/web/client-error-routes.d.ts +3 -0
  237. package/dist/server/web/client-error-routes.d.ts.map +1 -0
  238. package/dist/server/web/client-error-routes.js +101 -0
  239. package/dist/server/web/system-routes.d.ts.map +1 -1
  240. package/dist/server/web/system-routes.js +21 -131
  241. package/dist/shared/agent-config.d.ts +8 -18
  242. package/dist/shared/agent-config.d.ts.map +1 -1
  243. package/dist/shared/agent-config.js +3 -4
  244. package/dist/shared/inbox.d.ts +27 -0
  245. package/dist/shared/inbox.d.ts.map +1 -1
  246. package/dist/shared/inbox.js +1 -0
  247. package/dist/shared/server-info.d.ts +6 -5
  248. package/dist/shared/server-info.d.ts.map +1 -1
  249. package/dist/shared/server-info.js +3 -1
  250. package/dist/shared/server-settings.d.ts +6 -0
  251. package/dist/shared/server-settings.d.ts.map +1 -1
  252. package/dist/shared/server-settings.js +1 -0
  253. package/dist/shared/skills.d.ts +24 -3
  254. package/dist/shared/skills.d.ts.map +1 -1
  255. package/dist/web/assets/activity-DHjip8tY.js +7 -0
  256. package/dist/web/assets/clipboard-D2NSC52v.js +1 -0
  257. package/dist/web/assets/{external-link-Dt0qTpmV.js → external-link-Bhr1WdRS.js} +1 -1
  258. package/dist/web/assets/{file-text-Dd9ys6py.js → file-text-CYMA_SMA.js} +1 -1
  259. package/dist/web/assets/{index-CzAQnbuO.js → index-CVpew409.js} +5 -5
  260. package/dist/web/assets/index-mh0uJTkY.css +2 -0
  261. package/dist/web/assets/kb-DDVpYkT9.js +35 -0
  262. package/dist/web/assets/profile-CBy6GNzp.js +1 -0
  263. package/dist/web/assets/reminders-Bbwb9QNT.js +1 -0
  264. package/dist/web/favicon.svg +1 -1
  265. package/dist/web/index.html +2 -2
  266. package/package.json +1 -1
  267. package/templates/runtime-standing-prompt.md +16 -2
  268. package/templates/skills/find-skills/SKILL.md +133 -0
  269. package/dist/server/runtime/activity-payload.d.ts.map +0 -1
  270. package/dist/server/runtime/activity-text.d.ts +0 -4
  271. package/dist/server/runtime/activity-text.d.ts.map +0 -1
  272. package/dist/server/runtime/activity-text.js +0 -16
  273. package/dist/server/runtime/managed-runtime.d.ts.map +0 -1
  274. package/dist/server/runtime/provider-contract.d.ts.map +0 -1
  275. package/dist/server/runtime/runtime-release.d.ts.map +0 -1
  276. package/dist/server/runtime/runtime-upgrade.d.ts.map +0 -1
  277. package/dist/server/slack/shortcut.service.d.ts.map +0 -1
  278. package/dist/server/slack/shortcut.service.js +0 -493
  279. package/dist/web/assets/activity-BP0C81IV.js +0 -7
  280. package/dist/web/assets/globe-DmDZSgDP.js +0 -1
  281. package/dist/web/assets/index-DKecrvN0.css +0 -2
  282. package/dist/web/assets/kb-3wFeP2Jt.js +0 -32
  283. package/dist/web/assets/profile-D7wPZXW_.js +0 -1
  284. package/dist/web/assets/reminders-DOhroVwo.js +0 -1
  285. /package/dist/server/{runtime/provider-contract.js → providers/contract.js} +0 -0
  286. /package/dist/server/{runtime → runtime-management}/managed-runtime.d.ts +0 -0
  287. /package/dist/server/{runtime → runtime-management}/managed-runtime.js +0 -0
  288. /package/dist/server/{runtime → runtime-management}/runtime-release.d.ts +0 -0
  289. /package/dist/server/{runtime → runtime-management}/runtime-release.js +0 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runtime.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.test.d.ts","sourceRoot":"","sources":["../../../server/tests/runtime.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,354 @@
1
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import test from 'node:test';
5
+ import assert from 'node:assert/strict';
6
+ import { makeSlackEvent } from './helpers/slack.js';
7
+ import { slackSurfaceForEvent } from '../inbox/slack-events.js';
8
+ import { WakeQueueService } from '../inbox/wake-queue.service.js';
9
+ import { ingestEvent } from './helpers/inbox.js';
10
+ import { loadState } from './helpers/state.js';
11
+ import { activityServiceForAgent } from '../activities/activity.service.js';
12
+ import { isFirstClassAnimaCliCommand } from '../activities/format.js';
13
+ import { activitiesForInboxItemWindow } from '../runtime/item-activities.js';
14
+ import { startChildProcess } from '../providers/child-process.js';
15
+ import { RuntimeHost } from '../runtime/host.js';
16
+ import { withAnimaHome } from './anima-home.js';
17
+ test('child process completion preserves exit details when stream effects fail', async () => {
18
+ const child = startChildProcess({
19
+ args: ['-e', 'process.stdout.write("payload"); process.stderr.write("boom"); process.exit(7);'],
20
+ command: process.execPath,
21
+ env: process.env,
22
+ label: 'test child',
23
+ onStdoutChunk: async () => {
24
+ throw new Error('callback parse failed');
25
+ },
26
+ });
27
+ await assert.rejects(child.completion, (error) => {
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ assert.match(message, /test child exited with code 7/);
30
+ assert.match(message, /stderr: boom/);
31
+ assert.match(message, /stdout: payload/);
32
+ assert.match(message, /stream effect failed: callback parse failed/);
33
+ return true;
34
+ });
35
+ });
36
+ test('first-class anima CLI command detection covers plain agent-facing tools', () => {
37
+ assert.equal(isFirstClassAnimaCliCommand('anima ask --question "Pick" --option A --option B'), true);
38
+ assert.equal(isFirstClassAnimaCliCommand('ANIMA_AGENT_ID=scout anima reminder list'), true);
39
+ assert.equal(isFirstClassAnimaCliCommand('anima subscription mute --channel C123'), true);
40
+ assert.equal(isFirstClassAnimaCliCommand('anima message send --channel C123'), true);
41
+ assert.equal(isFirstClassAnimaCliCommand('cd ~/anima && ANIMA_AGENT_ID=scout anima ask --question "Pick"'), false);
42
+ });
43
+ test('runtime host idles with zero agents and starts a newly runnable agent once', async () => {
44
+ let agents = [];
45
+ const started = [];
46
+ const stopped = [];
47
+ const host = new RuntimeHost({}, {
48
+ animaHome: '/tmp/anima-home',
49
+ loadAgents: async () => agents,
50
+ logger: silentLogger,
51
+ startAgent: async (agent) => {
52
+ started.push(agent.id);
53
+ return stopHandle(agent.id, stopped);
54
+ },
55
+ validateAgent: async () => { },
56
+ });
57
+ await host.reconcileOnce();
58
+ assert.deepEqual(started, []);
59
+ assert.deepEqual(host.runningAgentIds(), []);
60
+ agents = [runtimeHostAgent('aria', { connected: true })];
61
+ await host.reconcileOnce();
62
+ await host.reconcileOnce();
63
+ assert.deepEqual(started, ['aria']);
64
+ assert.deepEqual(host.runningAgentIds(), ['aria']);
65
+ await host.stop();
66
+ assert.deepEqual(stopped, ['aria']);
67
+ });
68
+ test('runtime host starts after Slack connection and reloads idle agents after config changes', async () => {
69
+ let scout = runtimeHostAgent('scout', { connected: false });
70
+ const started = [];
71
+ const stopped = [];
72
+ const host = new RuntimeHost({}, {
73
+ animaHome: '/tmp/anima-home',
74
+ loadAgents: async () => [scout],
75
+ logger: silentLogger,
76
+ startAgent: async (agent) => {
77
+ started.push(`${agent.id}:${agent.homePath}:${agent.provider.model ?? ''}:${agent.profile.role}`);
78
+ return stopHandle(agent.id, stopped);
79
+ },
80
+ validateAgent: async () => { },
81
+ });
82
+ await host.reconcileOnce();
83
+ assert.deepEqual(started, []);
84
+ scout = runtimeHostAgent('scout', { connected: true, homePath: '/tmp/home-a', model: 'opus' });
85
+ await host.reconcileOnce();
86
+ assert.deepEqual(started, ['scout:/tmp/home-a:opus:general purpose']);
87
+ scout = runtimeHostAgent('scout', { connected: true, homePath: '/tmp/home-a', model: 'opus', role: 'research lead' });
88
+ await host.reconcileOnce();
89
+ assert.deepEqual(stopped, ['scout']);
90
+ assert.deepEqual(started, [
91
+ 'scout:/tmp/home-a:opus:general purpose',
92
+ 'scout:/tmp/home-a:opus:research lead',
93
+ ]);
94
+ scout = runtimeHostAgent('scout', { connected: true, homePath: '/tmp/home-b', model: 'sonnet', role: 'research lead' });
95
+ await host.reconcileOnce();
96
+ await host.reconcileOnce();
97
+ assert.deepEqual(stopped, ['scout', 'scout']);
98
+ assert.deepEqual(started, [
99
+ 'scout:/tmp/home-a:opus:general purpose',
100
+ 'scout:/tmp/home-a:opus:research lead',
101
+ 'scout:/tmp/home-b:sonnet:research lead',
102
+ ]);
103
+ assert.deepEqual(host.runningAgentIds(), ['scout']);
104
+ await host.stop();
105
+ });
106
+ test('runtime host defers config reload until the running agent is idle', async () => {
107
+ let scout = runtimeHostAgent('scout', { connected: true, homePath: '/tmp/home-a', model: 'opus' });
108
+ let active = false;
109
+ const started = [];
110
+ const stopped = [];
111
+ const host = new RuntimeHost({}, {
112
+ animaHome: '/tmp/anima-home',
113
+ loadAgents: async () => [scout],
114
+ logger: silentLogger,
115
+ startAgent: async (agent) => {
116
+ started.push(`${agent.id}:${agent.homePath}:${agent.provider.model ?? ''}`);
117
+ return stopHandle(agent.id, stopped, () => active);
118
+ },
119
+ validateAgent: async () => { },
120
+ });
121
+ await host.reconcileOnce();
122
+ assert.deepEqual(started, ['scout:/tmp/home-a:opus']);
123
+ active = true;
124
+ scout = runtimeHostAgent('scout', { connected: true, homePath: '/tmp/home-b', model: 'sonnet' });
125
+ await host.reconcileOnce();
126
+ await host.reconcileOnce();
127
+ assert.deepEqual(stopped, []);
128
+ assert.deepEqual(started, ['scout:/tmp/home-a:opus']);
129
+ assert.deepEqual(host.runningAgentIds(), ['scout']);
130
+ active = false;
131
+ await host.reconcileOnce();
132
+ assert.deepEqual(stopped, ['scout']);
133
+ assert.deepEqual(started, ['scout:/tmp/home-a:opus', 'scout:/tmp/home-b:sonnet']);
134
+ await host.stop();
135
+ });
136
+ test('runtime host stops a running agent after it becomes disabled', async () => {
137
+ let scout = runtimeHostAgent('scout', { connected: true });
138
+ const stopped = [];
139
+ const host = new RuntimeHost({}, {
140
+ animaHome: '/tmp/anima-home',
141
+ loadAgents: async () => [scout],
142
+ logger: silentLogger,
143
+ startAgent: async (agent) => stopHandle(agent.id, stopped),
144
+ validateAgent: async () => { },
145
+ });
146
+ await host.reconcileOnce();
147
+ assert.deepEqual(host.runningAgentIds(), ['scout']);
148
+ scout = runtimeHostAgent('scout', { connected: true, enabled: false });
149
+ await host.reconcileOnce();
150
+ await host.reconcileOnce();
151
+ assert.deepEqual(stopped, ['scout']);
152
+ assert.deepEqual(host.runningAgentIds(), []);
153
+ await host.stop();
154
+ });
155
+ test('Slack DM and channel events share one primary session', async () => {
156
+ const stateDir = await mkdtemp(join(tmpdir(), 'anima-test-'));
157
+ try {
158
+ await withAnimaHome(stateDir, async () => {
159
+ const config = { agentId: 'anima', stateDir };
160
+ const first = await ingestEvent(makeSlackEvent({
161
+ channelId: 'C-product',
162
+ channelName: 'product',
163
+ teamId: 'T-demo',
164
+ text: 'We should improve rough CEO ideas into tracked spikes.',
165
+ threadTs: '1770000000.000001',
166
+ userId: 'U1',
167
+ }), config);
168
+ const second = await ingestEvent(makeSlackEvent({
169
+ channelId: 'D-user-anima',
170
+ teamId: 'T-demo',
171
+ text: 'Private context: this is about review friction.',
172
+ userId: 'U1',
173
+ }), config);
174
+ const third = await ingestEvent(makeSlackEvent({
175
+ channelId: 'C-product',
176
+ channelName: 'product',
177
+ teamId: 'T-demo',
178
+ text: 'Can you summarize the decision and next step?',
179
+ threadTs: '1770000000.000001',
180
+ userId: 'U1',
181
+ }), config);
182
+ assert.equal(first.session.createdAt, second.session.createdAt);
183
+ assert.equal(second.session.createdAt, third.session.createdAt);
184
+ const state = await loadState();
185
+ const storedEvent = state.events[third.item.id];
186
+ assert.equal(storedEvent?.kind, 'slack');
187
+ assert.equal(storedEvent?.kind === 'slack' ? slackSurfaceForEvent(storedEvent).id : undefined, 'slack:T-demo:C-product:thread:1770000000.000001');
188
+ assert.equal(storedEvent && storedEvent.kind === 'slack' ? storedEvent.channelName : undefined, 'product');
189
+ const activities = await activitiesForInboxItemWindow('anima', third.item.id);
190
+ assert.equal(activities.length, 0);
191
+ });
192
+ }
193
+ finally {
194
+ await rm(stateDir, { force: true, recursive: true });
195
+ }
196
+ });
197
+ test('duplicate queue enqueue creates one item', async () => {
198
+ const stateDir = await mkdtemp(join(tmpdir(), 'anima-duplicate-ingest-test-'));
199
+ try {
200
+ await withAnimaHome(stateDir, async () => {
201
+ const config = { agentId: 'anima', stateDir };
202
+ const event = makeSlackEvent({
203
+ channelId: 'C-product',
204
+ eventId: 'slack:T-demo:C-product:1770000042.000001',
205
+ teamId: 'T-demo',
206
+ text: '<@U999> same delivery',
207
+ ts: '1770000042.000001',
208
+ userId: 'U1',
209
+ });
210
+ const queue = new WakeQueueService(config.agentId);
211
+ const results = [];
212
+ for (let i = 0; i < 20; i += 1) {
213
+ results.push(await queue.enqueue(event));
214
+ }
215
+ const events = await queue.list();
216
+ const state = await loadState();
217
+ const items = Object.values(state.items).filter((item) => item.id === event.id);
218
+ assert.equal(events.filter((stored) => stored.id === event.id).length, 1);
219
+ assert.equal(items.length, 1);
220
+ assert.equal(results.filter((result) => result.duplicate).length, 19);
221
+ assert.equal(new Set(results.map((result) => result.item.id)).size, 1);
222
+ });
223
+ }
224
+ finally {
225
+ await rm(stateDir, { force: true, recursive: true });
226
+ }
227
+ });
228
+ test('Slack message send activity can target another channel without item ownership', async () => {
229
+ const stateDir = await mkdtemp(join(tmpdir(), 'anima-test-'));
230
+ try {
231
+ await withAnimaHome(stateDir, async () => {
232
+ const config = { agentId: 'anima', stateDir };
233
+ const ctx = await ingestEvent(makeSlackEvent({
234
+ channelId: 'C-product',
235
+ channelName: 'product',
236
+ teamId: 'T-demo',
237
+ text: 'Please post the summary to ops too.',
238
+ userId: 'U1',
239
+ }), config);
240
+ const activity = await activityServiceForAgent('anima').record({
241
+ payload: {
242
+ channel: 'C-ops',
243
+ channelName: 'ops',
244
+ payload: {
245
+ channel: 'C-ops',
246
+ text: 'Summary for ops.',
247
+ },
248
+ status: 'dry-run',
249
+ text: 'Summary for ops.',
250
+ tool: 'anima.message.send',
251
+ },
252
+ type: 'tool.call.completed',
253
+ });
254
+ assert.equal(Object.hasOwn(activity, 'itemId'), false);
255
+ assert.equal(activity.payload?.['channel'], 'C-ops');
256
+ assert.equal(activity.payload?.['channelName'], 'ops');
257
+ assert.deepEqual(activity.payload?.['payload'], {
258
+ channel: 'C-ops',
259
+ text: 'Summary for ops.',
260
+ });
261
+ const state = await loadState();
262
+ const storedEvent = state.events[ctx.item.id];
263
+ assert.equal(storedEvent?.kind, 'slack');
264
+ assert.equal(storedEvent && storedEvent.kind === 'slack' ? storedEvent.channelId : undefined, 'C-product');
265
+ assert.equal(state.activities[activity.activityId]?.payload?.['channel'], 'C-ops');
266
+ });
267
+ }
268
+ finally {
269
+ await rm(stateDir, { force: true, recursive: true });
270
+ }
271
+ });
272
+ test('state is stored per agent with append-only activity logs', async () => {
273
+ const stateDir = await mkdtemp(join(tmpdir(), 'anima-state-layout-test-'));
274
+ try {
275
+ await withAnimaHome(stateDir, async () => {
276
+ const ctx = await ingestEvent(makeSlackEvent({
277
+ channelId: 'D-anima',
278
+ teamId: 'T-demo',
279
+ text: 'Persist this in the folder state.',
280
+ userId: 'U1',
281
+ }), { agentId: 'anima', stateDir });
282
+ await activityServiceForAgent('anima').record({
283
+ payload: {
284
+ status: 'dry-run',
285
+ text: 'Folder activity.',
286
+ tool: 'anima.message.send',
287
+ },
288
+ type: 'tool.call.completed',
289
+ });
290
+ await assert.rejects(readFile(join(stateDir, 'state.json'), 'utf8'), /ENOENT/);
291
+ const sessionJson = await readFile(join(stateDir, 'agents/anima/sessions.json'), 'utf8');
292
+ const sessionRecord = JSON.parse(sessionJson);
293
+ assert.equal(Object.hasOwn(sessionRecord, 'sessionKey'), false);
294
+ assert.equal(Object.hasOwn(sessionRecord, 'activeTopicSummary'), false);
295
+ assert.equal(Object.hasOwn(sessionRecord, 'eventIds'), false);
296
+ assert.equal(Object.hasOwn(sessionRecord, 'turnIds'), false);
297
+ assert.match(await readFile(join(stateDir, 'agents/anima/inbox.json'), 'utf8'), /Persist this in the folder state/);
298
+ await assert.rejects(readFile(join(stateDir, 'state/agents/anima/items', ctx.item.id, 'item.json'), 'utf8'), /ENOENT/);
299
+ assert.match(await readFile(join(stateDir, 'agents/anima/activity.jsonl'), 'utf8'), /Folder activity/);
300
+ });
301
+ }
302
+ finally {
303
+ await rm(stateDir, { force: true, recursive: true });
304
+ }
305
+ });
306
+ test('inbox queue does not bootstrap home memory', async () => {
307
+ const stateDir = await mkdtemp(join(tmpdir(), 'anima-memory-bootstrap-test-'));
308
+ try {
309
+ const homePath = join(stateDir, 'agents', 'anima');
310
+ await withAnimaHome(stateDir, () => new WakeQueueService('anima').enqueue(makeSlackEvent({ channelId: 'D-anima', teamId: 'T-demo', text: 'Start', userId: 'U1' })));
311
+ await assert.rejects(readFile(join(homePath, 'MEMORY.md'), 'utf8'), /ENOENT/);
312
+ await assert.rejects(readFile(join(homePath, 'notes'), 'utf8'), /ENOENT/);
313
+ }
314
+ finally {
315
+ await rm(stateDir, { force: true, recursive: true });
316
+ }
317
+ });
318
+ function runtimeHostAgent(id, options) {
319
+ return {
320
+ createdAt: '2026-01-01T00:00:00.000Z',
321
+ enabled: options.enabled ?? true,
322
+ homePath: options.homePath ?? `/tmp/${id}`,
323
+ id,
324
+ profile: {
325
+ displayName: id,
326
+ role: options.role ?? 'general purpose',
327
+ },
328
+ provider: {
329
+ kind: 'claude-code',
330
+ model: options.model ?? 'opus',
331
+ },
332
+ slack: {
333
+ appToken: options.connected ? 'xapp-test' : '',
334
+ botToken: options.connected ? 'xoxb-test' : '',
335
+ connected: options.connected,
336
+ manifestVersion: 0,
337
+ teamId: options.connected ? 'T-test' : '',
338
+ workspaceIconUrl: '',
339
+ workspaceName: options.connected ? 'Test' : '',
340
+ },
341
+ };
342
+ }
343
+ function stopHandle(agentId, stopped, isActive = () => false) {
344
+ return {
345
+ isActive,
346
+ async stop() {
347
+ stopped.push(agentId);
348
+ },
349
+ };
350
+ }
351
+ const silentLogger = {
352
+ error() { },
353
+ log() { },
354
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=services.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.test.d.ts","sourceRoot":"","sources":["../../../server/tests/services.test.ts"],"names":[],"mappings":""}