kimaki 0.26.0 → 0.28.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 (302) hide show
  1. package/dist/agent-model.e2e.test.js +250 -164
  2. package/dist/anthropic-account-identity.js +9 -1
  3. package/dist/anthropic-auth-plugin.js +10 -13
  4. package/dist/anthropic-auth-plugin.test.js +17 -0
  5. package/dist/anthropic-auth-state.js +1 -0
  6. package/dist/bash-tool-schema-plugin.js +32 -0
  7. package/dist/bash-tool-schema-plugin.test.js +49 -0
  8. package/dist/bin.js +73 -19
  9. package/dist/cli-commands/maintenance.js +46 -7
  10. package/dist/cli-commands/project.js +11 -27
  11. package/dist/cli-commands/send.js +30 -1
  12. package/dist/cli-commands/session.js +322 -90
  13. package/dist/cli-commands/task.js +34 -15
  14. package/dist/cli-parsing.test.js +83 -4
  15. package/dist/cli-runner.js +44 -8
  16. package/dist/cli-runner.test.js +27 -0
  17. package/dist/cli-send-thread.e2e.test.js +9 -9
  18. package/dist/cli.js +32 -0
  19. package/dist/commands/abort.js +5 -1
  20. package/dist/commands/action-buttons.js +2 -1
  21. package/dist/commands/add-project.js +4 -7
  22. package/dist/commands/agent.js +164 -32
  23. package/dist/commands/ask-question.js +23 -33
  24. package/dist/commands/ask-question.test.js +43 -1
  25. package/dist/commands/btw.js +28 -6
  26. package/dist/commands/file-upload.js +2 -1
  27. package/dist/commands/fork-subagent.js +2 -4
  28. package/dist/commands/fork.js +4 -4
  29. package/dist/commands/last-sessions.js +17 -10
  30. package/dist/commands/login.js +6 -0
  31. package/dist/commands/merge-worktree.js +47 -14
  32. package/dist/commands/model-variant.js +8 -1
  33. package/dist/commands/model.js +16 -9
  34. package/dist/commands/multioauth.js +1 -0
  35. package/dist/commands/new-worktree.js +170 -102
  36. package/dist/commands/queue.js +3 -2
  37. package/dist/commands/resume.js +4 -4
  38. package/dist/commands/session.js +78 -63
  39. package/dist/commands/user-command.js +6 -8
  40. package/dist/commands/worktrees.js +5 -13
  41. package/dist/config.js +16 -1
  42. package/dist/cpu-profiler.js +211 -0
  43. package/dist/cpu-profiler.test.js +160 -0
  44. package/dist/database.js +152 -3
  45. package/dist/db.js +33 -0
  46. package/dist/db.test.js +267 -1
  47. package/dist/default-channel-provisioning.test.js +148 -0
  48. package/dist/discord-bot.js +517 -488
  49. package/dist/discord-command-registration.js +25 -10
  50. package/dist/discord-command-registration.test.js +17 -0
  51. package/dist/discord-utils.js +97 -5
  52. package/dist/discord-utils.test.js +151 -1
  53. package/dist/errors.js +10 -0
  54. package/dist/exec-async.js +17 -5
  55. package/dist/external-directory-permissions.e2e.test.js +11 -7
  56. package/dist/external-directory-project-deny.e2e.test.js +5 -3
  57. package/dist/external-opencode-sync.js +94 -17
  58. package/dist/external-opencode-sync.test.js +206 -0
  59. package/dist/extract-sdk-error-message.test.js +13 -0
  60. package/dist/file-edit-log.js +197 -0
  61. package/dist/file-edit-log.test.js +258 -0
  62. package/dist/format-tables.js +52 -0
  63. package/dist/format-tables.test.js +93 -0
  64. package/dist/gateway-proxy-reconnect.e2e.test.js +28 -8
  65. package/dist/gateway-proxy.e2e.test.js +18 -18
  66. package/dist/git-worktree-core.js +182 -17
  67. package/dist/interaction-handler.js +416 -398
  68. package/dist/ipc-tools-plugin.js +68 -3
  69. package/dist/kimaki-opencode-plugin-loading.e2e.test.js +122 -58
  70. package/dist/kimaki-opencode-plugin.js +9 -3
  71. package/dist/kimaki-workspace-adaptor.js +33 -10
  72. package/dist/logger.js +11 -1
  73. package/dist/markdown.js +3 -1
  74. package/dist/markdown.test.js +11 -3
  75. package/dist/message-formatting.js +181 -51
  76. package/dist/message-formatting.test.js +529 -22
  77. package/dist/message-preprocessing.js +105 -1
  78. package/dist/oauth-rotation-shared.js +10 -0
  79. package/dist/openai-auth-plugin.js +1 -0
  80. package/dist/openai-auth-state.js +1 -0
  81. package/dist/opencode-command.js +28 -0
  82. package/dist/opencode-command.test.js +109 -1
  83. package/dist/opencode-interrupt-plugin.js +4 -0
  84. package/dist/opencode-interrupt-plugin.test.js +24 -0
  85. package/dist/opencode.js +82 -15
  86. package/dist/project-add.test.js +62 -0
  87. package/dist/question-abort-clears-dropdown.e2e.test.js +89 -0
  88. package/dist/question-abort-retry-text-before-dropdown.e2e.test.js +150 -0
  89. package/dist/question-text-before-dropdown.e2e.test.js +52 -0
  90. package/dist/queue-advanced-abort.e2e.test.js +8 -10
  91. package/dist/queue-advanced-action-buttons.e2e.test.js +8 -8
  92. package/dist/queue-advanced-e2e-setup.js +78 -5
  93. package/dist/queue-advanced-footer.e2e.test.js +60 -269
  94. package/dist/queue-advanced-model-switch.e2e.test.js +9 -11
  95. package/dist/queue-advanced-permissions-typing.e2e.test.js +15 -15
  96. package/dist/queue-advanced-question.e2e.test.js +32 -27
  97. package/dist/queue-advanced-typing-interrupt.e2e.test.js +10 -12
  98. package/dist/queue-advanced-typing.e2e.test.js +7 -9
  99. package/dist/queue-delete-message.e2e.test.js +3 -3
  100. package/dist/queue-drain-after-interactive-ui.e2e.test.js +5 -5
  101. package/dist/queue-interrupt-drain.e2e.test.js +122 -12
  102. package/dist/queue-question-select-drain.e2e.test.js +34 -132
  103. package/dist/runtime-lifecycle.e2e.test.js +21 -51
  104. package/dist/schema.js +35 -1
  105. package/dist/session-handler/event-stream-state.js +391 -50
  106. package/dist/session-handler/event-stream-state.test.js +736 -8
  107. package/dist/session-handler/model-utils.js +172 -5
  108. package/dist/session-handler/model-utils.test.js +445 -0
  109. package/dist/session-handler/opencode-session-event-log.js +30 -1
  110. package/dist/session-handler/thread-ingress.test.js +41 -0
  111. package/dist/session-handler/thread-runtime-state.js +2 -0
  112. package/dist/session-handler/thread-session-runtime-summary.test.js +121 -0
  113. package/dist/session-handler/thread-session-runtime.js +574 -299
  114. package/dist/session-search.js +123 -4
  115. package/dist/session-search.test.js +248 -1
  116. package/dist/session-sleep.test.js +224 -0
  117. package/dist/session-title-rename.test.js +37 -39
  118. package/dist/sleep.e2e.test.js +494 -0
  119. package/dist/startup-time.e2e.test.js +2 -1
  120. package/dist/store.js +3 -0
  121. package/dist/system-message.js +102 -16
  122. package/dist/system-message.test.js +192 -15
  123. package/dist/task-runner.js +96 -5
  124. package/dist/task-schedule.js +87 -1
  125. package/dist/task-schedule.test.js +149 -1
  126. package/dist/test-utils.js +17 -9
  127. package/dist/thinking-utils.js +10 -0
  128. package/dist/thinking-utils.test.js +35 -0
  129. package/dist/thread-message-queue.e2e.test.js +65 -213
  130. package/dist/undo-redo.e2e.test.js +17 -9
  131. package/dist/voice-attachment.js +23 -1
  132. package/dist/voice-handler.js +21 -4
  133. package/dist/voice-message.e2e.test.js +144 -30
  134. package/dist/voice-routing-input.e2e.test.js +195 -0
  135. package/dist/voice-routing-question.e2e.test.js +125 -0
  136. package/dist/voice.js +28 -8
  137. package/dist/voice.test.js +96 -30
  138. package/dist/worktree-clone-isolation.e2e.test.js +188 -0
  139. package/dist/worktree-lifecycle.e2e.test.js +55 -36
  140. package/dist/worktrees.js +226 -443
  141. package/dist/worktrees.test.js +435 -58
  142. package/dist/xai-auth-plugin.js +1 -0
  143. package/dist/xai-auth-state.js +1 -0
  144. package/package.json +8 -6
  145. package/skills/bump-holocron-consumers/SKILL.md +137 -0
  146. package/skills/critique/SKILL.md +4 -4
  147. package/skills/errore/SKILL.md +60 -218
  148. package/skills/goke/SKILL.md +150 -10
  149. package/skills/holocron/SKILL.md +154 -17
  150. package/skills/npm-package/SKILL.md +55 -35
  151. package/skills/profano/SKILL.md +17 -2
  152. package/skills/spiceflow/SKILL.md +39 -3
  153. package/skills/strada/SKILL.md +39 -0
  154. package/skills/zele/SKILL.md +19 -0
  155. package/src/agent-model.e2e.test.ts +287 -186
  156. package/src/anthropic-account-identity.ts +20 -1
  157. package/src/anthropic-auth-plugin.test.ts +19 -0
  158. package/src/anthropic-auth-plugin.ts +12 -16
  159. package/src/anthropic-auth-state.ts +1 -0
  160. package/src/bash-tool-schema-plugin.test.ts +55 -0
  161. package/src/bash-tool-schema-plugin.ts +46 -0
  162. package/src/bin.ts +78 -26
  163. package/src/cli-commands/maintenance.ts +59 -13
  164. package/src/cli-commands/project.ts +14 -35
  165. package/src/cli-commands/send.ts +31 -1
  166. package/src/cli-commands/session.ts +374 -114
  167. package/src/cli-commands/task.ts +34 -15
  168. package/src/cli-parsing.test.ts +103 -4
  169. package/src/cli-runner.test.ts +49 -0
  170. package/src/cli-runner.ts +44 -7
  171. package/src/cli-send-thread.e2e.test.ts +9 -11
  172. package/src/cli.ts +53 -0
  173. package/src/commands/abort.ts +6 -1
  174. package/src/commands/action-buttons.ts +2 -1
  175. package/src/commands/add-project.ts +4 -8
  176. package/src/commands/agent.ts +205 -29
  177. package/src/commands/ask-question.test.ts +48 -1
  178. package/src/commands/ask-question.ts +23 -33
  179. package/src/commands/btw.ts +40 -5
  180. package/src/commands/file-upload.ts +2 -1
  181. package/src/commands/fork-subagent.ts +2 -3
  182. package/src/commands/fork.ts +4 -3
  183. package/src/commands/last-sessions.ts +10 -4
  184. package/src/commands/login.ts +6 -0
  185. package/src/commands/merge-worktree.ts +54 -16
  186. package/src/commands/model-variant.ts +9 -1
  187. package/src/commands/model.ts +20 -9
  188. package/src/commands/multioauth.ts +1 -0
  189. package/src/commands/new-worktree.ts +138 -55
  190. package/src/commands/queue.ts +3 -2
  191. package/src/commands/resume.ts +4 -3
  192. package/src/commands/session.ts +94 -70
  193. package/src/commands/user-command.ts +6 -6
  194. package/src/commands/worktrees.ts +5 -12
  195. package/src/config.ts +18 -1
  196. package/src/cpu-profiler.test.ts +183 -0
  197. package/src/cpu-profiler.ts +293 -0
  198. package/src/database.ts +235 -3
  199. package/src/db.test.ts +293 -0
  200. package/src/db.ts +38 -0
  201. package/src/default-channel-provisioning.test.ts +155 -0
  202. package/src/discord-bot.ts +78 -36
  203. package/src/discord-command-registration.test.ts +20 -0
  204. package/src/discord-command-registration.ts +42 -12
  205. package/src/discord-utils.test.ts +176 -0
  206. package/src/discord-utils.ts +152 -7
  207. package/src/errors.ts +12 -0
  208. package/src/exec-async.ts +20 -11
  209. package/src/external-directory-permissions.e2e.test.ts +11 -7
  210. package/src/external-directory-project-deny.e2e.test.ts +5 -3
  211. package/src/external-opencode-sync.test.ts +248 -0
  212. package/src/external-opencode-sync.ts +122 -19
  213. package/src/extract-sdk-error-message.test.ts +18 -0
  214. package/src/file-edit-log.test.ts +287 -0
  215. package/src/file-edit-log.ts +283 -0
  216. package/src/format-tables.test.ts +101 -0
  217. package/src/format-tables.ts +75 -0
  218. package/src/gateway-proxy-reconnect.e2e.test.ts +38 -8
  219. package/src/gateway-proxy.e2e.test.ts +18 -17
  220. package/src/git-worktree-core.ts +247 -20
  221. package/src/interaction-handler.ts +36 -13
  222. package/src/ipc-tools-plugin.ts +82 -5
  223. package/src/kimaki-opencode-plugin-loading.e2e.test.ts +188 -97
  224. package/src/kimaki-opencode-plugin.ts +9 -3
  225. package/src/kimaki-workspace-adaptor.ts +40 -11
  226. package/src/logger.ts +14 -4
  227. package/src/markdown.test.ts +13 -3
  228. package/src/markdown.ts +4 -1
  229. package/src/message-formatting.test.ts +672 -25
  230. package/src/message-formatting.ts +242 -53
  231. package/src/message-preprocessing.ts +116 -1
  232. package/src/oauth-rotation-shared.ts +10 -0
  233. package/src/openai-auth-plugin.ts +1 -0
  234. package/src/openai-auth-state.ts +1 -0
  235. package/src/opencode-command.test.ts +140 -0
  236. package/src/opencode-command.ts +38 -0
  237. package/src/opencode-interrupt-plugin.test.ts +32 -0
  238. package/src/opencode-interrupt-plugin.ts +3 -0
  239. package/src/opencode.ts +115 -15
  240. package/src/project-add.test.ts +80 -0
  241. package/src/question-abort-clears-dropdown.e2e.test.ts +109 -0
  242. package/src/question-abort-retry-text-before-dropdown.e2e.test.ts +172 -0
  243. package/src/question-text-before-dropdown.e2e.test.ts +67 -0
  244. package/src/queue-advanced-abort.e2e.test.ts +8 -9
  245. package/src/queue-advanced-action-buttons.e2e.test.ts +8 -8
  246. package/src/queue-advanced-e2e-setup.ts +83 -4
  247. package/src/queue-advanced-footer.e2e.test.ts +67 -315
  248. package/src/queue-advanced-model-switch.e2e.test.ts +9 -10
  249. package/src/queue-advanced-permissions-typing.e2e.test.ts +15 -15
  250. package/src/queue-advanced-question.e2e.test.ts +36 -30
  251. package/src/queue-advanced-typing-interrupt.e2e.test.ts +11 -11
  252. package/src/queue-advanced-typing.e2e.test.ts +8 -8
  253. package/src/queue-delete-message.e2e.test.ts +3 -3
  254. package/src/queue-drain-after-interactive-ui.e2e.test.ts +5 -5
  255. package/src/queue-interrupt-drain.e2e.test.ts +133 -12
  256. package/src/queue-question-select-drain.e2e.test.ts +36 -161
  257. package/src/runtime-lifecycle.e2e.test.ts +21 -54
  258. package/src/schema.sql +15 -2
  259. package/src/schema.ts +37 -1
  260. package/src/session-handler/event-stream-state.test.ts +790 -7
  261. package/src/session-handler/event-stream-state.ts +568 -53
  262. package/src/session-handler/model-utils.test.ts +537 -0
  263. package/src/session-handler/model-utils.ts +281 -5
  264. package/src/session-handler/opencode-session-event-log.ts +31 -1
  265. package/src/session-handler/thread-ingress.test.ts +54 -0
  266. package/src/session-handler/thread-runtime-state.ts +3 -0
  267. package/src/session-handler/thread-session-runtime-summary.test.ts +169 -0
  268. package/src/session-handler/thread-session-runtime.ts +712 -358
  269. package/src/session-search.test.ts +281 -0
  270. package/src/session-search.ts +241 -7
  271. package/src/session-sleep.test.ts +294 -0
  272. package/src/session-title-rename.test.ts +54 -48
  273. package/src/sleep.e2e.test.ts +565 -0
  274. package/src/startup-time.e2e.test.ts +5 -1
  275. package/src/store.ts +22 -0
  276. package/src/system-message.test.ts +226 -15
  277. package/src/system-message.ts +130 -16
  278. package/src/task-runner.ts +138 -3
  279. package/src/task-schedule.test.ts +170 -0
  280. package/src/task-schedule.ts +141 -1
  281. package/src/test-utils.ts +21 -10
  282. package/src/thinking-utils.test.ts +46 -0
  283. package/src/thinking-utils.ts +21 -0
  284. package/src/thread-message-queue.e2e.test.ts +65 -245
  285. package/src/undo-redo.e2e.test.ts +17 -9
  286. package/src/voice-attachment.ts +29 -1
  287. package/src/voice-handler.ts +23 -4
  288. package/src/voice-message.e2e.test.ts +149 -29
  289. package/src/voice-routing-input.e2e.test.ts +203 -0
  290. package/src/voice-routing-question.e2e.test.ts +131 -0
  291. package/src/voice.test.ts +107 -29
  292. package/src/voice.ts +34 -6
  293. package/src/worktree-clone-isolation.e2e.test.ts +213 -0
  294. package/src/worktree-lifecycle.e2e.test.ts +55 -35
  295. package/src/worktrees.test.ts +505 -64
  296. package/src/worktrees.ts +257 -603
  297. package/src/xai-auth-plugin.ts +1 -0
  298. package/src/xai-auth-state.ts +1 -0
  299. package/dist/message-finish-field.e2e.test.js +0 -169
  300. package/dist/subagent-rate-limit-plugin.js +0 -184
  301. package/src/message-finish-field.e2e.test.ts +0 -196
  302. package/src/subagent-rate-limit-plugin.ts +0 -228
