sensorium-mcp 3.1.2 → 3.1.3-board.10

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 (625) hide show
  1. package/Install-Sensorium.ps1 +257 -43
  2. package/README.md +2 -0
  3. package/dist/board/api/api.js +84 -0
  4. package/dist/board/api/api.test.ts +23 -0
  5. package/dist/board/api/client.js +113 -0
  6. package/dist/board/components/BottomNav.js +32 -0
  7. package/dist/board/components/Card.js +78 -0
  8. package/dist/board/components/Combobox.js +79 -0
  9. package/dist/board/components/CommentComposer.js +29 -0
  10. package/dist/board/components/CommentList.js +42 -0
  11. package/dist/board/components/DirTreePicker.js +128 -0
  12. package/dist/board/components/Dropdown.js +67 -0
  13. package/dist/board/components/Lane.js +47 -0
  14. package/dist/board/components/PriorityChip.js +20 -0
  15. package/dist/board/components/ProfileSummary.js +89 -0
  16. package/dist/board/components/StateBanner.js +24 -0
  17. package/dist/board/components/StatusChip.js +26 -0
  18. package/dist/board/components/rateLimit.js +32 -0
  19. package/dist/board/components/rateLimit.test.ts +16 -0
  20. package/dist/board/components/time.js +14 -0
  21. package/dist/board/config.js +56 -0
  22. package/dist/board/index.html +18 -0
  23. package/dist/board/main.js +37 -0
  24. package/dist/board/mock/api.mock.js +491 -0
  25. package/dist/board/mock/api.mock.test.ts +460 -0
  26. package/dist/board/mock/fixtures.js +512 -0
  27. package/dist/board/schema/policy.js +261 -0
  28. package/dist/board/state/recur.js +24 -0
  29. package/dist/board/state/recur.test.ts +24 -0
  30. package/dist/board/state/useBoard.js +161 -0
  31. package/dist/board/state/useBuilder.js +357 -0
  32. package/dist/board/state/useBuilder.test.ts +468 -0
  33. package/dist/board/state/useCard.js +193 -0
  34. package/dist/board/state/useCard.test.ts +435 -0
  35. package/dist/board/state/useDirBrowser.js +23 -0
  36. package/dist/board/styles/board.css +2885 -0
  37. package/dist/board/styles/tokens.css +72 -0
  38. package/dist/board/vendor/vue.esm-browser.js +9 -0
  39. package/dist/board/views/App.js +61 -0
  40. package/dist/board/views/BoardView.js +153 -0
  41. package/dist/board/views/BuilderView.js +582 -0
  42. package/dist/board/views/CardView.js +382 -0
  43. package/dist/board/views/SettingsView.js +152 -0
  44. package/dist/config/agent-settings.d.ts +11 -0
  45. package/dist/config/agent-settings.d.ts.map +1 -1
  46. package/dist/config/agent-settings.js +79 -0
  47. package/dist/config/agent-settings.js.map +1 -1
  48. package/dist/config/detail-grant.d.ts +7 -16
  49. package/dist/config/detail-grant.d.ts.map +1 -1
  50. package/dist/config/detail-grant.js +12 -35
  51. package/dist/config/detail-grant.js.map +1 -1
  52. package/dist/config/index.d.ts +2 -0
  53. package/dist/config/index.d.ts.map +1 -1
  54. package/dist/config/index.js +17 -0
  55. package/dist/config/index.js.map +1 -1
  56. package/dist/config/mcp-settings.d.ts +7 -1
  57. package/dist/config/mcp-settings.d.ts.map +1 -1
  58. package/dist/config/mcp-settings.js +9 -0
  59. package/dist/config/mcp-settings.js.map +1 -1
  60. package/dist/config/misc-settings.d.ts +22 -1
  61. package/dist/config/misc-settings.d.ts.map +1 -1
  62. package/dist/config/misc-settings.js +93 -3
  63. package/dist/config/misc-settings.js.map +1 -1
  64. package/dist/config/operator-grant.d.ts +9 -0
  65. package/dist/config/operator-grant.d.ts.map +1 -0
  66. package/dist/config/operator-grant.js +24 -0
  67. package/dist/config/operator-grant.js.map +1 -0
  68. package/dist/config/voice-grant.d.ts +10 -0
  69. package/dist/config/voice-grant.d.ts.map +1 -0
  70. package/dist/config/voice-grant.js +18 -0
  71. package/dist/config/voice-grant.js.map +1 -0
  72. package/dist/dashboard/board-static.d.ts +22 -0
  73. package/dist/dashboard/board-static.d.ts.map +1 -0
  74. package/dist/dashboard/board-static.js +130 -0
  75. package/dist/dashboard/board-static.js.map +1 -0
  76. package/dist/dashboard/presets.d.ts +1 -1
  77. package/dist/dashboard/presets.d.ts.map +1 -1
  78. package/dist/dashboard/presets.js +1 -1
  79. package/dist/dashboard/presets.js.map +1 -1
  80. package/dist/dashboard/routes/agent-templates.d.ts.map +1 -1
  81. package/dist/dashboard/routes/agent-templates.js +6 -9
  82. package/dist/dashboard/routes/agent-templates.js.map +1 -1
  83. package/dist/dashboard/routes/board-body.d.ts +8 -0
  84. package/dist/dashboard/routes/board-body.d.ts.map +1 -0
  85. package/dist/dashboard/routes/board-body.js +20 -0
  86. package/dist/dashboard/routes/board-body.js.map +1 -0
  87. package/dist/dashboard/routes/board-dir-listing.d.ts +70 -0
  88. package/dist/dashboard/routes/board-dir-listing.d.ts.map +1 -0
  89. package/dist/dashboard/routes/board-dir-listing.js +192 -0
  90. package/dist/dashboard/routes/board-dir-listing.js.map +1 -0
  91. package/dist/dashboard/routes/board-lane-groups.d.ts +6 -0
  92. package/dist/dashboard/routes/board-lane-groups.d.ts.map +1 -0
  93. package/dist/dashboard/routes/board-lane-groups.js +52 -0
  94. package/dist/dashboard/routes/board-lane-groups.js.map +1 -0
  95. package/dist/dashboard/routes/board-policy.d.ts +224 -0
  96. package/dist/dashboard/routes/board-policy.d.ts.map +1 -0
  97. package/dist/dashboard/routes/board-policy.js +214 -0
  98. package/dist/dashboard/routes/board-policy.js.map +1 -0
  99. package/dist/dashboard/routes/board-profile-parse.d.ts +32 -0
  100. package/dist/dashboard/routes/board-profile-parse.d.ts.map +1 -0
  101. package/dist/dashboard/routes/board-profile-parse.js +338 -0
  102. package/dist/dashboard/routes/board-profile-parse.js.map +1 -0
  103. package/dist/dashboard/routes/board.d.ts +90 -0
  104. package/dist/dashboard/routes/board.d.ts.map +1 -0
  105. package/dist/dashboard/routes/board.js +721 -0
  106. package/dist/dashboard/routes/board.js.map +1 -0
  107. package/dist/dashboard/routes/codex.d.ts +14 -0
  108. package/dist/dashboard/routes/codex.d.ts.map +1 -0
  109. package/dist/dashboard/routes/codex.js +92 -0
  110. package/dist/dashboard/routes/codex.js.map +1 -0
  111. package/dist/dashboard/routes/prompts.d.ts +2 -2
  112. package/dist/dashboard/routes/prompts.d.ts.map +1 -1
  113. package/dist/dashboard/routes/prompts.js +2 -2
  114. package/dist/dashboard/routes/prompts.js.map +1 -1
  115. package/dist/dashboard/routes/settings.d.ts +2 -0
  116. package/dist/dashboard/routes/settings.d.ts.map +1 -1
  117. package/dist/dashboard/routes/settings.js +24 -1
  118. package/dist/dashboard/routes/settings.js.map +1 -1
  119. package/dist/dashboard/routes/skills.d.ts +7 -1
  120. package/dist/dashboard/routes/skills.d.ts.map +1 -1
  121. package/dist/dashboard/routes/skills.js +6 -6
  122. package/dist/dashboard/routes/skills.js.map +1 -1
  123. package/dist/dashboard/routes/snapshots.d.ts +2 -1
  124. package/dist/dashboard/routes/snapshots.d.ts.map +1 -1
  125. package/dist/dashboard/routes/snapshots.js +24 -18
  126. package/dist/dashboard/routes/snapshots.js.map +1 -1
  127. package/dist/dashboard/routes/templates.js +5 -5
  128. package/dist/dashboard/routes/templates.js.map +1 -1
  129. package/dist/dashboard/routes/threads-create.d.ts +17 -0
  130. package/dist/dashboard/routes/threads-create.d.ts.map +1 -1
  131. package/dist/dashboard/routes/threads-create.js +32 -10
  132. package/dist/dashboard/routes/threads-create.js.map +1 -1
  133. package/dist/dashboard/routes/threads-helpers.d.ts +11 -2
  134. package/dist/dashboard/routes/threads-helpers.d.ts.map +1 -1
  135. package/dist/dashboard/routes/threads-helpers.js +20 -3
  136. package/dist/dashboard/routes/threads-helpers.js.map +1 -1
  137. package/dist/dashboard/routes/threads-read.d.ts +16 -4
  138. package/dist/dashboard/routes/threads-read.d.ts.map +1 -1
  139. package/dist/dashboard/routes/threads-read.js +61 -55
  140. package/dist/dashboard/routes/threads-read.js.map +1 -1
  141. package/dist/dashboard/routes/threads.d.ts +2 -2
  142. package/dist/dashboard/routes/threads.d.ts.map +1 -1
  143. package/dist/dashboard/routes/threads.js +143 -29
  144. package/dist/dashboard/routes/threads.js.map +1 -1
  145. package/dist/dashboard/routes/types.d.ts +6 -0
  146. package/dist/dashboard/routes/types.d.ts.map +1 -1
  147. package/dist/dashboard/routes/types.js.map +1 -1
  148. package/dist/dashboard/routes.d.ts +2 -2
  149. package/dist/dashboard/routes.d.ts.map +1 -1
  150. package/dist/dashboard/routes.js +85 -7
  151. package/dist/dashboard/routes.js.map +1 -1
  152. package/dist/dashboard/spa.html +15 -12
  153. package/dist/data/board/board-schema.d.ts +29 -0
  154. package/dist/data/board/board-schema.d.ts.map +1 -0
  155. package/dist/data/board/board-schema.js +202 -0
  156. package/dist/data/board/board-schema.js.map +1 -0
  157. package/dist/data/board/board-types.d.ts +175 -0
  158. package/dist/data/board/board-types.d.ts.map +1 -0
  159. package/dist/data/board/board-types.js +9 -0
  160. package/dist/data/board/board-types.js.map +1 -0
  161. package/dist/data/board/card.repository.d.ts +137 -0
  162. package/dist/data/board/card.repository.d.ts.map +1 -0
  163. package/dist/data/board/card.repository.js +470 -0
  164. package/dist/data/board/card.repository.js.map +1 -0
  165. package/dist/data/board/comment.repository.d.ts +14 -0
  166. package/dist/data/board/comment.repository.d.ts.map +1 -0
  167. package/dist/data/board/comment.repository.js +42 -0
  168. package/dist/data/board/comment.repository.js.map +1 -0
  169. package/dist/data/board/lane-group.repository.d.ts +19 -0
  170. package/dist/data/board/lane-group.repository.d.ts.map +1 -0
  171. package/dist/data/board/lane-group.repository.js +66 -0
  172. package/dist/data/board/lane-group.repository.js.map +1 -0
  173. package/dist/data/board/profile-defaults.d.ts +17 -0
  174. package/dist/data/board/profile-defaults.d.ts.map +1 -0
  175. package/dist/data/board/profile-defaults.js +107 -0
  176. package/dist/data/board/profile-defaults.js.map +1 -0
  177. package/dist/data/interfaces.d.ts +1 -0
  178. package/dist/data/interfaces.d.ts.map +1 -1
  179. package/dist/data/memory/bootstrap.d.ts +1 -8
  180. package/dist/data/memory/bootstrap.d.ts.map +1 -1
  181. package/dist/data/memory/bootstrap.js +27 -37
  182. package/dist/data/memory/bootstrap.js.map +1 -1
  183. package/dist/data/memory/consolidation.d.ts +9 -31
  184. package/dist/data/memory/consolidation.d.ts.map +1 -1
  185. package/dist/data/memory/consolidation.js +22 -32
  186. package/dist/data/memory/consolidation.js.map +1 -1
  187. package/dist/data/memory/db-maintenance.d.ts +54 -0
  188. package/dist/data/memory/db-maintenance.d.ts.map +1 -0
  189. package/dist/data/memory/db-maintenance.js +81 -0
  190. package/dist/data/memory/db-maintenance.js.map +1 -0
  191. package/dist/data/memory/episodes.d.ts +2 -1
  192. package/dist/data/memory/episodes.d.ts.map +1 -1
  193. package/dist/data/memory/episodes.js +5 -1
  194. package/dist/data/memory/episodes.js.map +1 -1
  195. package/dist/data/memory/index.d.ts +0 -4
  196. package/dist/data/memory/index.d.ts.map +1 -1
  197. package/dist/data/memory/index.js +0 -4
  198. package/dist/data/memory/index.js.map +1 -1
  199. package/dist/data/memory/key-knowledge.d.ts +51 -0
  200. package/dist/data/memory/key-knowledge.d.ts.map +1 -0
  201. package/dist/data/memory/key-knowledge.js +128 -0
  202. package/dist/data/memory/key-knowledge.js.map +1 -0
  203. package/dist/data/memory/migration-runner.d.ts +1 -1
  204. package/dist/data/memory/migration-runner.d.ts.map +1 -1
  205. package/dist/data/memory/migration-runner.js +407 -1
  206. package/dist/data/memory/migration-runner.js.map +1 -1
  207. package/dist/data/memory/narrative-lint.d.ts +2 -2
  208. package/dist/data/memory/narrative-lint.d.ts.map +1 -1
  209. package/dist/data/memory/narrative-lint.js +23 -13
  210. package/dist/data/memory/narrative-lint.js.map +1 -1
  211. package/dist/data/memory/narrative-resolutions.d.ts +9 -2
  212. package/dist/data/memory/narrative-resolutions.d.ts.map +1 -1
  213. package/dist/data/memory/narrative-resolutions.js +9 -8
  214. package/dist/data/memory/narrative-resolutions.js.map +1 -1
  215. package/dist/data/memory/narrative-sources.d.ts +1 -1
  216. package/dist/data/memory/narrative-sources.d.ts.map +1 -1
  217. package/dist/data/memory/narrative-sources.js +8 -3
  218. package/dist/data/memory/narrative-sources.js.map +1 -1
  219. package/dist/data/memory/narrative-store.d.ts +24 -0
  220. package/dist/data/memory/narrative-store.d.ts.map +1 -1
  221. package/dist/data/memory/narrative-store.js +73 -35
  222. package/dist/data/memory/narrative-store.js.map +1 -1
  223. package/dist/data/memory/narrative.d.ts.map +1 -1
  224. package/dist/data/memory/narrative.js +47 -29
  225. package/dist/data/memory/narrative.js.map +1 -1
  226. package/dist/data/memory/note-archival.d.ts +1 -1
  227. package/dist/data/memory/note-archival.d.ts.map +1 -1
  228. package/dist/data/memory/note-archival.js +7 -3
  229. package/dist/data/memory/note-archival.js.map +1 -1
  230. package/dist/data/memory/note-embeddings.d.ts +7 -0
  231. package/dist/data/memory/note-embeddings.d.ts.map +1 -1
  232. package/dist/data/memory/note-embeddings.js +6 -2
  233. package/dist/data/memory/note-embeddings.js.map +1 -1
  234. package/dist/data/memory/passive-notice.repository.d.ts +70 -0
  235. package/dist/data/memory/passive-notice.repository.d.ts.map +1 -0
  236. package/dist/data/memory/passive-notice.repository.js +85 -0
  237. package/dist/data/memory/passive-notice.repository.js.map +1 -0
  238. package/dist/data/memory/schema-ddl.d.ts +120 -1
  239. package/dist/data/memory/schema-ddl.d.ts.map +1 -1
  240. package/dist/data/memory/schema-ddl.js +171 -4
  241. package/dist/data/memory/schema-ddl.js.map +1 -1
  242. package/dist/data/memory/schema-guard.d.ts.map +1 -1
  243. package/dist/data/memory/schema-guard.js +5 -41
  244. package/dist/data/memory/schema-guard.js.map +1 -1
  245. package/dist/data/memory/schema.d.ts.map +1 -1
  246. package/dist/data/memory/schema.js +10 -34
  247. package/dist/data/memory/schema.js.map +1 -1
  248. package/dist/data/memory/semantic-notes.d.ts +13 -8
  249. package/dist/data/memory/semantic-notes.d.ts.map +1 -1
  250. package/dist/data/memory/semantic-notes.js +12 -17
  251. package/dist/data/memory/semantic-notes.js.map +1 -1
  252. package/dist/data/memory/thread-registry.d.ts +330 -15
  253. package/dist/data/memory/thread-registry.d.ts.map +1 -1
  254. package/dist/data/memory/thread-registry.js +523 -67
  255. package/dist/data/memory/thread-registry.js.map +1 -1
  256. package/dist/data/memory/utils.d.ts +6 -0
  257. package/dist/data/memory/utils.d.ts.map +1 -1
  258. package/dist/data/memory/utils.js +13 -0
  259. package/dist/data/memory/utils.js.map +1 -1
  260. package/dist/data/memory/worker-lifecycle.repository.d.ts +308 -0
  261. package/dist/data/memory/worker-lifecycle.repository.d.ts.map +1 -0
  262. package/dist/data/memory/worker-lifecycle.repository.js +428 -0
  263. package/dist/data/memory/worker-lifecycle.repository.js.map +1 -0
  264. package/dist/data/optional.d.ts +16 -0
  265. package/dist/data/optional.d.ts.map +1 -0
  266. package/dist/data/optional.js +47 -0
  267. package/dist/data/optional.js.map +1 -0
  268. package/dist/index.js +156 -24
  269. package/dist/index.js.map +1 -1
  270. package/dist/integrations/openai/speech.d.ts.map +1 -1
  271. package/dist/integrations/openai/speech.js +52 -19
  272. package/dist/integrations/openai/speech.js.map +1 -1
  273. package/dist/integrations/openai/video.d.ts +1 -1
  274. package/dist/integrations/openai/video.d.ts.map +1 -1
  275. package/dist/integrations/openai/video.js +13 -8
  276. package/dist/integrations/openai/video.js.map +1 -1
  277. package/dist/integrations/telegram/types.d.ts +11 -0
  278. package/dist/integrations/telegram/types.d.ts.map +1 -1
  279. package/dist/lib/http-retry.d.ts +91 -0
  280. package/dist/lib/http-retry.d.ts.map +1 -0
  281. package/dist/lib/http-retry.js +177 -0
  282. package/dist/lib/http-retry.js.map +1 -0
  283. package/dist/lib/mcp-http-port.d.ts +12 -0
  284. package/dist/lib/mcp-http-port.d.ts.map +1 -0
  285. package/dist/lib/mcp-http-port.js +19 -0
  286. package/dist/lib/mcp-http-port.js.map +1 -0
  287. package/dist/lib/paths.d.ts +29 -4
  288. package/dist/lib/paths.d.ts.map +1 -1
  289. package/dist/lib/paths.js +31 -4
  290. package/dist/lib/paths.js.map +1 -1
  291. package/dist/lib/types.d.ts +2 -0
  292. package/dist/lib/types.d.ts.map +1 -1
  293. package/dist/lib/usage-metering/pricing.js +1 -1
  294. package/dist/lib/usage-metering/pricing.js.map +1 -1
  295. package/dist/lib/usage-metering/sink.d.ts +4 -0
  296. package/dist/lib/usage-metering/sink.d.ts.map +1 -1
  297. package/dist/lib/usage-metering/sink.js +6 -4
  298. package/dist/lib/usage-metering/sink.js.map +1 -1
  299. package/dist/lib/utils.d.ts +31 -0
  300. package/dist/lib/utils.d.ts.map +1 -1
  301. package/dist/lib/utils.js +49 -0
  302. package/dist/lib/utils.js.map +1 -1
  303. package/dist/llm/prompts/narrative.d.ts.map +1 -1
  304. package/dist/llm/prompts/narrative.js +27 -86
  305. package/dist/llm/prompts/narrative.js.map +1 -1
  306. package/dist/log-parsing/log-events.d.ts +38 -0
  307. package/dist/log-parsing/log-events.d.ts.map +1 -0
  308. package/dist/log-parsing/log-events.js +2 -0
  309. package/dist/log-parsing/log-events.js.map +1 -0
  310. package/dist/log-parsing/parser-registry.d.ts +4 -0
  311. package/dist/log-parsing/parser-registry.d.ts.map +1 -0
  312. package/dist/log-parsing/parser-registry.js +18 -0
  313. package/dist/log-parsing/parser-registry.js.map +1 -0
  314. package/dist/log-parsing/parsers/claude.d.ts +6 -0
  315. package/dist/log-parsing/parsers/claude.d.ts.map +1 -0
  316. package/dist/log-parsing/parsers/claude.js +100 -0
  317. package/dist/log-parsing/parsers/claude.js.map +1 -0
  318. package/dist/log-parsing/parsers/codex.d.ts +6 -0
  319. package/dist/log-parsing/parsers/codex.d.ts.map +1 -0
  320. package/dist/log-parsing/parsers/codex.js +116 -0
  321. package/dist/log-parsing/parsers/codex.js.map +1 -0
  322. package/dist/log-parsing/parsers/copilot.d.ts +6 -0
  323. package/dist/log-parsing/parsers/copilot.d.ts.map +1 -0
  324. package/dist/log-parsing/parsers/copilot.js +65 -0
  325. package/dist/log-parsing/parsers/copilot.js.map +1 -0
  326. package/dist/log-parsing/parsers/json-lines.d.ts +9 -0
  327. package/dist/log-parsing/parsers/json-lines.d.ts.map +1 -0
  328. package/dist/log-parsing/parsers/json-lines.js +25 -0
  329. package/dist/log-parsing/parsers/json-lines.js.map +1 -0
  330. package/dist/log-parsing/render.d.ts +33 -0
  331. package/dist/log-parsing/render.d.ts.map +1 -0
  332. package/dist/log-parsing/render.js +113 -0
  333. package/dist/log-parsing/render.js.map +1 -0
  334. package/dist/log-parsing/text.d.ts +8 -0
  335. package/dist/log-parsing/text.d.ts.map +1 -0
  336. package/dist/log-parsing/text.js +20 -0
  337. package/dist/log-parsing/text.js.map +1 -0
  338. package/dist/server/http-server.d.ts +8 -1
  339. package/dist/server/http-server.d.ts.map +1 -1
  340. package/dist/server/http-server.js +41 -19
  341. package/dist/server/http-server.js.map +1 -1
  342. package/dist/server/port-holder.d.ts +94 -0
  343. package/dist/server/port-holder.d.ts.map +1 -0
  344. package/dist/server/port-holder.js +122 -0
  345. package/dist/server/port-holder.js.map +1 -0
  346. package/dist/services/agent-spawn.service.d.ts +50 -5
  347. package/dist/services/agent-spawn.service.d.ts.map +1 -1
  348. package/dist/services/agent-spawn.service.js +59 -66
  349. package/dist/services/agent-spawn.service.js.map +1 -1
  350. package/dist/services/background-runner.d.ts +0 -1
  351. package/dist/services/background-runner.d.ts.map +1 -1
  352. package/dist/services/background-runner.js +0 -58
  353. package/dist/services/background-runner.js.map +1 -1
  354. package/dist/services/board/board-signals.d.ts +45 -0
  355. package/dist/services/board/board-signals.d.ts.map +1 -0
  356. package/dist/services/board/board-signals.js +170 -0
  357. package/dist/services/board/board-signals.js.map +1 -0
  358. package/dist/services/board/card-briefing.d.ts +24 -0
  359. package/dist/services/board/card-briefing.d.ts.map +1 -0
  360. package/dist/services/board/card-briefing.js +132 -0
  361. package/dist/services/board/card-briefing.js.map +1 -0
  362. package/dist/services/board/card-diagnostics.d.ts +25 -0
  363. package/dist/services/board/card-diagnostics.d.ts.map +1 -0
  364. package/dist/services/board/card-diagnostics.js +50 -0
  365. package/dist/services/board/card-diagnostics.js.map +1 -0
  366. package/dist/services/board/live-root-thread.d.ts +8 -0
  367. package/dist/services/board/live-root-thread.d.ts.map +1 -0
  368. package/dist/services/board/live-root-thread.js +2 -0
  369. package/dist/services/board/live-root-thread.js.map +1 -0
  370. package/dist/services/board/orchestrator.service.d.ts +119 -0
  371. package/dist/services/board/orchestrator.service.d.ts.map +1 -0
  372. package/dist/services/board/orchestrator.service.js +387 -0
  373. package/dist/services/board/orchestrator.service.js.map +1 -0
  374. package/dist/services/board/release-worker.d.ts +43 -0
  375. package/dist/services/board/release-worker.d.ts.map +1 -0
  376. package/dist/services/board/release-worker.js +96 -0
  377. package/dist/services/board/release-worker.js.map +1 -0
  378. package/dist/services/board/report-capture.d.ts +94 -0
  379. package/dist/services/board/report-capture.d.ts.map +1 -0
  380. package/dist/services/board/report-capture.js +398 -0
  381. package/dist/services/board/report-capture.js.map +1 -0
  382. package/dist/services/board/worker-spawn.d.ts +89 -0
  383. package/dist/services/board/worker-spawn.d.ts.map +1 -0
  384. package/dist/services/board/worker-spawn.js +177 -0
  385. package/dist/services/board/worker-spawn.js.map +1 -0
  386. package/dist/services/board/worktree.d.ts +45 -0
  387. package/dist/services/board/worktree.d.ts.map +1 -0
  388. package/dist/services/board/worktree.js +183 -0
  389. package/dist/services/board/worktree.js.map +1 -0
  390. package/dist/services/broker-client.d.ts +84 -2
  391. package/dist/services/broker-client.d.ts.map +1 -1
  392. package/dist/services/broker-client.js +72 -6
  393. package/dist/services/broker-client.js.map +1 -1
  394. package/dist/services/claude-spawn.service.d.ts +5 -0
  395. package/dist/services/claude-spawn.service.d.ts.map +1 -1
  396. package/dist/services/claude-spawn.service.js +37 -21
  397. package/dist/services/claude-spawn.service.js.map +1 -1
  398. package/dist/services/codex-spawn.service.d.ts +8 -3
  399. package/dist/services/codex-spawn.service.d.ts.map +1 -1
  400. package/dist/services/codex-spawn.service.js +84 -67
  401. package/dist/services/codex-spawn.service.js.map +1 -1
  402. package/dist/services/consolidation.service.d.ts.map +1 -1
  403. package/dist/services/consolidation.service.js +5 -49
  404. package/dist/services/consolidation.service.js.map +1 -1
  405. package/dist/services/copilot-spawn.service.d.ts +1 -1
  406. package/dist/services/copilot-spawn.service.d.ts.map +1 -1
  407. package/dist/services/copilot-spawn.service.js +10 -5
  408. package/dist/services/copilot-spawn.service.js.map +1 -1
  409. package/dist/services/daily-session.d.ts +24 -11
  410. package/dist/services/daily-session.d.ts.map +1 -1
  411. package/dist/services/daily-session.js +113 -33
  412. package/dist/services/daily-session.js.map +1 -1
  413. package/dist/services/death-alert.service.d.ts +55 -0
  414. package/dist/services/death-alert.service.d.ts.map +1 -0
  415. package/dist/services/death-alert.service.js +64 -0
  416. package/dist/services/death-alert.service.js.map +1 -0
  417. package/dist/services/dispatcher/broker.d.ts +66 -0
  418. package/dist/services/dispatcher/broker.d.ts.map +1 -1
  419. package/dist/services/dispatcher/broker.js +60 -52
  420. package/dist/services/dispatcher/broker.js.map +1 -1
  421. package/dist/services/dispatcher/poller.d.ts.map +1 -1
  422. package/dist/services/dispatcher/poller.js +31 -24
  423. package/dist/services/dispatcher/poller.js.map +1 -1
  424. package/dist/services/http-retry.d.ts +1 -90
  425. package/dist/services/http-retry.d.ts.map +1 -1
  426. package/dist/services/http-retry.js +1 -176
  427. package/dist/services/http-retry.js.map +1 -1
  428. package/dist/services/intent.d.ts +15 -1
  429. package/dist/services/intent.d.ts.map +1 -1
  430. package/dist/services/intent.js +39 -27
  431. package/dist/services/intent.js.map +1 -1
  432. package/dist/services/keeper.service.d.ts +76 -3
  433. package/dist/services/keeper.service.d.ts.map +1 -1
  434. package/dist/services/keeper.service.js +112 -35
  435. package/dist/services/keeper.service.js.map +1 -1
  436. package/dist/services/mcp-config.service.d.ts.map +1 -1
  437. package/dist/services/mcp-config.service.js +14 -1
  438. package/dist/services/mcp-config.service.js.map +1 -1
  439. package/dist/services/memory-briefing.service.d.ts +1 -1
  440. package/dist/services/memory-briefing.service.d.ts.map +1 -1
  441. package/dist/services/memory-briefing.service.js +28 -73
  442. package/dist/services/memory-briefing.service.js.map +1 -1
  443. package/dist/services/passive-notice.service.d.ts +56 -0
  444. package/dist/services/passive-notice.service.d.ts.map +1 -0
  445. package/dist/services/passive-notice.service.js +92 -0
  446. package/dist/services/passive-notice.service.js.map +1 -0
  447. package/dist/services/predefined-prompts.d.ts +11 -0
  448. package/dist/services/predefined-prompts.d.ts.map +1 -0
  449. package/dist/services/predefined-prompts.js +25 -0
  450. package/dist/services/predefined-prompts.js.map +1 -0
  451. package/dist/services/process.service.d.ts +111 -27
  452. package/dist/services/process.service.d.ts.map +1 -1
  453. package/dist/services/process.service.js +245 -173
  454. package/dist/services/process.service.js.map +1 -1
  455. package/dist/services/spawn-common.d.ts +281 -5
  456. package/dist/services/spawn-common.d.ts.map +1 -1
  457. package/dist/services/spawn-common.js +884 -188
  458. package/dist/services/spawn-common.js.map +1 -1
  459. package/dist/services/spawn-exit-watcher.d.ts +45 -35
  460. package/dist/services/spawn-exit-watcher.d.ts.map +1 -1
  461. package/dist/services/spawn-exit-watcher.js +120 -100
  462. package/dist/services/spawn-exit-watcher.js.map +1 -1
  463. package/dist/services/telegram.d.ts +31 -8
  464. package/dist/services/telegram.d.ts.map +1 -1
  465. package/dist/services/telegram.js +58 -26
  466. package/dist/services/telegram.js.map +1 -1
  467. package/dist/services/thread-health.service.d.ts +8 -0
  468. package/dist/services/thread-health.service.d.ts.map +1 -1
  469. package/dist/services/thread-health.service.js +14 -10
  470. package/dist/services/thread-health.service.js.map +1 -1
  471. package/dist/services/thread-lifecycle.service.d.ts +17 -1
  472. package/dist/services/thread-lifecycle.service.d.ts.map +1 -1
  473. package/dist/services/thread-lifecycle.service.js +47 -6
  474. package/dist/services/thread-lifecycle.service.js.map +1 -1
  475. package/dist/services/thread-log-reader.d.ts +81 -0
  476. package/dist/services/thread-log-reader.d.ts.map +1 -0
  477. package/dist/services/thread-log-reader.js +141 -0
  478. package/dist/services/thread-log-reader.js.map +1 -0
  479. package/dist/services/thread-purge.service.d.ts +200 -0
  480. package/dist/services/thread-purge.service.d.ts.map +1 -0
  481. package/dist/services/thread-purge.service.js +644 -0
  482. package/dist/services/thread-purge.service.js.map +1 -0
  483. package/dist/services/topic.service.d.ts +44 -2
  484. package/dist/services/topic.service.d.ts.map +1 -1
  485. package/dist/services/topic.service.js +74 -13
  486. package/dist/services/topic.service.js.map +1 -1
  487. package/dist/services/usage/usage-inbox-notifier.d.ts +50 -0
  488. package/dist/services/usage/usage-inbox-notifier.d.ts.map +1 -0
  489. package/dist/services/usage/usage-inbox-notifier.js +118 -0
  490. package/dist/services/usage/usage-inbox-notifier.js.map +1 -0
  491. package/dist/services/usage/usage-poller.service.d.ts +41 -0
  492. package/dist/services/usage/usage-poller.service.d.ts.map +1 -0
  493. package/dist/services/usage/usage-poller.service.js +67 -0
  494. package/dist/services/usage/usage-poller.service.js.map +1 -0
  495. package/dist/services/usage/usage-snapshot-provider.d.ts +40 -0
  496. package/dist/services/usage/usage-snapshot-provider.d.ts.map +1 -0
  497. package/dist/services/usage/usage-snapshot-provider.js +89 -0
  498. package/dist/services/usage/usage-snapshot-provider.js.map +1 -0
  499. package/dist/services/usage/usage-types.d.ts +28 -0
  500. package/dist/services/usage/usage-types.d.ts.map +1 -0
  501. package/dist/services/usage/usage-types.js +14 -0
  502. package/dist/services/usage/usage-types.js.map +1 -0
  503. package/dist/services/worker-birth.d.ts +154 -0
  504. package/dist/services/worker-birth.d.ts.map +1 -0
  505. package/dist/services/worker-birth.js +274 -0
  506. package/dist/services/worker-birth.js.map +1 -0
  507. package/dist/services/worker-cleanup.service.d.ts +242 -19
  508. package/dist/services/worker-cleanup.service.d.ts.map +1 -1
  509. package/dist/services/worker-cleanup.service.js +893 -355
  510. package/dist/services/worker-cleanup.service.js.map +1 -1
  511. package/dist/services/worker-lifecycle-executor.d.ts +42 -0
  512. package/dist/services/worker-lifecycle-executor.d.ts.map +1 -0
  513. package/dist/services/worker-lifecycle-executor.js +63 -0
  514. package/dist/services/worker-lifecycle-executor.js.map +1 -0
  515. package/dist/services/worker-single-use.d.ts +92 -0
  516. package/dist/services/worker-single-use.d.ts.map +1 -0
  517. package/dist/services/worker-single-use.js +155 -0
  518. package/dist/services/worker-single-use.js.map +1 -0
  519. package/dist/sessions/mcp-registry.d.ts +33 -2
  520. package/dist/sessions/mcp-registry.d.ts.map +1 -1
  521. package/dist/sessions/mcp-registry.js +153 -30
  522. package/dist/sessions/mcp-registry.js.map +1 -1
  523. package/dist/test-bootstrap.d.ts +19 -12
  524. package/dist/test-bootstrap.d.ts.map +1 -1
  525. package/dist/test-bootstrap.js +69 -21
  526. package/dist/test-bootstrap.js.map +1 -1
  527. package/dist/test-tripwire.test-support.d.ts +75 -0
  528. package/dist/test-tripwire.test-support.d.ts.map +1 -0
  529. package/dist/test-tripwire.test-support.js +353 -0
  530. package/dist/test-tripwire.test-support.js.map +1 -0
  531. package/dist/tools/defs/memory-defs.d.ts.map +1 -1
  532. package/dist/tools/defs/memory-defs.js +12 -4
  533. package/dist/tools/defs/memory-defs.js.map +1 -1
  534. package/dist/tools/defs/session-defs.d.ts.map +1 -1
  535. package/dist/tools/defs/session-defs.js +26 -10
  536. package/dist/tools/defs/session-defs.js.map +1 -1
  537. package/dist/tools/defs/utility-defs.js +1 -1
  538. package/dist/tools/defs/utility-defs.js.map +1 -1
  539. package/dist/tools/delegate-tool.d.ts +81 -1
  540. package/dist/tools/delegate-tool.d.ts.map +1 -1
  541. package/dist/tools/delegate-tool.js +840 -219
  542. package/dist/tools/delegate-tool.js.map +1 -1
  543. package/dist/tools/memory-tools.d.ts.map +1 -1
  544. package/dist/tools/memory-tools.js +66 -9
  545. package/dist/tools/memory-tools.js.map +1 -1
  546. package/dist/tools/response-builders.d.ts.map +1 -1
  547. package/dist/tools/response-builders.js +12 -8
  548. package/dist/tools/response-builders.js.map +1 -1
  549. package/dist/tools/session-tools.d.ts.map +1 -1
  550. package/dist/tools/session-tools.js +3 -32
  551. package/dist/tools/session-tools.js.map +1 -1
  552. package/dist/tools/skill-tools.js +1 -1
  553. package/dist/tools/skill-tools.js.map +1 -1
  554. package/dist/tools/start-session-tool.d.ts.map +1 -1
  555. package/dist/tools/start-session-tool.js +66 -20
  556. package/dist/tools/start-session-tool.js.map +1 -1
  557. package/dist/tools/utility-tools.d.ts.map +1 -1
  558. package/dist/tools/utility-tools.js +38 -3
  559. package/dist/tools/utility-tools.js.map +1 -1
  560. package/dist/tools/wait/auto-forward.d.ts.map +1 -1
  561. package/dist/tools/wait/auto-forward.js +8 -3
  562. package/dist/tools/wait/auto-forward.js.map +1 -1
  563. package/dist/tools/wait/consolidation-strategy.d.ts +42 -0
  564. package/dist/tools/wait/consolidation-strategy.d.ts.map +1 -0
  565. package/dist/tools/wait/consolidation-strategy.js +50 -0
  566. package/dist/tools/wait/consolidation-strategy.js.map +1 -0
  567. package/dist/tools/wait/drive-handler.d.ts +5 -7
  568. package/dist/tools/wait/drive-handler.d.ts.map +1 -1
  569. package/dist/tools/wait/drive-handler.js +45 -59
  570. package/dist/tools/wait/drive-handler.js.map +1 -1
  571. package/dist/tools/wait/media-processor.d.ts.map +1 -1
  572. package/dist/tools/wait/media-processor.js +8 -4
  573. package/dist/tools/wait/media-processor.js.map +1 -1
  574. package/dist/tools/wait/message-delivery.d.ts +9 -6
  575. package/dist/tools/wait/message-delivery.d.ts.map +1 -1
  576. package/dist/tools/wait/message-delivery.js +77 -58
  577. package/dist/tools/wait/message-delivery.js.map +1 -1
  578. package/dist/tools/wait/message-processing.d.ts.map +1 -1
  579. package/dist/tools/wait/message-processing.js +2 -1
  580. package/dist/tools/wait/message-processing.js.map +1 -1
  581. package/dist/tools/wait/poll-loop.d.ts.map +1 -1
  582. package/dist/tools/wait/poll-loop.js +339 -239
  583. package/dist/tools/wait/poll-loop.js.map +1 -1
  584. package/package.json +4 -3
  585. package/templates/azure-devops-securevault.default.md +20 -6
  586. package/templates/board-quality-checklist.default.md +121 -0
  587. package/templates/buildless-vue-preview.default.md +47 -0
  588. package/templates/expert-code-review.default.md +12 -0
  589. package/templates/handoff.default.md +41 -18
  590. package/templates/increment-pr-pipeline.default.md +79 -0
  591. package/templates/swarm.default.md +1 -0
  592. package/templates/worker-autonomous.default.md +1 -0
  593. package/templates/worker-delegate.default.md +1 -0
  594. package/dist/dashboard/log-parser.d.ts +0 -65
  595. package/dist/dashboard/log-parser.d.ts.map +0 -1
  596. package/dist/dashboard/log-parser.js +0 -219
  597. package/dist/dashboard/log-parser.js.map +0 -1
  598. package/dist/data/memory/reflection-dedup.d.ts +0 -17
  599. package/dist/data/memory/reflection-dedup.d.ts.map +0 -1
  600. package/dist/data/memory/reflection-dedup.js +0 -55
  601. package/dist/data/memory/reflection-dedup.js.map +0 -1
  602. package/dist/data/memory/reflection-gates.d.ts +0 -40
  603. package/dist/data/memory/reflection-gates.d.ts.map +0 -1
  604. package/dist/data/memory/reflection-gates.js +0 -220
  605. package/dist/data/memory/reflection-gates.js.map +0 -1
  606. package/dist/data/memory/reflection-sources.d.ts +0 -13
  607. package/dist/data/memory/reflection-sources.d.ts.map +0 -1
  608. package/dist/data/memory/reflection-sources.js +0 -115
  609. package/dist/data/memory/reflection-sources.js.map +0 -1
  610. package/dist/data/memory/reflection.d.ts +0 -43
  611. package/dist/data/memory/reflection.d.ts.map +0 -1
  612. package/dist/data/memory/reflection.js +0 -329
  613. package/dist/data/memory/reflection.js.map +0 -1
  614. package/dist/llm/prompts/reflection.d.ts +0 -6
  615. package/dist/llm/prompts/reflection.d.ts.map +0 -1
  616. package/dist/llm/prompts/reflection.js +0 -77
  617. package/dist/llm/prompts/reflection.js.map +0 -1
  618. package/dist/services/memory-pruning.service.d.ts +0 -12
  619. package/dist/services/memory-pruning.service.d.ts.map +0 -1
  620. package/dist/services/memory-pruning.service.js +0 -179
  621. package/dist/services/memory-pruning.service.js.map +0 -1
  622. package/templates/daily-review.default.md +0 -26
  623. package/templates/fire-and-forget.default.md +0 -76
  624. package/templates/offload.default.md +0 -33
  625. package/templates/sensorium-repair.default.md +0 -114