@@ -10,6 +10,34 @@ let eventLogWriteDisabled = false;
10
10
  export function isOpencodeSessionEventLogEnabled() {
11
11
  return process.env['KIMAKI_LOG_OPENCODE_SESSION_EVENTS'] === '1';
12
12
  }
13
+ function sessionIdFromEventProperties(properties) {
14
+ if (!properties || typeof properties !== 'object') {
15
+ return undefined;
16
+ }
17
+ const sessionID = Reflect.get(properties, 'sessionID');
18
+ if (typeof sessionID === 'string') {
19
+ return sessionID;
20
+ }
21
+ const info = Reflect.get(properties, 'info');
22
+ if (info && typeof info === 'object') {
23
+ const id = Reflect.get(info, 'id');
24
+ if (typeof id === 'string') {
25
+ return id;
26
+ }
27
+ const infoSessionId = Reflect.get(info, 'sessionID');
28
+ if (typeof infoSessionId === 'string') {
29
+ return infoSessionId;
30
+ }
31
+ }
32
+ const part = Reflect.get(properties, 'part');
33
+ if (part && typeof part === 'object') {
34
+ const partSessionId = Reflect.get(part, 'sessionID');
35
+ if (typeof partSessionId === 'string') {
36
+ return partSessionId;
37
+ }
38
+ }
39
+ return undefined;
40
+ }
13
41
  export function getOpencodeEventSessionId(event) {
14
42
  switch (event.type) {
15
43
  case 'message.updated':
@@ -34,7 +62,8 @@ export function getOpencodeEventSessionId(event) {
34
62
  case 'session.deleted':
35
63
  return event.properties.info.id;
36
64
  default:
37
- return undefined;
65
+ // session.next.* and later SDK events put sessionID on properties.
66
+ return sessionIdFromEventProperties(event.properties);
38
67
  }
39
68
  }
40
69
  function sanitizeSessionIdForFilename(sessionId) {
@@ -0,0 +1,41 @@
1
+ // Unit tests for the per-thread Discord arrival FIFO.
2
+ // /plan-agent and a follow-up message must keep Discord order.
3
+ import { describe, expect, test } from 'vitest';
4
+ import { reserveThreadIngress, runInThreadIngressSlot, waitForCurrentThreadIngress, } from './thread-session-runtime.js';
5
+ describe('thread ingress FIFO', () => {
6
+ test('later waiters stay behind an unreleased earlier slot', async () => {
7
+ const first = reserveThreadIngress('thread-fifo');
8
+ const second = reserveThreadIngress('thread-fifo');
9
+ const order = [];
10
+ const firstWork = runInThreadIngressSlot(first, async () => {
11
+ await waitForCurrentThreadIngress();
12
+ order.push('first-write');
13
+ first.release();
14
+ await new Promise((resolve) => setTimeout(resolve, 20));
15
+ order.push('first-work');
16
+ });
17
+ const secondWork = runInThreadIngressSlot(second, async () => {
18
+ await waitForCurrentThreadIngress();
19
+ order.push('second');
20
+ second.release();
21
+ });
22
+ await Promise.all([firstWork, secondWork]);
23
+ expect(order).toEqual(['first-write', 'second', 'first-work']);
24
+ });
25
+ test('runInThreadIngressSlot releases after a thrown callback', async () => {
26
+ const first = reserveThreadIngress('thread-fifo-throw');
27
+ const second = reserveThreadIngress('thread-fifo-throw');
28
+ const order = [];
29
+ const firstWork = runInThreadIngressSlot(first, async () => {
30
+ await waitForCurrentThreadIngress();
31
+ order.push('first');
32
+ throw new Error('boom');
33
+ }).catch(() => { });
34
+ const secondWork = runInThreadIngressSlot(second, async () => {
35
+ await waitForCurrentThreadIngress();
36
+ order.push('second');
37
+ });
38
+ await Promise.all([firstWork, secondWork]);
39
+ expect(order).toEqual(['first', 'second']);
40
+ });
41
+ });
@@ -71,6 +71,8 @@ export function setSessionUsername(threadId, username) {
71
71
  });
72
72
  }
73
73
  export function setSessionUserId(threadId, userId) {
74
+ if (!userId)
75
+ return;
74
76
  updateThread(threadId, (t) => {
75
77
  if (t.sessionUserId) {
76
78
  return t;
@@ -0,0 +1,121 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { ThreadSessionRuntime } from './thread-session-runtime.js';
3
+ function summaryMessageEvent({ sessionId, messageId, }) {
4
+ return {
5
+ id: `evt_${messageId}`,
6
+ type: 'message.updated',
7
+ properties: {
8
+ sessionID: sessionId,
9
+ info: {
10
+ id: messageId,
11
+ sessionID: sessionId,
12
+ role: 'assistant',
13
+ parentID: 'msg_user',
14
+ time: { created: 2, completed: 3 },
15
+ modelID: 'compaction-model',
16
+ providerID: 'test-provider',
17
+ mode: 'compaction',
18
+ agent: 'compaction',
19
+ path: { cwd: '/test', root: '/test' },
20
+ cost: 1,
21
+ tokens: {
22
+ input: 10,
23
+ output: 1,
24
+ reasoning: 0,
25
+ cache: { read: 0, write: 0 },
26
+ },
27
+ finish: 'stop',
28
+ summary: true,
29
+ },
30
+ },
31
+ };
32
+ }
33
+ function summaryTextPart({ sessionId, messageId, }) {
34
+ return {
35
+ id: `prt_${messageId}`,
36
+ sessionID: sessionId,
37
+ messageID: messageId,
38
+ type: 'text',
39
+ text: 'internal compaction summary must not reach Discord',
40
+ time: { start: 2, end: 3 },
41
+ };
42
+ }
43
+ describe('ThreadSessionRuntime compaction summary routing', () => {
44
+ test('drops summary message parts before fallback buffering or completion', async () => {
45
+ const sessionId = 'ses_main';
46
+ const messageId = 'msg_summary_embedded_parts';
47
+ const event = summaryMessageEvent({ sessionId, messageId });
48
+ if (event.type !== 'message.updated') {
49
+ throw new Error('Expected summary message event');
50
+ }
51
+ const message = {
52
+ ...event.properties.info,
53
+ parts: [summaryTextPart({ sessionId, messageId })],
54
+ };
55
+ const runtime = Object.create(ThreadSessionRuntime.prototype);
56
+ Object.defineProperty(runtime, 'state', {
57
+ value: { sessionId },
58
+ configurable: true,
59
+ });
60
+ runtime.eventBuffer = [{ event, timestamp: 1 }];
61
+ runtime.partBuffer = new Map();
62
+ runtime.flushBufferedParts = async () => {
63
+ throw new Error('Summary message must not flush parts');
64
+ };
65
+ runtime.handleNaturalAssistantCompletion = async () => {
66
+ throw new Error('Summary message must not complete the Discord turn');
67
+ };
68
+ await runtime.handleMessageUpdated(message);
69
+ expect(runtime.partBuffer.size).toBe(0);
70
+ });
71
+ test.each([
72
+ ['main session', 'ses_main'],
73
+ ['subagent session', 'ses_child'],
74
+ ])('preserves summary identity and drops %s parts before buffering', async (_, sessionId) => {
75
+ const messageId = `msg_summary_${sessionId}`;
76
+ const runtime = Object.create(ThreadSessionRuntime.prototype);
77
+ const compacted = runtime.compactEventForEventBuffer(summaryMessageEvent({ sessionId, messageId }));
78
+ expect(compacted?.type).toBe('message.updated');
79
+ if (!compacted || compacted.type !== 'message.updated') {
80
+ throw new Error('Expected compacted summary message event');
81
+ }
82
+ expect(compacted.properties.info.summary).toBe(true);
83
+ runtime.eventBuffer = [{ event: compacted, timestamp: 1 }];
84
+ runtime.partBuffer = new Map();
85
+ await runtime.handlePartUpdated(summaryTextPart({ sessionId, messageId }));
86
+ expect(runtime.partBuffer.size).toBe(0);
87
+ });
88
+ test.each([
89
+ ['main session', 'ses_main'],
90
+ ['subagent session', 'ses_child'],
91
+ ])('holds %s parts until summary identity arrives, then drops them', async (_, sessionId) => {
92
+ const messageId = `msg_summary_unknown_${sessionId}`;
93
+ const runtime = Object.create(ThreadSessionRuntime.prototype);
94
+ Object.defineProperty(runtime, 'state', {
95
+ value: { sessionId: 'ses_main' },
96
+ configurable: true,
97
+ });
98
+ runtime.eventBuffer = [];
99
+ runtime.partBuffer = new Map();
100
+ runtime.handleMainPart = async () => {
101
+ throw new Error('Unknown summary parts must not render');
102
+ };
103
+ runtime.handleSubtaskPart = async () => {
104
+ throw new Error('Unknown summary parts must not render');
105
+ };
106
+ runtime.flushBufferedParts = async () => {
107
+ throw new Error('Summary message must not flush parts');
108
+ };
109
+ runtime.handleNaturalAssistantCompletion = async () => {
110
+ throw new Error('Summary message must not complete the Discord turn');
111
+ };
112
+ await runtime.handlePartUpdated(summaryTextPart({ sessionId, messageId }));
113
+ expect(runtime.partBuffer.get(messageId)?.size).toBe(1);
114
+ const event = summaryMessageEvent({ sessionId, messageId });
115
+ if (event.type !== 'message.updated') {
116
+ throw new Error('Expected summary message event');
117
+ }
118
+ await runtime.handleMessageUpdated(event.properties.info);
119
+ expect(runtime.partBuffer.size).toBe(0);
120
+ });
121
+ });