@@ -18,14 +18,16 @@ import { readFile, rename, unlink } from "node:fs/promises";
18
18
  import { join } from "node:path";
19
19
  import { checkMaintenanceFlag, writeActivityHeartbeat, writeThreadHeartbeatSync, } from "../../data/file-storage.js";
20
20
  import { onMaintenanceSignal, markMaintenanceObserved, clearMaintenanceObserved, } from "../../services/maintenance-signal.js";
21
- import { getEffectiveAgentType, getEffectiveAutonomousMode, } from "../../services/thread-config.service.js";
21
+ import { getEffectiveAutonomousMode } from "../../services/thread-config.service.js";
22
22
  import { peekThreadMessages } from "../../services/dispatcher/index.js";
23
23
  import { log } from "../../lib/logger.js";
24
24
  import { getShortReminder, buildMaintenanceResponse } from "../response-builders.js";
25
25
  import { ThreadState } from "../../services/thread-lifecycle.service.js";
26
- import { PENDING_TASKS_DIR, markTaskConsumed } from "../../services/process.service.js";
27
- import { isWorkerInGrace } from "../../services/worker-cleanup.service.js";
28
- import { isSessionSuperseded, reconcileThreadOwnership } from "../../sessions/mcp-registry.js";
26
+ import { PENDING_TASKS_DIR } from "../../services/process.service.js";
27
+ import { Optional } from "../../data/optional.js";
28
+ import { markWorkerTaskConsumed } from "../../data/memory/worker-lifecycle.repository.js";
29
+ import { takeWorkerDeathNotice } from "../../services/passive-notice.service.js";
30
+ import { isSessionSuperseded, markThreadWaiting, reconcileThreadOwnership, unmarkThreadWaiting, } from "../../sessions/mcp-registry.js";
29
31
  import { handleReactionOnly } from "./reaction-handler.js";
30
32
  import { checkForDueTasks } from "./task-handler.js";
31
33
  import { processIncomingMessages, handlePollTimeout } from "./message-processing.js";
@@ -39,42 +41,105 @@ import { maybeAutoForwardFinalText } from "./auto-forward.js";
39
41
  // close. The set is shared with self-update (ACK-gated port close) and lives in
40
42
  // maintenance-signal.ts; this loop owns clearing it because the poll loop is the
41
43
  // only place that reads the flag continuously.
44
+ // unref'd like every other background timer here (index.ts, orchestrator,
45
+ // dispatcher poller, spawn-exit-watcher): clearing a stale flag must never be the
46
+ // handle that keeps a process alive. The server's transport handles own its
47
+ // lifetime; without this, merely IMPORTING this module pins the event loop open.
42
48
  setInterval(() => {
43
49
  if (checkMaintenanceFlag() === null) {
44
50
  clearMaintenanceObserved();
45
51
  }
46
- }, 30_000);
52
+ }, 30_000).unref();
47
53
  // ---------------------------------------------------------------------------
48
54
  // Pending-task file helper
49
55
  // ---------------------------------------------------------------------------
50
56
  /**
51
57
  * Check for a pending task file written by send_message_to_thread.
52
- * If one exists, atomically consume it and return a ToolResult.
58
+ * If one exists, atomically consume it and return the ToolResult carrying it.
53
59
  * The file now contains pre-formatted structured messages (one-shot or
54
60
  * manager-worker), so we pass the content through as-is.
55
- * Returns null when no pending task is available.
61
+ * Empty when no pending task is available.
62
+ *
63
+ * `db` is here for one write: this is the single place a task is consumed, so it is
64
+ * the single place that records the consumption durably. A thread that read its work
65
+ * must still read as having read it in the NEXT server process — see
66
+ * `markWorkerTaskConsumed`.
67
+ *
68
+ * ## Ordering: the claim and the content, never apart
69
+ *
70
+ * The steps are rename → read → RECORD → unlink → deliver, and a failure at the
71
+ * record leaves the `.processing` file for the next poll to ADOPT. That order is
72
+ * forced, in the shape inc E3a settled for the passive-notice store: nothing that
73
+ * makes the briefing unrecoverable may happen before the consumption fact is
74
+ * durable.
75
+ *
76
+ * The order used to be rename → read → UNLINK → record, and the record sits under
77
+ * the catch below. One failing SQLite write therefore returned empty with the
78
+ * mailbox AND the `.processing` file gone and no row written: a transient DB error
79
+ * permanently deleted a worker's briefing — the one thing a card worker cannot work
80
+ * without. The in-memory Set this column replaced had no such path, because it could
81
+ * not fail.
82
+ *
83
+ * Adoption is what makes the rename reversible, and it is required rather than
84
+ * decorative: nothing else in this codebase ever comes back for a `.processing`
85
+ * file. `deletePendingTaskMailbox` is the only other code that knows that name, and
86
+ * it only DELETES, at retirement — so "leave it behind on failure" is a remedy only
87
+ * once some path returns for it, and this is that path.
88
+ *
89
+ * On a failed record the task stays queued and the worker polls again in 2s; it is
90
+ * never "neither the task nor the record". Delivery is NOT conditional on the
91
+ * database being writable in the sense that matters: a blip costs a poll interval,
92
+ * not the briefing.
93
+ *
94
+ * What adoption pays for is duplicate DELIVERY, never loss, and the cost lands on the
95
+ * WORKER rather than on this column: it receives the same briefing twice and may ACT
96
+ * twice. Three states reach that — the unlink failed, the process died between the
97
+ * commit and the unlink, two pollers adopted one stranded file. The column is unmoved
98
+ * in all three, because `markWorkerTaskConsumed` keeps the FIRST timestamp.
99
+ *
100
+ * The number of extra deliveries is not one. A TRANSIENT unlink failure costs exactly
101
+ * one: the next poll adopts the file, delivers it, and unlinks it. An unlink that can
102
+ * NEVER succeed — file held open, permissions revoked — leaves a `.processing` that
103
+ * the fast pre-check counts as pending work on every poll, so the same briefing goes
104
+ * out every 2s for the life of this wait call, where the old order lost it exactly
105
+ * once. What keeps that narrow is the step before it: the write that recorded the
106
+ * consumption already succeeded, so a filesystem that cannot unlink the file it just
107
+ * let us rename and read is already misbehaving.
56
108
  */
57
- async function consumePendingTask(threadId) {
109
+ async function consumePendingTask(db, threadId) {
58
110
  const pendingTaskPath = join(PENDING_TASKS_DIR, `${threadId}.txt`);
59
- // Fast pre-check to avoid unnecessary rename attempts every 2s
60
- if (!existsSync(pendingTaskPath))
61
- return null;
111
+ const claimedPath = pendingTaskPath + ".processing";
112
+ // Fast pre-check to avoid unnecessary rename attempts every 2s. A `.processing`
113
+ // file counts as pending work: it is an unfinished consumption, not litter.
114
+ if (!existsSync(pendingTaskPath) && !existsSync(claimedPath))
115
+ return Optional.empty();
62
116
  try {
63
- const tmpPath = pendingTaskPath + ".processing";
64
- await rename(pendingTaskPath, tmpPath);
65
- const taskContent = (await readFile(tmpPath, "utf-8")).trim();
117
+ // The rename is the claim, and it is atomic between this loop's two arms and
118
+ // between racing sessions. A `.processing` that is already there is a claim an
119
+ // earlier pass took and did not finish: adopt it rather than renaming onto it.
120
+ if (existsSync(pendingTaskPath))
121
+ await rename(pendingTaskPath, claimedPath);
122
+ const taskContent = (await readFile(claimedPath, "utf-8")).trim();
123
+ // Record the consumption durably, so the worker-exit handler never respawns a
124
+ // worker that already did work — including after a server restart, which the
125
+ // in-memory Set this replaces could not survive, and across which the exit
126
+ // watcher still observes a worker that outlived it. Also guards against the
127
+ // duplicate posts a late mailbox append used to cause by recreating the file.
128
+ //
129
+ // BEFORE the unlink: a throw here returns empty with the content still sitting in
130
+ // `.processing`, which the next poll adopts. After the unlink, the same throw
131
+ // would have deleted the briefing outright.
132
+ markWorkerTaskConsumed(db, threadId, Date.now());
66
133
  try {
67
- await unlink(tmpPath);
134
+ await unlink(claimedPath);
68
135
  }
69
136
  catch {
70
- /* ignore cleanup errors */
137
+ /* ignore cleanup errors — the fact is already durable, so a surviving
138
+ .processing costs re-delivery and never the task; the ordering note above
139
+ says how much re-delivery */
71
140
  }
72
- // Mark this thread's task as consumed so the worker-exit handler never
73
- // respawns a worker that already did work (guards against duplicate posts
74
- // when a late mailbox append recreates the pending-task file).
75
- markTaskConsumed(threadId);
76
141
  log.info(`[wait] Injecting pending task for thread ${threadId} (${taskContent.length} chars)`);
77
- return {
142
+ return Optional.of({
78
143
  content: [
79
144
  {
80
145
  type: "text",
@@ -83,14 +148,34 @@ async function consumePendingTask(threadId) {
83
148
  `<<< END CROSS-THREAD MESSAGE >>>`,
84
149
  },
85
150
  ],
86
- };
151
+ });
87
152
  }
88
153
  catch (err) {
89
154
  if (err?.code !== "ENOENT")
90
155
  log.warn(`[wait] Pending task read failed: ${err}`);
91
- return null; // File doesn't exist or read error
156
+ // The file is gone, unreadable, or the consumption could not be recorded. In the
157
+ // last case the claimed `.processing` file still holds the task and the next poll
158
+ // adopts it, so returning empty costs a poll interval and not the briefing.
159
+ return Optional.empty();
92
160
  }
93
161
  }
162
+ /** A worker's death notice, delivered as its own inbox message — the way the
163
+ * worker's reply would have arrived. One per wait call, oldest first. */
164
+ function consumeDeathNotice(db, threadId) {
165
+ return takeWorkerDeathNotice(db, threadId).map((notice) => {
166
+ log.info(`[wait] Delivering a worker death notice to thread ${threadId}`);
167
+ return {
168
+ content: [
169
+ {
170
+ type: "text",
171
+ text: `<<< CROSS-THREAD MESSAGE >>>
172
+ ${notice.text}
173
+ <<< END CROSS-THREAD MESSAGE >>>`,
174
+ },
175
+ ],
176
+ };
177
+ });
178
+ }
94
179
  // ---------------------------------------------------------------------------
95
180
  // Handler
96
181
  // ---------------------------------------------------------------------------
@@ -126,13 +211,7 @@ export async function handleWaitForInstructions(args, ctx, extra) {
126
211
  }
127
212
  const callNumber = ++state.waitCallCount;
128
213
  const timeoutMs = WAIT_TIMEOUT_MINUTES * 60 * 1000;
129
- // Codex CLI enforces a hard ~120s tool-call timeout and does not handle
130
- // SSE keepalive progress notifications. Cap the loop to 90s so we always
131
- // return a valid response before the Codex client gives up.
132
- const agentType = getEffectiveAgentType(effectiveThreadId);
133
- const isShortTimeoutClient = agentType === "codex" || agentType === "openai_codex";
134
- const effectiveTimeoutMs = isShortTimeoutClient ? 90_000 : timeoutMs;
135
- const deadline = Date.now() + effectiveTimeoutMs;
214
+ const deadline = Date.now() + timeoutMs;
136
215
  // ── Pending task injection (pre-loop check) ────────────────────────────
137
216
  // If start_thread or send_message_to_thread wrote a task file for this
138
217
  // thread, deliver it immediately — but only if this session is still active.
@@ -152,9 +231,12 @@ export async function handleWaitForInstructions(args, ctx, extra) {
152
231
  ],
153
232
  };
154
233
  }
155
- const preLoopTask = await consumePendingTask(effectiveThreadId);
156
- if (preLoopTask)
157
- return preLoopTask;
234
+ const preLoopTask = await consumePendingTask(getMemoryDb(), effectiveThreadId);
235
+ if (preLoopTask.has())
236
+ return preLoopTask.get();
237
+ const preLoopDeath = consumeDeathNotice(getMemoryDb(), effectiveThreadId);
238
+ if (preLoopDeath.has())
239
+ return preLoopDeath.get();
158
240
  // Poll the dispatcher's per-thread file instead of calling getUpdates
159
241
  // directly. This avoids 409 conflicts between concurrent instances.
160
242
  const POLL_INTERVAL_MS = 2000;
@@ -173,245 +255,263 @@ export async function handleWaitForInstructions(args, ctx, extra) {
173
255
  // echoing requestId there fails to match, the keepalive never resets its
174
256
  // timeout, and the call dies at ~60s. Echo the client's token when present.
175
257
  const progressToken = extra._meta?.progressToken ?? extra.requestId;
176
- while (Date.now() < deadline) {
177
- try {
178
- // Bail out if this session has been superseded by a newer session for the
179
- // same thread. Without this, a zombie session races the active one for
180
- // messages — consuming them but responding with degraded/stale context.
181
- const currentSid = ctx.getMcpSessionId?.();
182
- if (isSessionSuperseded(currentSid, effectiveThreadId)) {
183
- log.warn(`[wait] Session ${currentSid?.slice(0, 8)}… superseded for thread ${effectiveThreadId} — terminating poll loop.`);
184
- state.lastToolCallAt = Date.now();
185
- return {
186
- content: [
187
- {
188
- type: "text",
189
- text: "Session terminated — a newer session is now active for this thread. Do not call wait_for_instructions again.",
190
- },
191
- ],
192
- };
193
- }
194
- // Bail out if the MCP transport signalled abort (client disconnected).
195
- if (extra.signal.aborted) {
196
- log.info(`[wait] Signal aborted for session ${currentSid?.slice(0, 8)}… thread ${effectiveThreadId} — client disconnected.`);
197
- state.lastToolCallAt = Date.now();
198
- return {
199
- content: [
200
- {
201
- type: "text",
202
- text: "Client disconnected. Call wait_for_instructions again to resume.",
203
- },
204
- ],
205
- };
206
- }
207
- // Bail out if the thread has been archived — graceful shutdown without kill.
208
- if (Date.now() - lastArchivedCheck >= ARCHIVED_CHECK_INTERVAL_MS) {
209
- lastArchivedCheck = Date.now();
210
- // TTL grace window: the worker crossed its TTL and is winding down, but
211
- // its MCP socket is deliberately kept open so it can finish THIS turn and
212
- // deliver its final result before the cleanup service tears it down. This
213
- // returns to the worker so it can still act (send its result) — a soft
214
- // "finish then terminate" signal, not a hard stop.
215
- if (isWorkerInGrace(effectiveThreadId)) {
216
- log.info(`[wait] Thread ${effectiveThreadId} is in TTL grace — instructing worker to finish and deliver its final result.`);
258
+ // ── Park mark ───────────────────────────────────────────────────────────
259
+ // From here until this call returns, the worker is PARKED: it is not working,
260
+ // it is waiting on us. The TTL sweep reads this to tell a working worker
261
+ // (defer teardown) from a parked one (tear down — parked is not work). Marked
262
+ // AFTER the pre-loop task consumption, which returns without ever waiting.
263
+ //
264
+ // The mark is filed under THIS call's owner identity (the same `preSid` the
265
+ // reconcile above just claimed ownership with), so a superseded session's
266
+ // residual mark can never speak for the replacement that owns the thread now.
267
+ //
268
+ // The returned key is released verbatim below — never recomputed, since a
269
+ // mid-wait token change would resolve to a different key and leak the mark.
270
+ //
271
+ // The unmark MUST be in a `finally`: every exit path — message delivered,
272
+ // superseded, aborted, grace, archived, maintenance, timeout — has to release
273
+ // it, or a worker that resumed working would still read as parked.
274
+ const parkKey = markThreadWaiting(effectiveThreadId, preSid);
275
+ try {
276
+ while (Date.now() < deadline) {
277
+ try {
278
+ // Bail out if this session has been superseded by a newer session for the
279
+ // same thread. Without this, a zombie session races the active one for
280
+ // messages — consuming them but responding with degraded/stale context.
281
+ const currentSid = ctx.getMcpSessionId?.();
282
+ if (isSessionSuperseded(currentSid, effectiveThreadId)) {
283
+ log.warn(`[wait] Session ${currentSid?.slice(0, 8)}… superseded for thread ${effectiveThreadId} — terminating poll loop.`);
217
284
  state.lastToolCallAt = Date.now();
218
285
  return {
219
286
  content: [
220
287
  {
221
288
  type: "text",
222
- text: "This session is being archived. Finish your current task, send your final result to the orchestrator (send_message_to_thread), then terminate. Ignore any further orchestrator messages and do not call wait_for_instructions again.",
289
+ text: "Session terminated — a newer session is now active for this thread. Do not call wait_for_instructions again.",
223
290
  },
224
291
  ],
225
292
  };
226
293
  }
227
- const threadState = ctx.threadLifecycle.getThreadState(getMemoryDb(), effectiveThreadId);
228
- if (threadState === ThreadState.Archived || threadState === ThreadState.Expired) {
229
- log.info(`[wait] Thread ${effectiveThreadId} is ${threadState} — instructing agent to exit.`);
294
+ // Bail out if the MCP transport signalled abort (client disconnected).
295
+ if (extra.signal.aborted) {
296
+ log.info(`[wait] Signal aborted for session ${currentSid?.slice(0, 8)}… thread ${effectiveThreadId} — client disconnected.`);
230
297
  state.lastToolCallAt = Date.now();
231
298
  return {
232
299
  content: [
233
300
  {
234
301
  type: "text",
235
- text: "Session terminated. Thread has been archived. Do not call wait_for_instructions again — exit immediately.",
302
+ text: "Client disconnected. Call wait_for_instructions again to resume.",
236
303
  },
237
304
  ],
238
305
  };
239
306
  }
240
- }
241
- // Check for pending update — tell agent to poll the supervisor's /ready
242
- // endpoint over plain HTTP. CRITICAL: Do NOT tell agents to call any MCP tool
243
- // on sensorium-mcp here — the server is about to die. buildMaintenanceResponse
244
- // hands back a PowerShell poll loop against the supervisor (survives the
245
- // restart), with an external sleep as the last-resort fallback.
246
- const maintenanceInfo = checkMaintenanceFlag();
247
- if (maintenanceInfo) {
248
- log.info(`[wait] Maintenance flag detected: ${maintenanceInfo}`);
249
- // Record that this thread observed maintenance. This both (a) fires the
250
- // Telegram notification once per thread across all wait_for_instructions
251
- // calls, and (b) tells self-update this agent has backed off so the port
252
- // close can proceed (ACK-gated). markMaintenanceObserved returns true only
253
- // on the first observation this cycle.
254
- const tgKey = effectiveThreadId ?? 0;
255
- if (markMaintenanceObserved(tgKey)) {
256
- let version = "unknown";
257
- try {
258
- version = JSON.parse(maintenanceInfo).version ?? version;
307
+ // Bail out if the thread has been archived — graceful shutdown without kill.
308
+ if (Date.now() - lastArchivedCheck >= ARCHIVED_CHECK_INTERVAL_MS) {
309
+ lastArchivedCheck = Date.now();
310
+ const threadState = ctx.threadLifecycle.getThreadState(getMemoryDb(), effectiveThreadId);
311
+ if (threadState === ThreadState.Archived || threadState === ThreadState.Expired) {
312
+ log.info(`[wait] Thread ${effectiveThreadId} is ${threadState} — instructing agent to exit.`);
313
+ state.lastToolCallAt = Date.now();
314
+ return {
315
+ content: [
316
+ {
317
+ type: "text",
318
+ text: "Session terminated. Thread has been archived. Do not call wait_for_instructions again — exit immediately.",
319
+ },
320
+ ],
321
+ };
259
322
  }
260
- catch {
261
- /* not JSON or missing field */
323
+ }
324
+ // Check for pending update — tell agent to poll the supervisor's /ready
325
+ // endpoint over plain HTTP. CRITICAL: Do NOT tell agents to call any MCP tool
326
+ // on sensorium-mcp here — the server is about to die. buildMaintenanceResponse
327
+ // hands back a PowerShell poll loop against the supervisor (survives the
328
+ // restart), with an external sleep as the last-resort fallback.
329
+ const maintenanceInfo = checkMaintenanceFlag();
330
+ if (maintenanceInfo) {
331
+ log.info(`[wait] Maintenance flag detected: ${maintenanceInfo}`);
332
+ // Record that this thread observed maintenance. This both (a) fires the
333
+ // Telegram notification once per thread across all wait_for_instructions
334
+ // calls, and (b) tells self-update this agent has backed off so the port
335
+ // close can proceed (ACK-gated). markMaintenanceObserved returns true only
336
+ // on the first observation this cycle.
337
+ const tgKey = effectiveThreadId ?? 0;
338
+ if (markMaintenanceObserved(tgKey)) {
339
+ let version = "unknown";
340
+ try {
341
+ version = JSON.parse(maintenanceInfo).version ?? version;
342
+ }
343
+ catch {
344
+ /* not JSON or missing field */
345
+ }
346
+ telegram
347
+ .sendMessage(telegramChatId, `\u26A0\uFE0F Server update: v${version} deploying. Agent sessions will reconnect after update.`, undefined, effectiveThreadId)
348
+ .catch((err) => {
349
+ log.warn(`[poll] Maintenance notification failed for thread ${effectiveThreadId}: ${err instanceof Error ? err.message : err}`);
350
+ });
262
351
  }
263
- telegram
264
- .sendMessage(telegramChatId, `\u26A0\uFE0F Server update: v${version} deploying. Agent sessions will reconnect after update.`, undefined, effectiveThreadId)
265
- .catch((err) => {
266
- log.warn(`[poll] Maintenance notification failed for thread ${effectiveThreadId}: ${err instanceof Error ? err.message : err}`);
267
- });
352
+ return buildMaintenanceResponse(effectiveThreadId, getShortReminder(effectiveThreadId, state.sessionStartedAt));
268
353
  }
269
- return buildMaintenanceResponse(effectiveThreadId, getShortReminder(effectiveThreadId, state.sessionStartedAt));
270
- }
271
- // Peek first (non-destructive) to avoid consuming messages when the
272
- // SSE connection may be dead.
273
- const peeked = peekThreadMessages(effectiveThreadId);
274
- if (peeked.length > 0) {
275
- return processIncomingMessages(effectiveThreadId, peeked.length, ctx, extra);
276
- }
277
- // ── Reaction-only wake-up ───────────────────────────────────────
278
- // Guard: don't consume the reaction file if the SSE connection is
279
- // already dead — readPendingReaction() is destructive (read + delete).
280
- // Without this check the reaction is eaten but never delivered.
281
- if (!extra.signal.aborted &&
282
- !isSessionSuperseded(ctx.getMcpSessionId?.(), effectiveThreadId)) {
283
- const reactionResult = await handleReactionOnly({
284
- telegram,
285
- getMemoryDb,
286
- effectiveThreadId,
287
- sessionStartedAt: state.sessionStartedAt,
288
- autonomousMode: getEffectiveAutonomousMode(effectiveThreadId),
289
- });
290
- if (reactionResult)
291
- return reactionResult;
292
- }
293
- // ── Pending task injection (in-loop) ─────────────────────────────────────────
294
- // Check for tasks sent via send_message_to_thread while we're already
295
- // polling. Without this, messages arrive only on the NEXT
296
- // wait_for_instructions call, causing a "no instructions" gap.
297
- if (!extra.signal.aborted &&
298
- !isSessionSuperseded(ctx.getMcpSessionId?.(), effectiveThreadId)) {
299
- const inLoopTask = await consumePendingTask(effectiveThreadId);
300
- if (inLoopTask)
301
- return inLoopTask;
302
- }
303
- // Check scheduled tasks every ~60s during idle polling.
304
- if (effectiveThreadId !== undefined && Date.now() - lastScheduleCheck >= 60_000) {
305
- lastScheduleCheck = Date.now();
306
- const taskResult = checkForDueTasks(ctx, effectiveThreadId);
307
- if (taskResult)
308
- return taskResult;
309
- }
310
- // ── In-loop drive activation check (every 30 min) ──────────────────
311
- // Without this, the drive only fires on poll TIMEOUT (every 2h default).
312
- // Checking inside the loop ensures consistent activation during long polls.
313
- if (effectiveThreadId !== undefined &&
314
- getEffectiveAutonomousMode(effectiveThreadId) &&
315
- Date.now() - lastDriveCheck >= DRIVE_CHECK_INTERVAL_MS) {
316
- lastDriveCheck = Date.now();
317
- runAutoConsolidation({
318
- state,
319
- effectiveThreadId,
320
- getMemoryDb,
321
- apiKey: config.OPENAI_API_KEY || undefined,
322
- config,
323
- memoryRefresh: "",
324
- scheduleHint: "",
325
- });
326
- const driveResult = checkDriveActivation({
327
- state,
328
- effectiveThreadId,
329
- getMemoryDb,
330
- apiKey: config.OPENAI_API_KEY || undefined,
331
- config,
332
- memoryRefresh: "",
333
- scheduleHint: "",
334
- });
335
- if (driveResult)
336
- return driveResult;
337
- }
338
- // No messages yet — sleep briefly and check again.
339
- // Send SSE keepalive to prevent silent connection death during long polls.
340
- if (Date.now() - lastKeepalive >= SSE_KEEPALIVE_INTERVAL_MS) {
341
- lastKeepalive = Date.now();
342
- state.lastToolCallAt = Date.now();
343
- try {
344
- await extra.sendNotification({
345
- method: "notifications/progress",
346
- params: {
347
- progressToken,
348
- progress: 0,
349
- total: 0,
350
- },
354
+ // Peek first (non-destructive) to avoid consuming messages when the
355
+ // SSE connection may be dead.
356
+ const peeked = peekThreadMessages(effectiveThreadId);
357
+ if (peeked.length > 0) {
358
+ return processIncomingMessages(effectiveThreadId, peeked.length, ctx, extra);
359
+ }
360
+ // ── Reaction-only wake-up ───────────────────────────────────────
361
+ // Guard: don't consume the reaction file if the SSE connection is
362
+ // already dead — readPendingReaction() is destructive (read + delete).
363
+ // Without this check the reaction is eaten but never delivered.
364
+ if (!extra.signal.aborted &&
365
+ !isSessionSuperseded(ctx.getMcpSessionId?.(), effectiveThreadId)) {
366
+ const reactionResult = await handleReactionOnly({
367
+ telegram,
368
+ getMemoryDb,
369
+ effectiveThreadId,
370
+ sessionStartedAt: state.sessionStartedAt,
371
+ autonomousMode: getEffectiveAutonomousMode(effectiveThreadId),
351
372
  });
373
+ if (reactionResult)
374
+ return reactionResult;
352
375
  }
353
- catch {
354
- // If notification fails, the SSE stream is already dead.
355
- // Return immediately so the agent can reconnect.
356
- log.warn(`[wait] SSE keepalive failed — connection dead. Returning early.`);
357
- state.lastToolCallAt = Date.now();
358
- return {
359
- content: [
360
- {
361
- type: "text",
362
- text: "The connection was interrupted. Please call wait_for_instructions again immediately to resume polling.",
363
- },
364
- ],
365
- };
376
+ // ── Pending task injection (in-loop) ─────────────────────────────────────────
377
+ // Check for tasks sent via send_message_to_thread while we're already
378
+ // polling. Without this, messages arrive only on the NEXT
379
+ // wait_for_instructions call, causing a "no instructions" gap.
380
+ if (!extra.signal.aborted &&
381
+ !isSessionSuperseded(ctx.getMcpSessionId?.(), effectiveThreadId)) {
382
+ const inLoopTask = await consumePendingTask(ctx.getMemoryDb(), effectiveThreadId);
383
+ if (inLoopTask.has())
384
+ return inLoopTask.get();
385
+ const inLoopDeath = consumeDeathNotice(ctx.getMemoryDb(), effectiveThreadId);
386
+ if (inLoopDeath.has())
387
+ return inLoopDeath.get();
366
388
  }
367
- }
368
- await new Promise((resolve) => {
369
- let resolved = false;
370
- let unsubMaintenance = null;
371
- const done = () => {
372
- if (!resolved) {
373
- resolved = true;
374
- clearTimeout(timer);
375
- unsubMaintenance?.();
376
- resolve();
377
- }
378
- };
379
- const timer = setTimeout(done, POLL_INTERVAL_MS);
380
- // Wake up immediately if the maintenance flag is written — don't wait
381
- // for the next 2s tick, which could be too late if killServer() follows
382
- // immediately after the flag is written.
383
- unsubMaintenance = onMaintenanceSignal(done);
384
- if (!extra.signal.aborted) {
385
- extra.signal.addEventListener("abort", done, { once: true });
389
+ // Check scheduled tasks every ~60s during idle polling.
390
+ if (effectiveThreadId !== undefined && Date.now() - lastScheduleCheck >= 60_000) {
391
+ lastScheduleCheck = Date.now();
392
+ const taskResult = checkForDueTasks(ctx, effectiveThreadId);
393
+ if (taskResult)
394
+ return taskResult;
386
395
  }
387
- else {
388
- done();
396
+ // ── In-loop drive activation check (every 30 min) ──────────────────
397
+ // Without this, the drive only fires on poll TIMEOUT (every 2h default).
398
+ // Checking inside the loop ensures consistent activation during long polls.
399
+ if (effectiveThreadId !== undefined &&
400
+ getEffectiveAutonomousMode(effectiveThreadId) &&
401
+ Date.now() - lastDriveCheck >= DRIVE_CHECK_INTERVAL_MS) {
402
+ lastDriveCheck = Date.now();
403
+ runAutoConsolidation({
404
+ state,
405
+ effectiveThreadId,
406
+ getMemoryDb,
407
+ apiKey: config.OPENAI_API_KEY || undefined,
408
+ config,
409
+ memoryRefresh: "",
410
+ scheduleHint: "",
411
+ });
412
+ const driveResult = checkDriveActivation({
413
+ state,
414
+ effectiveThreadId,
415
+ getMemoryDb,
416
+ apiKey: config.OPENAI_API_KEY || undefined,
417
+ config,
418
+ memoryRefresh: "",
419
+ scheduleHint: "",
420
+ });
421
+ if (driveResult)
422
+ return driveResult;
389
423
  }
390
- });
391
- writeActivityHeartbeat();
392
- if (effectiveThreadId !== undefined) {
393
- writeThreadHeartbeatSync(effectiveThreadId);
394
- // Update thread_registry.lastActiveAt periodically (not on every poll
395
- // iteration — every 60s is enough for reconnect detection).
396
- if (Date.now() - lastRegistryUpdate > 60_000) {
397
- lastRegistryUpdate = Date.now();
424
+ // No messages yet — sleep briefly and check again.
425
+ // Send SSE keepalive to prevent silent connection death during long polls.
426
+ if (Date.now() - lastKeepalive >= SSE_KEEPALIVE_INTERVAL_MS) {
427
+ lastKeepalive = Date.now();
428
+ state.lastToolCallAt = Date.now();
398
429
  try {
399
- ctx.threadLifecycle.touchThread(getMemoryDb(), effectiveThreadId, {
400
- lastActiveAt: new Date().toISOString(),
430
+ await extra.sendNotification({
431
+ method: "notifications/progress",
432
+ params: {
433
+ progressToken,
434
+ progress: 0,
435
+ total: 0,
436
+ },
401
437
  });
402
438
  }
403
- catch (err) {
404
- log.debug(`[poll] Heartbeat update failed for thread ${effectiveThreadId}: ${err instanceof Error ? err.message : err}`);
439
+ catch {
440
+ // If notification fails, the SSE stream is already dead.
441
+ // Return immediately so the agent can reconnect.
442
+ log.warn(`[wait] SSE keepalive failed — connection dead. Returning early.`);
443
+ state.lastToolCallAt = Date.now();
444
+ return {
445
+ content: [
446
+ {
447
+ type: "text",
448
+ text: "The connection was interrupted. Please call wait_for_instructions again immediately to resume polling.",
449
+ },
450
+ ],
451
+ };
452
+ }
453
+ }
454
+ await new Promise((resolve) => {
455
+ let resolved = false;
456
+ // A no-op until onMaintenanceSignal hands back the real unsubscribe below:
457
+ // `done` closes over this before the subscription exists, and a done() that
458
+ // fires first has nothing to unsubscribe.
459
+ let unsubMaintenance = () => { };
460
+ const done = () => {
461
+ if (!resolved) {
462
+ resolved = true;
463
+ clearTimeout(timer);
464
+ unsubMaintenance();
465
+ resolve();
466
+ }
467
+ };
468
+ const timer = setTimeout(done, POLL_INTERVAL_MS);
469
+ // Wake up immediately if the maintenance flag is written — don't wait
470
+ // for the next 2s tick, which could be too late if killServer() follows
471
+ // immediately after the flag is written.
472
+ unsubMaintenance = onMaintenanceSignal(done);
473
+ if (!extra.signal.aborted) {
474
+ extra.signal.addEventListener("abort", done, { once: true });
475
+ }
476
+ else {
477
+ done();
478
+ }
479
+ });
480
+ // A transport can close while the two-second poll sleep is resolving.
481
+ // Do not let that retired session write one final heartbeat: it would
482
+ // mask the dead agent from keeper until the next liveness check.
483
+ if (extra.signal.aborted ||
484
+ isSessionSuperseded(ctx.getMcpSessionId?.(), effectiveThreadId)) {
485
+ continue;
486
+ }
487
+ writeActivityHeartbeat();
488
+ if (effectiveThreadId !== undefined) {
489
+ writeThreadHeartbeatSync(effectiveThreadId);
490
+ // Update thread_registry.lastActiveAt periodically (not on every poll
491
+ // iteration — every 60s is enough for reconnect detection).
492
+ if (Date.now() - lastRegistryUpdate > 60_000) {
493
+ lastRegistryUpdate = Date.now();
494
+ try {
495
+ ctx.threadLifecycle.touchThread(getMemoryDb(), effectiveThreadId, {
496
+ lastActiveAt: new Date().toISOString(),
497
+ });
498
+ }
499
+ catch (err) {
500
+ log.debug(`[poll] Heartbeat update failed for thread ${effectiveThreadId}: ${err instanceof Error ? err.message : err}`);
501
+ }
405
502
  }
406
503
  }
407
504
  }
505
+ catch (loopErr) {
506
+ log.error(`Poll loop error: ${loopErr instanceof Error ? loopErr.message : String(loopErr)}`);
507
+ continue;
508
+ }
408
509
  }
409
- catch (loopErr) {
410
- log.error(`Poll loop error: ${loopErr instanceof Error ? loopErr.message : String(loopErr)}`);
411
- continue;
412
- }
510
+ // Timeout elapsed with no actionable message.
511
+ return handlePollTimeout(effectiveThreadId, callNumber, ctx);
512
+ }
513
+ finally {
514
+ unmarkThreadWaiting(effectiveThreadId, parkKey);
413
515
  }
414
- // Timeout elapsed with no actionable message.
415
- return handlePollTimeout(effectiveThreadId, callNumber, ctx);
416
516
  }
417
517
  //# sourceMappingURL=poll-loop.js.map