devchain-cli 0.21.0 → 0.22.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 (377) hide show
  1. package/README.md +1 -0
  2. package/dist/drizzle/0073_integration_storage.sql +27 -0
  3. package/dist/drizzle/0074_black_drax.sql +7 -0
  4. package/dist/drizzle/0075_misty_franklin_richards.sql +37 -0
  5. package/dist/drizzle/0076_epic_time_accounting.sql +48 -0
  6. package/dist/drizzle/meta/0073_snapshot.json +6247 -0
  7. package/dist/drizzle/meta/0074_snapshot.json +6299 -0
  8. package/dist/drizzle/meta/0075_snapshot.json +6563 -0
  9. package/dist/drizzle/meta/0076_snapshot.json +6913 -0
  10. package/dist/drizzle/meta/_journal.json +28 -0
  11. package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.d.ts +27 -1
  12. package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.d.ts.map +1 -1
  13. package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.js +15 -0
  14. package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.js.map +1 -1
  15. package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts +52 -52
  16. package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
  17. package/dist/server/app.main.module.js +4 -0
  18. package/dist/server/app.main.module.js.map +1 -1
  19. package/dist/server/app.normal.module.js +2 -0
  20. package/dist/server/app.normal.module.js.map +1 -1
  21. package/dist/server/common/config/container-scope.d.ts +11 -0
  22. package/dist/server/common/config/container-scope.js +37 -0
  23. package/dist/server/common/config/container-scope.js.map +1 -1
  24. package/dist/server/common/constants/terminal.d.ts +1 -0
  25. package/dist/server/common/constants/terminal.js +2 -1
  26. package/dist/server/common/constants/terminal.js.map +1 -1
  27. package/dist/server/common/filters/http-exception.filter.js +3 -4
  28. package/dist/server/common/filters/http-exception.filter.js.map +1 -1
  29. package/dist/server/common/guards/integration-admission.guard.d.ts +4 -0
  30. package/dist/server/common/guards/integration-admission.guard.js +22 -0
  31. package/dist/server/common/guards/integration-admission.guard.js.map +1 -0
  32. package/dist/server/common/test/app-bootstrap.helper.js +1 -1
  33. package/dist/server/common/test/app-bootstrap.helper.js.map +1 -1
  34. package/dist/server/modules/agent-message-delivery/agent-message-delivery.service.js +4 -0
  35. package/dist/server/modules/agent-message-delivery/agent-message-delivery.service.js.map +1 -1
  36. package/dist/server/modules/cloud/cloud.module.js +5 -1
  37. package/dist/server/modules/cloud/cloud.module.js.map +1 -1
  38. package/dist/server/modules/cloud/services/cloud-egress-bridge.service.d.ts +3 -1
  39. package/dist/server/modules/cloud/services/cloud-egress-bridge.service.js +19 -3
  40. package/dist/server/modules/cloud/services/cloud-egress-bridge.service.js.map +1 -1
  41. package/dist/server/modules/cloud/services/egress-queue.service.d.ts +2 -0
  42. package/dist/server/modules/cloud/services/egress-queue.service.js +35 -17
  43. package/dist/server/modules/cloud/services/egress-queue.service.js.map +1 -1
  44. package/dist/server/modules/cloud/services/event-mapper.service.d.ts +1 -0
  45. package/dist/server/modules/cloud/services/event-mapper.service.js.map +1 -1
  46. package/dist/server/modules/cloud/services/notification-recipient-resolver.service.d.ts +10 -0
  47. package/dist/server/modules/cloud/services/notification-recipient-resolver.service.js +55 -0
  48. package/dist/server/modules/cloud/services/notification-recipient-resolver.service.js.map +1 -0
  49. package/dist/server/modules/cloud-tunnel/services/mobile-rpc-contract.generated.d.ts +31 -5
  50. package/dist/server/modules/cloud-tunnel/services/mobile-rpc-contract.generated.js +15 -0
  51. package/dist/server/modules/cloud-tunnel/services/mobile-rpc-contract.generated.js.map +1 -1
  52. package/dist/server/modules/cloud-tunnel/services/mobile-rpc-workspace-access.service.js +5 -2
  53. package/dist/server/modules/cloud-tunnel/services/mobile-rpc-workspace-access.service.js.map +1 -1
  54. package/dist/server/modules/cloud-tunnel/services/tunnel-handler.service.d.ts +1 -0
  55. package/dist/server/modules/cloud-tunnel/services/tunnel-handler.service.js +8 -0
  56. package/dist/server/modules/cloud-tunnel/services/tunnel-handler.service.js.map +1 -1
  57. package/dist/server/modules/core/controllers/runtime.controller.d.ts +1 -0
  58. package/dist/server/modules/core/controllers/runtime.controller.js +2 -0
  59. package/dist/server/modules/core/controllers/runtime.controller.js.map +1 -1
  60. package/dist/server/modules/e2ee/services/e2ee-device-store.service.d.ts +2 -0
  61. package/dist/server/modules/e2ee/services/e2ee-device-store.service.js +26 -0
  62. package/dist/server/modules/e2ee/services/e2ee-device-store.service.js.map +1 -1
  63. package/dist/server/modules/e2ee/services/e2ee-trust.service.d.ts +5 -0
  64. package/dist/server/modules/e2ee/services/e2ee-trust.service.js +11 -0
  65. package/dist/server/modules/e2ee/services/e2ee-trust.service.js.map +1 -1
  66. package/dist/server/modules/e2ee/services/paired-device-workspace-access.service.d.ts +1 -0
  67. package/dist/server/modules/e2ee/services/paired-device-workspace-access.service.js +4 -0
  68. package/dist/server/modules/e2ee/services/paired-device-workspace-access.service.js.map +1 -1
  69. package/dist/server/modules/epic-time/controllers/epic-time.controller.d.ts +8 -0
  70. package/dist/server/modules/epic-time/controllers/epic-time.controller.js +70 -0
  71. package/dist/server/modules/epic-time/controllers/epic-time.controller.js.map +1 -0
  72. package/dist/server/modules/epic-time/epic-time.module.d.ts +2 -0
  73. package/dist/server/modules/epic-time/epic-time.module.js +28 -0
  74. package/dist/server/modules/epic-time/epic-time.module.js.map +1 -0
  75. package/dist/server/modules/epic-time/models/epic-time.models.d.ts +26 -0
  76. package/dist/server/modules/epic-time/models/epic-time.models.js +3 -0
  77. package/dist/server/modules/epic-time/models/epic-time.models.js.map +1 -0
  78. package/dist/server/modules/epic-time/services/agent-time-accounting.service.d.ts +32 -0
  79. package/dist/server/modules/epic-time/services/agent-time-accounting.service.js +159 -0
  80. package/dist/server/modules/epic-time/services/agent-time-accounting.service.js.map +1 -0
  81. package/dist/server/modules/epic-time/services/epic-time.service.d.ts +15 -0
  82. package/dist/server/modules/epic-time/services/epic-time.service.js +224 -0
  83. package/dist/server/modules/epic-time/services/epic-time.service.js.map +1 -0
  84. package/dist/server/modules/epic-time/services/epic-time.store.d.ts +75 -0
  85. package/dist/server/modules/epic-time/services/epic-time.store.js +453 -0
  86. package/dist/server/modules/epic-time/services/epic-time.store.js.map +1 -0
  87. package/dist/server/modules/epics/controllers/epics.controller.d.ts +10 -0
  88. package/dist/server/modules/epics/controllers/epics.controller.js +92 -0
  89. package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -1
  90. package/dist/server/modules/epics/services/epics.service.d.ts +32 -2
  91. package/dist/server/modules/epics/services/epics.service.js +243 -96
  92. package/dist/server/modules/epics/services/epics.service.js.map +1 -1
  93. package/dist/server/modules/events/catalog/agent.created.d.ts +4 -4
  94. package/dist/server/modules/events/catalog/agent.deleted.d.ts +2 -2
  95. package/dist/server/modules/events/catalog/agent.message.sent.d.ts +40 -40
  96. package/dist/server/modules/events/catalog/broadcast-registry.js +1 -0
  97. package/dist/server/modules/events/catalog/broadcast-registry.js.map +1 -1
  98. package/dist/server/modules/events/catalog/claude.hooks.ask_user_question.pending.d.ts +4 -4
  99. package/dist/server/modules/events/catalog/claude.hooks.session.started.d.ts +10 -10
  100. package/dist/server/modules/events/catalog/epic.comment.created.d.ts +6 -6
  101. package/dist/server/modules/events/catalog/epic.created.d.ts +8 -8
  102. package/dist/server/modules/events/catalog/epic.deleted.d.ts +4 -4
  103. package/dist/server/modules/events/catalog/epic.updated.d.ts +68 -42
  104. package/dist/server/modules/events/catalog/epic.updated.js +5 -0
  105. package/dist/server/modules/events/catalog/epic.updated.js.map +1 -1
  106. package/dist/server/modules/events/catalog/guest.registered.d.ts +2 -2
  107. package/dist/server/modules/events/catalog/guest.unregistered.d.ts +4 -4
  108. package/dist/server/modules/events/catalog/index.d.ts +370 -228
  109. package/dist/server/modules/events/catalog/index.js +11 -1
  110. package/dist/server/modules/events/catalog/index.js.map +1 -1
  111. package/dist/server/modules/events/catalog/integration.connection.created.d.ts +30 -0
  112. package/dist/server/modules/events/catalog/integration.connection.created.js +17 -0
  113. package/dist/server/modules/events/catalog/integration.connection.created.js.map +1 -0
  114. package/dist/server/modules/events/catalog/integration.connection.deleted.d.ts +27 -0
  115. package/dist/server/modules/events/catalog/integration.connection.deleted.js +16 -0
  116. package/dist/server/modules/events/catalog/integration.connection.deleted.js.map +1 -0
  117. package/dist/server/modules/events/catalog/integration.connection.updated.d.ts +39 -0
  118. package/dist/server/modules/events/catalog/integration.connection.updated.js +20 -0
  119. package/dist/server/modules/events/catalog/integration.connection.updated.js.map +1 -0
  120. package/dist/server/modules/events/catalog/review.comment.created.d.ts +16 -16
  121. package/dist/server/modules/events/catalog/review.comment.deleted.d.ts +2 -2
  122. package/dist/server/modules/events/catalog/review.comment.resolved.d.ts +2 -2
  123. package/dist/server/modules/events/catalog/review.comment.updated.d.ts +4 -4
  124. package/dist/server/modules/events/catalog/review.created.d.ts +12 -12
  125. package/dist/server/modules/events/catalog/review.updated.d.ts +22 -22
  126. package/dist/server/modules/events/catalog/scheduled-epic.executed.d.ts +12 -12
  127. package/dist/server/modules/events/catalog/session.human-prompt-state-changed.d.ts +77 -0
  128. package/dist/server/modules/events/catalog/session.human-prompt-state-changed.js +25 -0
  129. package/dist/server/modules/events/catalog/session.human-prompt-state-changed.js.map +1 -0
  130. package/dist/server/modules/events/catalog/session.provider-session-id.discovered.d.ts +2 -2
  131. package/dist/server/modules/events/catalog/session.recommendation.d.ts +4 -4
  132. package/dist/server/modules/events/catalog/session.restored.d.ts +2 -2
  133. package/dist/server/modules/events/catalog/session.started.d.ts +2 -2
  134. package/dist/server/modules/events/catalog/session.starting.d.ts +2 -2
  135. package/dist/server/modules/events/catalog/session.stopped.d.ts +2 -2
  136. package/dist/server/modules/events/catalog/session.transcript.discovered.d.ts +4 -4
  137. package/dist/server/modules/events/catalog/team.config.updated.d.ts +6 -6
  138. package/dist/server/modules/events/catalog/team.member.added.d.ts +2 -2
  139. package/dist/server/modules/events/catalog/team.member.removed.d.ts +2 -2
  140. package/dist/server/modules/events/catalog/terminal.watcher.triggered.d.ts +4 -4
  141. package/dist/server/modules/events/events-core.module.js +8 -1
  142. package/dist/server/modules/events/events-core.module.js.map +1 -1
  143. package/dist/server/modules/events/events-infra.module.js +6 -1
  144. package/dist/server/modules/events/events-infra.module.js.map +1 -1
  145. package/dist/server/modules/events/services/committed-event.store.d.ts +23 -0
  146. package/dist/server/modules/events/services/committed-event.store.js +169 -0
  147. package/dist/server/modules/events/services/committed-event.store.js.map +1 -0
  148. package/dist/server/modules/events/services/durable-event-dispatcher.service.d.ts +16 -0
  149. package/dist/server/modules/events/services/durable-event-dispatcher.service.js +97 -0
  150. package/dist/server/modules/events/services/durable-event-dispatcher.service.js.map +1 -0
  151. package/dist/server/modules/events/services/durable-event-registry.service.d.ts +25 -0
  152. package/dist/server/modules/events/services/durable-event-registry.service.js +59 -0
  153. package/dist/server/modules/events/services/durable-event-registry.service.js.map +1 -0
  154. package/dist/server/modules/events/services/event-log.service.d.ts +6 -1
  155. package/dist/server/modules/events/services/event-log.service.js +32 -18
  156. package/dist/server/modules/events/services/event-log.service.js.map +1 -1
  157. package/dist/server/modules/events/services/events.service.d.ts +11 -1
  158. package/dist/server/modules/events/services/events.service.js +50 -7
  159. package/dist/server/modules/events/services/events.service.js.map +1 -1
  160. package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.d.ts +97 -0
  161. package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.js +1404 -0
  162. package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.js.map +1 -0
  163. package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.d.ts +113 -0
  164. package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.js +1790 -0
  165. package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.js.map +1 -0
  166. package/dist/server/modules/external-integrations/adapters/rich/clickup-comment-converter.d.ts +10 -0
  167. package/dist/server/modules/external-integrations/adapters/rich/clickup-comment-converter.js +97 -0
  168. package/dist/server/modules/external-integrations/adapters/rich/clickup-comment-converter.js.map +1 -0
  169. package/dist/server/modules/external-integrations/adapters/rich/clickup-markdown-converter.d.ts +16 -0
  170. package/dist/server/modules/external-integrations/adapters/rich/clickup-markdown-converter.js +386 -0
  171. package/dist/server/modules/external-integrations/adapters/rich/clickup-markdown-converter.js.map +1 -0
  172. package/dist/server/modules/external-integrations/adapters/rich/jira-adf-converter.d.ts +5 -0
  173. package/dist/server/modules/external-integrations/adapters/rich/jira-adf-converter.js +229 -0
  174. package/dist/server/modules/external-integrations/adapters/rich/jira-adf-converter.js.map +1 -0
  175. package/dist/server/modules/external-integrations/adapters/vendor-shared.d.ts +42 -0
  176. package/dist/server/modules/external-integrations/adapters/vendor-shared.js +182 -0
  177. package/dist/server/modules/external-integrations/adapters/vendor-shared.js.map +1 -0
  178. package/dist/server/modules/external-integrations/connections/integration-connections.controller.d.ts +11 -0
  179. package/dist/server/modules/external-integrations/connections/integration-connections.controller.js +121 -0
  180. package/dist/server/modules/external-integrations/connections/integration-connections.controller.js.map +1 -0
  181. package/dist/server/modules/external-integrations/connections/integration-connections.service.d.ts +46 -0
  182. package/dist/server/modules/external-integrations/connections/integration-connections.service.js +193 -0
  183. package/dist/server/modules/external-integrations/connections/integration-connections.service.js.map +1 -0
  184. package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.d.ts +8 -0
  185. package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.js +73 -0
  186. package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.js.map +1 -0
  187. package/dist/server/modules/external-integrations/errors/external-provider.errors.d.ts +23 -0
  188. package/dist/server/modules/external-integrations/errors/external-provider.errors.js +105 -0
  189. package/dist/server/modules/external-integrations/errors/external-provider.errors.js.map +1 -0
  190. package/dist/server/modules/external-integrations/external-integrations.module.d.ts +2 -0
  191. package/dist/server/modules/external-integrations/external-integrations.module.js +72 -0
  192. package/dist/server/modules/external-integrations/external-integrations.module.js.map +1 -0
  193. package/dist/server/modules/external-integrations/external-task-provider.registry.d.ts +12 -0
  194. package/dist/server/modules/external-integrations/external-task-provider.registry.js +65 -0
  195. package/dist/server/modules/external-integrations/external-task-provider.registry.js.map +1 -0
  196. package/dist/server/modules/external-integrations/models/external-comment-lookup-token.d.ts +12 -0
  197. package/dist/server/modules/external-integrations/models/external-comment-lookup-token.js +49 -0
  198. package/dist/server/modules/external-integrations/models/external-comment-lookup-token.js.map +1 -0
  199. package/dist/server/modules/external-integrations/models/external-edit-session.models.d.ts +94 -0
  200. package/dist/server/modules/external-integrations/models/external-edit-session.models.js +10 -0
  201. package/dist/server/modules/external-integrations/models/external-edit-session.models.js.map +1 -0
  202. package/dist/server/modules/external-integrations/models/external-provider.models.d.ts +341 -0
  203. package/dist/server/modules/external-integrations/models/external-provider.models.js +21 -0
  204. package/dist/server/modules/external-integrations/models/external-provider.models.js.map +1 -0
  205. package/dist/server/modules/external-integrations/models/external-rich-capabilities.d.ts +6 -0
  206. package/dist/server/modules/external-integrations/models/external-rich-capabilities.js +9 -0
  207. package/dist/server/modules/external-integrations/models/external-rich-capabilities.js.map +1 -0
  208. package/dist/server/modules/external-integrations/models/external-rich-document.d.ts +59 -0
  209. package/dist/server/modules/external-integrations/models/external-rich-document.js +233 -0
  210. package/dist/server/modules/external-integrations/models/external-rich-document.js.map +1 -0
  211. package/dist/server/modules/external-integrations/models/external-task-source.d.ts +4 -0
  212. package/dist/server/modules/external-integrations/models/external-task-source.js +71 -0
  213. package/dist/server/modules/external-integrations/models/external-task-source.js.map +1 -0
  214. package/dist/server/modules/external-integrations/models/external-time-mutation.models.d.ts +74 -0
  215. package/dist/server/modules/external-integrations/models/external-time-mutation.models.js +19 -0
  216. package/dist/server/modules/external-integrations/models/external-time-mutation.models.js.map +1 -0
  217. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.d.ts +57 -0
  218. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js +776 -0
  219. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js.map +1 -0
  220. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.d.ts +37 -0
  221. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js +626 -0
  222. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js.map +1 -0
  223. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.d.ts +29 -0
  224. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js +847 -0
  225. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js.map +1 -0
  226. package/dist/server/modules/external-integrations/my-work/external-my-work.service.d.ts +34 -0
  227. package/dist/server/modules/external-integrations/my-work/external-my-work.service.js +330 -0
  228. package/dist/server/modules/external-integrations/my-work/external-my-work.service.js.map +1 -0
  229. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.d.ts +27 -0
  230. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js +412 -0
  231. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js.map +1 -0
  232. package/dist/server/modules/external-integrations/ports/external-task-provider.d.ts +13 -0
  233. package/dist/server/modules/external-integrations/ports/external-task-provider.js +5 -0
  234. package/dist/server/modules/external-integrations/ports/external-task-provider.js.map +1 -0
  235. package/dist/server/modules/external-integrations/request-validation.d.ts +2 -0
  236. package/dist/server/modules/external-integrations/request-validation.js +14 -0
  237. package/dist/server/modules/external-integrations/request-validation.js.map +1 -0
  238. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.d.ts +59 -0
  239. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js +329 -0
  240. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js.map +1 -0
  241. package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.d.ts +44 -0
  242. package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.js +171 -0
  243. package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.js.map +1 -0
  244. package/dist/server/modules/external-integrations/sessions/provider-operation-gate.d.ts +7 -0
  245. package/dist/server/modules/external-integrations/sessions/provider-operation-gate.js +33 -0
  246. package/dist/server/modules/external-integrations/sessions/provider-operation-gate.js.map +1 -0
  247. package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.d.ts +69 -0
  248. package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js +1092 -0
  249. package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js.map +1 -0
  250. package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.d.ts +8 -0
  251. package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.js +23 -0
  252. package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.js.map +1 -0
  253. package/dist/server/modules/external-integrations/subscribers/managed-subtask-recovery-policy.d.ts +7 -0
  254. package/dist/server/modules/external-integrations/subscribers/managed-subtask-recovery-policy.js +30 -0
  255. package/dist/server/modules/external-integrations/subscribers/managed-subtask-recovery-policy.js.map +1 -0
  256. package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.d.ts +44 -0
  257. package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.js +135 -0
  258. package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.js.map +1 -0
  259. package/dist/server/modules/external-integrations/transport/safe-vendor-http-client.d.ts +47 -0
  260. package/dist/server/modules/external-integrations/transport/safe-vendor-http-client.js +294 -0
  261. package/dist/server/modules/external-integrations/transport/safe-vendor-http-client.js.map +1 -0
  262. package/dist/server/modules/hooks/dtos/hook-event.dto.d.ts +64 -64
  263. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +36 -36
  264. package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.d.ts +5 -0
  265. package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.js +51 -1
  266. package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.js.map +1 -1
  267. package/dist/server/modules/orchestrator/sync/services/task-merge.service.d.ts +0 -2
  268. package/dist/server/modules/orchestrator/sync/services/task-merge.service.js +9 -48
  269. package/dist/server/modules/orchestrator/sync/services/task-merge.service.js.map +1 -1
  270. package/dist/server/modules/profiles/dto.d.ts +11 -11
  271. package/dist/server/modules/projects/controllers/projects.controller.d.ts +2 -2
  272. package/dist/server/modules/projects/dtos/export.dto.d.ts +6 -6
  273. package/dist/server/modules/projects/helpers/project-export.d.ts +1 -1
  274. package/dist/server/modules/projects/services/projects.service.d.ts +1 -1
  275. package/dist/server/modules/projects/template-codec/codecs/watchers.codec.d.ts +1 -1
  276. package/dist/server/modules/registry/controllers/templates.controller.d.ts +7 -7
  277. package/dist/server/modules/reviews/dtos/review.dto.d.ts +10 -10
  278. package/dist/server/modules/scheduled-epics/dtos/scheduled-epic.dto.d.ts +1 -1
  279. package/dist/server/modules/session-reader/adapters/antigravity-session-reader.adapter.d.ts +1 -1
  280. package/dist/server/modules/session-reader/adapters/opencode-session-reader.adapter.d.ts +1 -1
  281. package/dist/server/modules/session-reader/data/pricing.json +128 -39
  282. package/dist/server/modules/session-terminal-runtime/session-terminal-runtime.service.d.ts +2 -0
  283. package/dist/server/modules/session-terminal-runtime/session-terminal-runtime.service.js +18 -12
  284. package/dist/server/modules/session-terminal-runtime/session-terminal-runtime.service.js.map +1 -1
  285. package/dist/server/modules/sessions/controllers/sessions.controller.d.ts +3 -0
  286. package/dist/server/modules/sessions/controllers/sessions.controller.js +30 -0
  287. package/dist/server/modules/sessions/controllers/sessions.controller.js.map +1 -1
  288. package/dist/server/modules/sessions/dtos/sessions.dto.d.ts +2 -2
  289. package/dist/server/modules/sessions/services/message-enqueue.service.d.ts +2 -0
  290. package/dist/server/modules/sessions/services/message-enqueue.service.js +2 -0
  291. package/dist/server/modules/sessions/services/message-enqueue.service.js.map +1 -1
  292. package/dist/server/modules/sessions/services/message-pool.types.d.ts +8 -1
  293. package/dist/server/modules/sessions/services/sessions-message-pool.service.d.ts +50 -6
  294. package/dist/server/modules/sessions/services/sessions-message-pool.service.js +828 -190
  295. package/dist/server/modules/sessions/services/sessions-message-pool.service.js.map +1 -1
  296. package/dist/server/modules/settings/dtos/settings.dto.d.ts +8 -0
  297. package/dist/server/modules/settings/dtos/settings.dto.js +1 -0
  298. package/dist/server/modules/settings/dtos/settings.dto.js.map +1 -1
  299. package/dist/server/modules/settings/local/delegates/core-settings.delegate.js +12 -0
  300. package/dist/server/modules/settings/local/delegates/core-settings.delegate.js.map +1 -1
  301. package/dist/server/modules/settings/services/settings.constants.d.ts +1 -1
  302. package/dist/server/modules/settings/services/settings.constants.js +2 -1
  303. package/dist/server/modules/settings/services/settings.constants.js.map +1 -1
  304. package/dist/server/modules/settings/services/settings.service.d.ts +1 -1
  305. package/dist/server/modules/settings/services/settings.service.js +2 -1
  306. package/dist/server/modules/settings/services/settings.service.js.map +1 -1
  307. package/dist/server/modules/skills/dtos/skill.dto.d.ts +2 -2
  308. package/dist/server/modules/storage/db/schema.d.ts +2420 -958
  309. package/dist/server/modules/storage/db/schema.js +144 -2
  310. package/dist/server/modules/storage/db/schema.js.map +1 -1
  311. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +34 -6
  312. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  313. package/dist/server/modules/storage/local/delegates/epic.delegate.d.ts +9 -5
  314. package/dist/server/modules/storage/local/delegates/epic.delegate.js +67 -30
  315. package/dist/server/modules/storage/local/delegates/epic.delegate.js.map +1 -1
  316. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.d.ts +18 -0
  317. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js +320 -0
  318. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js.map +1 -0
  319. package/dist/server/modules/storage/local/delegates/integration.delegate.d.ts +37 -0
  320. package/dist/server/modules/storage/local/delegates/integration.delegate.js +308 -0
  321. package/dist/server/modules/storage/local/delegates/integration.delegate.js.map +1 -0
  322. package/dist/server/modules/storage/local/integration-credential-cipher.d.ts +18 -0
  323. package/dist/server/modules/storage/local/integration-credential-cipher.js +138 -0
  324. package/dist/server/modules/storage/local/integration-credential-cipher.js.map +1 -0
  325. package/dist/server/modules/storage/local/local-storage.service.d.ts +36 -7
  326. package/dist/server/modules/storage/local/local-storage.service.js +93 -10
  327. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  328. package/dist/server/modules/storage/models/domain.models.d.ts +127 -0
  329. package/dist/server/modules/storage/models/domain.models.js +15 -0
  330. package/dist/server/modules/storage/models/domain.models.js.map +1 -1
  331. package/dist/server/modules/storage/storage.module.js +2 -1
  332. package/dist/server/modules/storage/storage.module.js.map +1 -1
  333. package/dist/server/modules/subscribers/dtos/subscriber.dto.d.ts +1 -1
  334. package/dist/server/modules/terminal/dtos/ws-envelope.dto.d.ts +2 -2
  335. package/dist/server/modules/terminal/gateways/terminal.gateway.d.ts +9 -1
  336. package/dist/server/modules/terminal/gateways/terminal.gateway.js +79 -3
  337. package/dist/server/modules/terminal/gateways/terminal.gateway.js.map +1 -1
  338. package/dist/server/modules/terminal/services/human-prompt-state.service.d.ts +58 -0
  339. package/dist/server/modules/terminal/services/human-prompt-state.service.js +206 -0
  340. package/dist/server/modules/terminal/services/human-prompt-state.service.js.map +1 -0
  341. package/dist/server/modules/terminal/services/terminal-activity.service.js +7 -18
  342. package/dist/server/modules/terminal/services/terminal-activity.service.js.map +1 -1
  343. package/dist/server/modules/terminal/services/terminal-io/delivery.d.ts +2 -1
  344. package/dist/server/modules/terminal/services/terminal-io/delivery.js +77 -33
  345. package/dist/server/modules/terminal/services/terminal-io/delivery.js.map +1 -1
  346. package/dist/server/modules/terminal/services/terminal-io/terminal-io.service.d.ts +14 -5
  347. package/dist/server/modules/terminal/services/terminal-io/terminal-io.service.js +64 -8
  348. package/dist/server/modules/terminal/services/terminal-io/terminal-io.service.js.map +1 -1
  349. package/dist/server/modules/terminal/services/terminal-io/types.d.ts +8 -0
  350. package/dist/server/modules/terminal/services/terminal-key-input/terminal-key-input.facade.d.ts +5 -1
  351. package/dist/server/modules/terminal/services/terminal-key-input/terminal-key-input.facade.js +39 -2
  352. package/dist/server/modules/terminal/services/terminal-key-input/terminal-key-input.facade.js.map +1 -1
  353. package/dist/server/modules/terminal/services/terminal-session/terminal-session-registry.d.ts +3 -1
  354. package/dist/server/modules/terminal/services/terminal-session/terminal-session-registry.js +7 -2
  355. package/dist/server/modules/terminal/services/terminal-session/terminal-session-registry.js.map +1 -1
  356. package/dist/server/modules/terminal/services/terminal-session/terminal-session.d.ts +4 -0
  357. package/dist/server/modules/terminal/services/terminal-session/terminal-session.js +8 -0
  358. package/dist/server/modules/terminal/services/terminal-session/terminal-session.js.map +1 -1
  359. package/dist/server/modules/terminal/terminal-delivery.module.js +13 -2
  360. package/dist/server/modules/terminal/terminal-delivery.module.js.map +1 -1
  361. package/dist/server/modules/terminal/terminal.module.js +4 -3
  362. package/dist/server/modules/terminal/terminal.module.js.map +1 -1
  363. package/dist/server/modules/terminal/utils/terminal-activity.d.ts +4 -0
  364. package/dist/server/modules/terminal/utils/terminal-activity.js +86 -0
  365. package/dist/server/modules/terminal/utils/terminal-activity.js.map +1 -0
  366. package/dist/server/modules/watchers/dtos/watcher.dto.d.ts +8 -8
  367. package/dist/server/tsconfig.tsbuildinfo +1 -1
  368. package/dist/server/ui/assets/ExternalRichEditor-DMtnZIMZ.js +113 -0
  369. package/dist/server/ui/assets/{ReviewDetailPage-CNffXKv8.js → ReviewDetailPage-BmyiM5IR.js} +1 -1
  370. package/dist/server/ui/assets/{ReviewsPage-DAcufZ3r.js → ReviewsPage-CQ87BMwT.js} +1 -1
  371. package/dist/server/ui/assets/index-BX6Yze-A.js +1177 -0
  372. package/dist/server/ui/assets/index-qoQEv_u4.css +32 -0
  373. package/dist/server/ui/assets/{useReviewSubscription-DD29I7uU.js → useReviewSubscription-DjbEUzt4.js} +1 -1
  374. package/dist/server/ui/index.html +2 -2
  375. package/package.json +12 -4
  376. package/dist/server/ui/assets/index-7iCs8Dxj.js +0 -1153
  377. package/dist/server/ui/assets/index-CLXZjL_B.css +0 -32
@@ -12,7 +12,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  return function (target, key) { decorator(target, key, paramIndex); }
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.SessionsMessagePoolService = exports.FAILURE_NOTICE_SOURCE = void 0;
15
+ exports.SessionsMessagePoolService = exports.HUMAN_DRAFT_IDLE_GRACE_MS = exports.FAILURE_NOTICE_SOURCE = void 0;
16
16
  const common_1 = require("@nestjs/common");
17
17
  const event_emitter_1 = require("@nestjs/event-emitter");
18
18
  const crypto_1 = require("crypto");
@@ -27,10 +27,20 @@ const logger_1 = require("../../../common/logging/logger");
27
27
  const terminal_io_service_1 = require("../../terminal/services/terminal-io/terminal-io.service");
28
28
  const provider_adapter_factory_1 = require("../../providers/adapters/provider-adapter.factory");
29
29
  const message_pool_types_1 = require("./message-pool.types");
30
+ const session_human_prompt_state_changed_1 = require("../../events/catalog/session.human-prompt-state-changed");
31
+ const human_prompt_state_service_1 = require("../../terminal/services/human-prompt-state.service");
30
32
  const delivery_failure_disclosure_1 = require("./delivery-failure-disclosure");
31
33
  var message_pool_types_2 = require("./message-pool.types");
32
34
  Object.defineProperty(exports, "FAILURE_NOTICE_SOURCE", { enumerable: true, get: function () { return message_pool_types_2.FAILURE_NOTICE_SOURCE; } });
33
35
  const logger = (0, logger_1.createLogger)('SessionsMessagePoolService');
36
+ exports.HUMAN_DRAFT_IDLE_GRACE_MS = 2_000;
37
+ function canStartDeferredClaimMutation(claim) {
38
+ return claim.state.phase === 'preparing' && !claim.state.cancellationReason;
39
+ }
40
+ function deferredClaimCancellationResult(claim, discardedCount) {
41
+ const reason = claim?.state.phase === 'preparing' ? claim.state.cancellationReason : undefined;
42
+ return reason ? { success: false, discardedCount, reason } : null;
43
+ }
34
44
  const DEFAULT_CONFIG = {
35
45
  enabled: true,
36
46
  delayMs: 10000,
@@ -48,10 +58,12 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
48
58
  providerAdapterFactory;
49
59
  messageLog;
50
60
  failureNotifier;
61
+ humanPromptState;
51
62
  pools = new Map();
52
- idleLanes = new Map();
63
+ deferredLanes = new Map();
53
64
  config;
54
- constructor(sessions, coordinator, terminalIO, settings, storage, activityStream, providerAdapterFactory, messageLog, failureNotifier) {
65
+ closing = false;
66
+ constructor(sessions, coordinator, terminalIO, settings, storage, activityStream, providerAdapterFactory, messageLog, failureNotifier, humanPromptState) {
55
67
  this.sessions = sessions;
56
68
  this.coordinator = coordinator;
57
69
  this.terminalIO = terminalIO;
@@ -61,6 +73,7 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
61
73
  this.providerAdapterFactory = providerAdapterFactory;
62
74
  this.messageLog = messageLog;
63
75
  this.failureNotifier = failureNotifier;
76
+ this.humanPromptState = humanPromptState;
64
77
  this.config = this.loadConfigFromSettings();
65
78
  logger.info({ config: this.config }, 'SessionsMessagePoolService initialized with config');
66
79
  }
@@ -156,12 +169,14 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
156
169
  const logEntryId = (0, crypto_1.randomUUID)();
157
170
  const timestamp = Date.now();
158
171
  const deliveryMode = this.resolveDeliveryMode(options);
159
- if (deliveryMode === 'on_idle') {
160
- return this.enqueueOnIdle({
172
+ if (options.deferWhileHumanTyping || options.humanPromptSubmit || deliveryMode === 'on_idle') {
173
+ return this.enqueueSerialized({
161
174
  agentId,
162
175
  text,
163
176
  source,
164
177
  submitKeys,
178
+ preKeys,
179
+ preDelayMs,
165
180
  senderAgentId,
166
181
  clientMessageId,
167
182
  failureDisclosure,
@@ -169,6 +184,9 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
169
184
  agentName,
170
185
  logEntryId,
171
186
  timestamp,
187
+ deliveryMode,
188
+ deferWhileHumanTyping: options.deferWhileHumanTyping === true,
189
+ humanPromptSubmit: options.humanPromptSubmit === true,
172
190
  });
173
191
  }
174
192
  const projectConfig = this.getConfigForProject(projectId, failureDisclosure);
@@ -342,48 +360,79 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
342
360
  }, pool.config.delayMs);
343
361
  return { status: 'queued', poolSize: pool.messages.length, logEntryId };
344
362
  }
345
- async enqueueOnIdle(input) {
346
- const activeSession = this.sessions.getActiveSessionForAgent(input.agentId);
347
- if (input.clientMessageId) {
348
- const existing = this.messageLog.findByClientMessageId(input.clientMessageId, input.agentId, input.source);
349
- if (existing) {
350
- return { status: existing.status, logEntryId: existing.id };
351
- }
352
- }
353
- let detachedLane;
354
- const currentLane = this.idleLanes.get(input.agentId);
355
- if (currentLane && currentLane.sessionId !== activeSession?.id) {
356
- this.idleLanes.delete(input.agentId);
357
- detachedLane = currentLane;
358
- }
359
- if (!activeSession) {
360
- const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(input.failureDisclosure, 'No active session', 'no_active_session');
361
- if (detachedLane) {
362
- await this.failIdleLane(detachedLane, 'Target session is no longer active');
363
+ async enqueueSerialized(input) {
364
+ while (true) {
365
+ const step = await this.coordinator.withAgentLock(input.agentId, async () => {
366
+ const activeSession = this.sessions.getActiveSessionForAgent(input.agentId);
367
+ const existingLane = this.deferredLanes.get(input.agentId);
368
+ if (existingLane && existingLane.sessionId !== activeSession?.id) {
369
+ return {
370
+ detachment: this.detachDeferredLaneUnderAgentLock(existingLane, 'Target session was replaced before deferred delivery'),
371
+ };
372
+ }
373
+ const result = await this.enqueueSerializedUnderAgentLock(input);
374
+ const claim = result.status === 'queued' &&
375
+ input.deliveryMode === 'on_idle' &&
376
+ activeSession?.activityState === 'idle'
377
+ ? this.claimDeferredLaneUnderAgentLock(input.agentId, activeSession.id, false)
378
+ : null;
379
+ return { result, claim };
380
+ });
381
+ if ('result' in step) {
382
+ if (!step.claim)
383
+ return step.result;
384
+ const flushResult = await this.deliverDeferredClaim(step.claim);
385
+ if (!flushResult.success) {
386
+ return { status: 'failed', error: flushResult.reason, logEntryId: input.logEntryId };
387
+ }
388
+ if (flushResult.deliveredCount === 0) {
389
+ const logEntry = this.messageLog.getById(input.logEntryId);
390
+ return {
391
+ status: logEntry?.status ?? 'queued',
392
+ ...(logEntry?.error ? { error: logEntry.error } : {}),
393
+ logEntryId: input.logEntryId,
394
+ };
395
+ }
396
+ return {
397
+ status: flushResult.outcome === 'unconfirmed' ? 'unconfirmed' : 'delivered',
398
+ logEntryId: input.logEntryId,
399
+ };
363
400
  }
364
- return { status: 'failed', error: failure.error };
401
+ await this.settleDeferredLaneDetachment(step.detachment);
365
402
  }
403
+ }
404
+ async enqueueSerializedUnderAgentLock(input) {
405
+ const duplicate = this.findDuplicate(input);
406
+ if (duplicate)
407
+ return duplicate;
366
408
  const projectConfig = this.getConfigForProject(input.projectId, input.failureDisclosure);
367
- let lane = this.idleLanes.get(input.agentId);
368
- if (lane) {
369
- lane.separator = projectConfig.separator;
370
- }
371
- if ((lane?.messages.length ?? 0) >= projectConfig.maxMessages) {
372
- const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(input.failureDisclosure, 'Message pool capacity reached', 'pool_capacity_exceeded');
373
- return { status: 'failed', error: failure.error };
409
+ const activeSession = this.sessions.getActiveSessionForAgent(input.agentId);
410
+ const promptState = activeSession?.tmuxSessionId
411
+ ? this.humanPromptState.getState(activeSession.tmuxSessionId)
412
+ : null;
413
+ const heldGeneration = input.deferWhileHumanTyping && promptState && promptState.phase !== 'inactive'
414
+ ? promptState.generation
415
+ : undefined;
416
+ if (heldGeneration !== undefined || input.deliveryMode === 'on_idle') {
417
+ return this.enqueueDeferredUnderAgentLock(input, projectConfig, activeSession, heldGeneration);
374
418
  }
375
- if (!lane) {
376
- lane = {
377
- sessionId: activeSession.id,
378
- agentId: input.agentId,
379
- projectId: input.projectId,
380
- messages: [],
381
- firstEnqueueTime: input.timestamp,
382
- separator: projectConfig.separator,
383
- };
384
- this.idleLanes.set(input.agentId, lane);
419
+ if (input.humanPromptSubmit || input.deliveryMode === 'immediate' || !projectConfig.enabled) {
420
+ const logEntry = this.createLogEntry(input, true);
421
+ this.messageLog.addEntry(logEntry);
422
+ this.activityStream.broadcastEnqueued(logEntry);
423
+ this.broadcastPoolsUpdate();
424
+ return this.deliverImmediateUnderAgentLock(input, logEntry);
385
425
  }
386
- const logEntry = {
426
+ return this.enqueueProtectedPoolUnderAgentLock(input, projectConfig);
427
+ }
428
+ findDuplicate(input) {
429
+ if (!input.clientMessageId)
430
+ return null;
431
+ const existing = this.messageLog.findByClientMessageId(input.clientMessageId, input.agentId, input.source);
432
+ return existing ? { status: existing.status, logEntryId: existing.id } : null;
433
+ }
434
+ createLogEntry(input, immediate) {
435
+ return {
387
436
  id: input.logEntryId,
388
437
  timestamp: input.timestamp,
389
438
  projectId: input.projectId,
@@ -394,10 +443,11 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
394
443
  senderAgentId: input.senderAgentId,
395
444
  clientMessageId: input.clientMessageId,
396
445
  status: 'queued',
397
- immediate: false,
446
+ immediate,
398
447
  };
399
- this.messageLog.addEntry(logEntry);
400
- lane.messages.push({
448
+ }
449
+ createPooledMessage(input, options) {
450
+ return {
401
451
  text: input.text,
402
452
  source: input.source,
403
453
  timestamp: input.timestamp,
@@ -406,60 +456,192 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
406
456
  logEntryId: input.logEntryId,
407
457
  clientMessageId: input.clientMessageId,
408
458
  failureDisclosure: input.failureDisclosure,
409
- });
459
+ deferWhileHumanTyping: input.deferWhileHumanTyping,
460
+ requiresProviderIdle: options?.requiresProviderIdle === true,
461
+ heldGeneration: options?.heldGeneration,
462
+ };
463
+ }
464
+ async enqueueProtectedPoolUnderAgentLock(input, projectConfig) {
465
+ if (this.getSharedQueuedCount(input.agentId) >= projectConfig.maxMessages) {
466
+ return this.capacityFailure(input.failureDisclosure);
467
+ }
468
+ let pool = this.pools.get(input.agentId);
469
+ if (!pool) {
470
+ pool = this.createAgentPool(input.agentId, input.projectId, projectConfig);
471
+ }
472
+ else if (!this.configsEqual(pool.config, projectConfig)) {
473
+ pool.config = projectConfig;
474
+ if (pool.messages.length > 0)
475
+ this.resetPoolTimers(input.agentId, pool, projectConfig);
476
+ }
477
+ const logEntry = this.createLogEntry(input, false);
478
+ this.messageLog.addEntry(logEntry);
479
+ pool.messages.push(this.createPooledMessage(input));
410
480
  this.activityStream.broadcastEnqueued(logEntry);
411
481
  this.broadcastPoolsUpdate();
412
- const exactSessionIsIdle = activeSession.activityState === 'idle';
413
- const detachedFailure = detachedLane
414
- ? this.failIdleLane(detachedLane, 'Target session was replaced before idle delivery')
415
- : Promise.resolve();
416
- if (exactSessionIsIdle) {
417
- const [, flushResult] = await Promise.all([
418
- detachedFailure,
419
- this.flushIdleLane(input.agentId, activeSession.id),
420
- ]);
421
- if (!flushResult.success) {
422
- return { status: 'failed', error: flushResult.reason, logEntryId: input.logEntryId };
423
- }
424
- if (flushResult.deliveredCount === 0) {
425
- const logEntry = this.messageLog.getById(input.logEntryId);
482
+ if (pool.messages.length >= pool.config.maxMessages) {
483
+ const result = await this.flushPoolUnderAgentLock(input.agentId);
484
+ if (!result.success)
485
+ return { status: 'failed', error: result.reason };
486
+ if (result.outcome === 'deferred') {
426
487
  return {
427
- status: logEntry?.status ?? 'queued',
428
- ...(logEntry?.error ? { error: logEntry.error } : {}),
488
+ status: 'queued',
489
+ poolSize: this.getSharedQueuedCount(input.agentId),
429
490
  logEntryId: input.logEntryId,
430
491
  };
431
492
  }
432
493
  return {
433
- status: flushResult.outcome === 'unconfirmed' ? 'unconfirmed' : 'delivered',
494
+ status: result.outcome === 'unconfirmed' ? 'unconfirmed' : 'delivered',
434
495
  logEntryId: input.logEntryId,
435
496
  };
436
497
  }
437
- await detachedFailure;
438
- return { status: 'queued', poolSize: lane.messages.length, logEntryId: input.logEntryId };
498
+ this.schedulePoolDebounce(input.agentId, pool);
499
+ return {
500
+ status: 'queued',
501
+ poolSize: this.getSharedQueuedCount(input.agentId),
502
+ logEntryId: input.logEntryId,
503
+ };
504
+ }
505
+ async enqueueDeferredUnderAgentLock(input, projectConfig, activeSession, heldGeneration) {
506
+ if (!activeSession?.tmuxSessionId) {
507
+ const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(input.failureDisclosure, 'No active session', 'no_active_session');
508
+ return { status: 'failed', error: failure.error };
509
+ }
510
+ if (this.getSharedQueuedCount(input.agentId) >= projectConfig.maxMessages) {
511
+ return this.capacityFailure(input.failureDisclosure);
512
+ }
513
+ let lane = this.deferredLanes.get(input.agentId);
514
+ if (!lane) {
515
+ lane = {
516
+ sessionId: activeSession.id,
517
+ tmuxSessionName: activeSession.tmuxSessionId,
518
+ agentId: input.agentId,
519
+ projectId: input.projectId,
520
+ messages: [],
521
+ separator: projectConfig.separator,
522
+ requiredGeneration: heldGeneration ?? null,
523
+ quietTimer: null,
524
+ quietBaseline: null,
525
+ activeClaim: null,
526
+ };
527
+ this.deferredLanes.set(input.agentId, lane);
528
+ }
529
+ lane.separator = projectConfig.separator;
530
+ const logEntry = this.createLogEntry(input, input.deliveryMode === 'immediate' || !projectConfig.enabled);
531
+ const message = this.createPooledMessage(input, {
532
+ requiresProviderIdle: input.deliveryMode === 'on_idle',
533
+ heldGeneration,
534
+ });
535
+ this.messageLog.addEntry(logEntry);
536
+ lane.messages.push(message);
537
+ if (heldGeneration !== undefined)
538
+ this.rebindLaneGeneration(lane, heldGeneration);
539
+ this.activityStream.broadcastEnqueued(logEntry);
540
+ this.broadcastPoolsUpdate();
541
+ if (lane.requiredGeneration !== null) {
542
+ this.ensureHumanQuietTimerUnderAgentLock(lane);
543
+ }
544
+ return {
545
+ status: 'queued',
546
+ poolSize: this.getSharedQueuedCount(input.agentId),
547
+ logEntryId: input.logEntryId,
548
+ };
549
+ }
550
+ capacityFailure(failureDisclosure) {
551
+ const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(failureDisclosure, 'Message pool capacity reached', 'pool_capacity_exceeded');
552
+ return { status: 'failed', error: failure.error };
553
+ }
554
+ getSharedQueuedCount(agentId) {
555
+ return ((this.pools.get(agentId)?.messages.length ?? 0) +
556
+ (this.deferredLanes.get(agentId)?.messages.length ?? 0));
557
+ }
558
+ createAgentPool(agentId, projectId, config) {
559
+ const pool = {
560
+ messages: [],
561
+ timer: null,
562
+ maxWaitTimer: null,
563
+ firstEnqueueTime: Date.now(),
564
+ config,
565
+ projectId,
566
+ };
567
+ this.pools.set(agentId, pool);
568
+ pool.maxWaitTimer = setTimeout(() => {
569
+ void this.flushNow(agentId);
570
+ }, config.maxWaitMs);
571
+ return pool;
572
+ }
573
+ schedulePoolDebounce(agentId, pool) {
574
+ if (pool.timer)
575
+ clearTimeout(pool.timer);
576
+ pool.timer = setTimeout(() => {
577
+ void this.flushNow(agentId);
578
+ }, pool.config.delayMs);
439
579
  }
440
580
  async flushNow(agentId) {
581
+ while (true) {
582
+ const step = await this.coordinator.withAgentLock(agentId, async () => {
583
+ const activeSession = this.sessions.getActiveSessionForAgent(agentId);
584
+ const existingLane = this.deferredLanes.get(agentId);
585
+ if (existingLane && existingLane.sessionId !== activeSession?.id) {
586
+ return {
587
+ detachment: this.detachDeferredLaneUnderAgentLock(existingLane, 'Target session was replaced before deferred delivery'),
588
+ };
589
+ }
590
+ return { result: await this.flushPoolUnderAgentLock(agentId) };
591
+ });
592
+ if ('result' in step)
593
+ return step.result;
594
+ await this.settleDeferredLaneDetachment(step.detachment);
595
+ }
596
+ }
597
+ async flushPoolUnderAgentLock(agentId) {
441
598
  const pool = this.pools.get(agentId);
442
599
  if (!pool || pool.messages.length === 0) {
443
- logger.debug({ agentId }, 'No messages to flush');
444
600
  return { success: true, deliveredCount: 0 };
445
601
  }
446
- if (pool.timer) {
447
- clearTimeout(pool.timer);
448
- pool.timer = null;
602
+ this.clearPoolTimers(pool);
603
+ this.pools.delete(agentId);
604
+ const activeSession = this.sessions.getActiveSessionForAgent(agentId);
605
+ const promptState = activeSession?.tmuxSessionId
606
+ ? this.humanPromptState.getState(activeSession.tmuxSessionId)
607
+ : null;
608
+ const protectedMessages = promptState?.phase !== 'inactive'
609
+ ? pool.messages.filter((message) => message.deferWhileHumanTyping)
610
+ : [];
611
+ const deliverableMessages = protectedMessages.length > 0
612
+ ? pool.messages.filter((message) => !message.deferWhileHumanTyping)
613
+ : [...pool.messages];
614
+ if (protectedMessages.length > 0 &&
615
+ activeSession?.tmuxSessionId &&
616
+ promptState &&
617
+ !this.closing) {
618
+ await this.moveMessagesToDeferredLaneUnderAgentLock(agentId, pool.projectId, pool.config.separator, activeSession, protectedMessages, promptState.generation);
449
619
  }
450
- if (pool.maxWaitTimer) {
451
- clearTimeout(pool.maxWaitTimer);
452
- pool.maxWaitTimer = null;
620
+ else if (protectedMessages.length > 0 && this.closing) {
621
+ await this.failDeferredLane({
622
+ sessionId: activeSession?.id ?? 'shutdown',
623
+ tmuxSessionName: activeSession?.tmuxSessionId ?? 'shutdown',
624
+ agentId,
625
+ projectId: pool.projectId,
626
+ messages: protectedMessages,
627
+ separator: pool.config.separator,
628
+ requiredGeneration: promptState?.generation ?? null,
629
+ quietTimer: null,
630
+ quietBaseline: null,
631
+ activeClaim: null,
632
+ }, 'Service stopped before human-held delivery', false);
453
633
  }
454
- const messages = [...pool.messages];
455
- const poolConfig = pool.config;
456
- this.pools.delete(agentId);
457
- logger.info({ agentId, projectId: pool.projectId, messageCount: messages.length }, 'Flushing message pool');
458
- let result = { success: true, deliveredCount: messages.length };
459
- await this.coordinator.withAgentLock(agentId, async () => {
460
- result = await this.deliverBatch(agentId, messages, poolConfig.separator);
461
- });
462
- return result;
634
+ if (deliverableMessages.length === 0) {
635
+ this.broadcastPoolsUpdate();
636
+ return this.closing
637
+ ? {
638
+ success: false,
639
+ discardedCount: protectedMessages.length,
640
+ reason: 'Service stopped before human-held delivery',
641
+ }
642
+ : { success: true, deliveredCount: 0, outcome: 'deferred' };
643
+ }
644
+ return this.deliverBatch(agentId, deliverableMessages, pool.config.separator);
463
645
  }
464
646
  async flushAll() {
465
647
  const pendingPools = Array.from(this.pools.entries()).map(([agentId, pool]) => ({
@@ -473,14 +655,14 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
473
655
  }
474
656
  getPoolStats() {
475
657
  const now = Date.now();
476
- const agentIds = new Set([...this.pools.keys(), ...this.idleLanes.keys()]);
658
+ const agentIds = new Set([...this.pools.keys(), ...this.deferredLanes.keys()]);
477
659
  return Array.from(agentIds).map((agentId) => {
478
660
  const pool = this.pools.get(agentId);
479
- const idleLane = this.idleLanes.get(agentId);
480
- const oldestMessageTime = Math.min(pool?.messages[0]?.timestamp ?? Number.POSITIVE_INFINITY, idleLane?.firstEnqueueTime ?? Number.POSITIVE_INFINITY);
661
+ const deferredLane = this.deferredLanes.get(agentId);
662
+ const oldestMessageTime = Math.min(pool?.messages[0]?.timestamp ?? Number.POSITIVE_INFINITY, deferredLane?.messages[0]?.timestamp ?? Number.POSITIVE_INFINITY);
481
663
  return {
482
664
  agentId,
483
- messageCount: (pool?.messages.length ?? 0) + (idleLane?.messages.length ?? 0),
665
+ messageCount: (pool?.messages.length ?? 0) + (deferredLane?.messages.length ?? 0),
484
666
  waitingMs: now - oldestMessageTime,
485
667
  };
486
668
  });
@@ -489,16 +671,16 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
489
671
  const now = Date.now();
490
672
  const PREVIEW_LENGTH = 100;
491
673
  const details = [];
492
- const agentIds = new Set([...this.pools.keys(), ...this.idleLanes.keys()]);
674
+ const agentIds = new Set([...this.pools.keys(), ...this.deferredLanes.keys()]);
493
675
  for (const agentId of agentIds) {
494
676
  const pool = this.pools.get(agentId);
495
- const idleLane = this.idleLanes.get(agentId);
496
- const pooledMessages = [...(pool?.messages ?? []), ...(idleLane?.messages ?? [])].sort((a, b) => a.timestamp - b.timestamp);
677
+ const deferredLane = this.deferredLanes.get(agentId);
678
+ const pooledMessages = [...(pool?.messages ?? []), ...(deferredLane?.messages ?? [])].sort((a, b) => a.timestamp - b.timestamp);
497
679
  if (pooledMessages.length === 0)
498
680
  continue;
499
681
  const firstMessage = pooledMessages[0];
500
682
  const logEntry = this.messageLog.getById(firstMessage.logEntryId);
501
- const poolProjectId = logEntry?.projectId ?? pool?.projectId ?? idleLane?.projectId ?? 'unknown';
683
+ const poolProjectId = logEntry?.projectId ?? pool?.projectId ?? deferredLane?.projectId ?? 'unknown';
502
684
  const agentName = logEntry?.agentName ?? 'unknown';
503
685
  if (projectId && poolProjectId !== projectId)
504
686
  continue;
@@ -511,22 +693,65 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
511
693
  timestamp: msg.timestamp,
512
694
  };
513
695
  });
514
- details.push({
696
+ const humanHeldMessageCount = deferredLane && deferredLane.requiredGeneration !== null ? deferredLane.messages.length : 0;
697
+ const humanReleaseEligibleAt = humanHeldMessageCount > 0 && deferredLane
698
+ ? this.humanPromptState.getManualReleaseEligibleAt(deferredLane.tmuxSessionName)
699
+ : null;
700
+ const detail = {
515
701
  agentId,
516
702
  agentName,
517
703
  projectId: poolProjectId,
518
704
  messageCount: pooledMessages.length,
705
+ humanHeldMessageCount,
519
706
  waitingMs: now - firstMessage.timestamp,
520
707
  messages,
521
- });
708
+ };
709
+ if (humanReleaseEligibleAt !== null) {
710
+ detail.humanReleaseEligibleAt = humanReleaseEligibleAt;
711
+ }
712
+ details.push(detail);
522
713
  }
523
714
  details.sort((a, b) => b.waitingMs - a.waitingMs);
524
715
  return details;
525
716
  }
717
+ async releaseHumanHeldMessages(agentId, projectId) {
718
+ return this.coordinator.withAgentLock(agentId, async () => {
719
+ const lane = this.deferredLanes.get(agentId);
720
+ const activeSession = this.sessions.getActiveSessionForAgent(agentId);
721
+ if (!lane ||
722
+ lane.projectId !== projectId ||
723
+ !activeSession?.tmuxSessionId ||
724
+ activeSession.id !== lane.sessionId ||
725
+ activeSession.tmuxSessionId !== lane.tmuxSessionName ||
726
+ lane.requiredGeneration === null) {
727
+ return { status: 'not_found' };
728
+ }
729
+ const eligibleAt = this.humanPromptState.getManualReleaseEligibleAt(lane.tmuxSessionName);
730
+ if (eligibleAt === null || Date.now() < eligibleAt) {
731
+ return { status: 'not_ready', eligibleAt };
732
+ }
733
+ const state = this.humanPromptState.getState(lane.tmuxSessionName);
734
+ if (state.phase !== 'draft_active') {
735
+ return { status: 'not_ready', eligibleAt: null };
736
+ }
737
+ const transition = this.humanPromptState.transitionToAwaiting(lane.tmuxSessionName, state.generation, true);
738
+ if (!transition.accepted) {
739
+ return { status: 'not_ready', eligibleAt: null };
740
+ }
741
+ await this.handleHumanPromptStateChangedUnderAgentLock(agentId, {
742
+ sessionId: lane.sessionId,
743
+ tmuxSessionName: lane.tmuxSessionName,
744
+ generation: transition.state.generation,
745
+ phase: 'awaiting_stable_idle',
746
+ });
747
+ return { status: 'released' };
748
+ });
749
+ }
526
750
  async onModuleDestroy() {
751
+ this.closing = true;
527
752
  const poolStats = this.getPoolStats();
528
753
  const totalMessages = poolStats.reduce((sum, p) => sum + p.messageCount, 0);
529
- logger.info({ agentCount: poolStats.length, totalMessages }, 'Shutting down message pool, flushing default lanes and clearing idle lanes...');
754
+ logger.info({ agentCount: poolStats.length, totalMessages }, 'Shutting down message pool, flushing default lanes and failing deferred lanes...');
530
755
  for (const [agentId, pool] of this.pools.entries()) {
531
756
  if (pool.timer) {
532
757
  clearTimeout(pool.timer);
@@ -538,26 +763,45 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
538
763
  }
539
764
  logger.debug({ agentId }, 'Cleared timers for agent pool');
540
765
  }
541
- const idleLanes = Array.from(this.idleLanes.values());
542
- this.idleLanes.clear();
543
- if (idleLanes.length > 0) {
766
+ const deferredLanes = Array.from(this.deferredLanes.values());
767
+ const deferredSettlements = [];
768
+ for (const lane of deferredLanes) {
769
+ const detachment = await this.coordinator.withAgentLock(lane.agentId, async () => {
770
+ const current = this.deferredLanes.get(lane.agentId);
771
+ return current === lane
772
+ ? this.detachDeferredLaneUnderAgentLock(lane, 'Service stopped before deferred delivery')
773
+ : null;
774
+ });
775
+ if (lane.requiredGeneration !== null) {
776
+ this.humanPromptState.clearSession(lane.tmuxSessionName);
777
+ }
778
+ if (detachment) {
779
+ deferredSettlements.push(this.settleDeferredLaneDetachment(detachment, false));
780
+ }
781
+ }
782
+ if (deferredLanes.length > 0) {
544
783
  logger.info({
545
- agentCount: idleLanes.length,
546
- totalMessages: idleLanes.reduce((sum, lane) => sum + lane.messages.length, 0),
547
- }, 'Clearing idle delivery lanes without terminal delivery');
548
- await Promise.all(idleLanes.map((lane) => this.failIdleLane(lane, 'Service stopped before idle delivery', false)));
784
+ agentCount: deferredLanes.length,
785
+ totalMessages: deferredLanes.reduce((sum, lane) => sum + lane.messages.length, 0),
786
+ }, 'Settling deferred delivery lanes before terminal shutdown');
549
787
  }
550
788
  const SHUTDOWN_TIMEOUT_MS = 5000;
551
- const flushPromise = this.flushAll();
552
- const timeoutPromise = new Promise((resolve) => setTimeout(() => {
553
- const remainingPools = this.pools.size;
554
- const remainingMessages = Array.from(this.pools.values()).reduce((sum, p) => sum + p.messages.length, 0);
555
- if (remainingMessages > 0) {
556
- logger.warn({ remainingPools, remainingMessages, timeoutMs: SHUTDOWN_TIMEOUT_MS }, 'Shutdown flush timeout reached, some messages may be lost');
557
- }
558
- resolve();
559
- }, SHUTDOWN_TIMEOUT_MS));
560
- await Promise.race([flushPromise, timeoutPromise]);
789
+ const shutdownWork = Promise.all([...deferredSettlements, this.flushAll()]).then(() => undefined);
790
+ let timeout;
791
+ const timeoutPromise = new Promise((resolve) => {
792
+ timeout = setTimeout(() => {
793
+ const remainingPools = this.pools.size;
794
+ const remainingMessages = this.getPoolStats().reduce((sum, pool) => sum + pool.messageCount, 0);
795
+ if (remainingMessages > 0) {
796
+ logger.warn({ remainingPools, remainingMessages, timeoutMs: SHUTDOWN_TIMEOUT_MS }, 'Shutdown flush timeout reached, some messages may be lost');
797
+ }
798
+ resolve();
799
+ }, SHUTDOWN_TIMEOUT_MS);
800
+ });
801
+ await Promise.race([shutdownWork, timeoutPromise]);
802
+ if (timeout)
803
+ clearTimeout(timeout);
804
+ void shutdownWork.catch((error) => logger.error({ error }, 'Message pool shutdown settlement failed'));
561
805
  logger.info('Message pool shutdown complete');
562
806
  }
563
807
  getMessageLog(options) {
@@ -569,50 +813,285 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
569
813
  getMessageById(messageId) {
570
814
  return this.messageLog.getMessageById(messageId);
571
815
  }
816
+ async handleHumanPromptStateChanged(event) {
817
+ const session = this.sessions.getSession(event.sessionId);
818
+ if (!session?.agentId || session.tmuxSessionId !== event.tmuxSessionName)
819
+ return;
820
+ const agentId = session.agentId;
821
+ while (true) {
822
+ const step = await this.coordinator.withAgentLock(agentId, async () => {
823
+ const activeSession = this.sessions.getActiveSessionForAgent(agentId);
824
+ const existingLane = this.deferredLanes.get(agentId);
825
+ if (activeSession?.id === event.sessionId &&
826
+ existingLane &&
827
+ existingLane.sessionId !== event.sessionId) {
828
+ return {
829
+ detachment: this.detachDeferredLaneUnderAgentLock(existingLane, 'Target session was replaced before deferred delivery'),
830
+ };
831
+ }
832
+ await this.handleHumanPromptStateChangedUnderAgentLock(agentId, event);
833
+ return { done: true };
834
+ });
835
+ if ('done' in step)
836
+ return;
837
+ await this.settleDeferredLaneDetachment(step.detachment);
838
+ }
839
+ }
840
+ async handleHumanPromptStateChangedUnderAgentLock(agentId, event) {
841
+ if (event.phase === 'draft_active') {
842
+ await this.promoteProtectedPoolMessagesUnderAgentLock(agentId, event);
843
+ }
844
+ const lane = this.deferredLanes.get(agentId);
845
+ if (!lane ||
846
+ lane.sessionId !== event.sessionId ||
847
+ lane.tmuxSessionName !== event.tmuxSessionName) {
848
+ return;
849
+ }
850
+ this.rebindLaneGeneration(lane, event.generation);
851
+ if (event.phase === 'draft_active') {
852
+ this.clearDeferredLaneTimer(lane);
853
+ }
854
+ else {
855
+ this.ensureHumanQuietTimerUnderAgentLock(lane);
856
+ }
857
+ this.broadcastPoolsUpdate();
858
+ }
859
+ async promoteProtectedPoolMessagesUnderAgentLock(agentId, event) {
860
+ const pool = this.pools.get(agentId);
861
+ if (!pool)
862
+ return;
863
+ const protectedMessages = pool.messages.filter((message) => message.deferWhileHumanTyping);
864
+ if (protectedMessages.length === 0)
865
+ return;
866
+ const session = this.sessions.getSession(event.sessionId);
867
+ const activeSession = this.sessions.getActiveSessionForAgent(agentId);
868
+ if (!session?.tmuxSessionId ||
869
+ session.tmuxSessionId !== event.tmuxSessionName ||
870
+ activeSession?.id !== event.sessionId) {
871
+ return;
872
+ }
873
+ pool.messages = pool.messages.filter((message) => !message.deferWhileHumanTyping);
874
+ if (pool.messages.length === 0) {
875
+ this.clearPoolTimers(pool);
876
+ this.pools.delete(agentId);
877
+ }
878
+ await this.moveMessagesToDeferredLaneUnderAgentLock(agentId, pool.projectId, pool.config.separator, activeSession, protectedMessages, event.generation);
879
+ }
572
880
  async handleSessionActivityChanged(event) {
573
881
  const session = this.sessions.getSession(event.sessionId);
574
882
  if (!session?.agentId || event.state !== 'idle')
575
883
  return;
576
- await this.flushIdleLane(session.agentId, event.sessionId);
884
+ await this.flushDeferredLane(session.agentId, event.sessionId);
577
885
  }
578
886
  async handleSessionStopped(event) {
579
887
  const session = this.sessions.getSession(event.sessionId);
580
888
  if (!session?.agentId)
581
889
  return;
582
- await this.failMatchingIdleLane(session.agentId, event.sessionId, 'Target session stopped before idle delivery');
890
+ const agentId = session.agentId;
891
+ await this.failMatchingDeferredLane(agentId, event.sessionId, 'Target session stopped before deferred delivery');
583
892
  }
584
893
  async handleSessionCrashed(event) {
585
894
  const session = this.sessions.getSession(event.sessionId);
586
895
  if (!session?.agentId)
587
896
  return;
588
- await this.failMatchingIdleLane(session.agentId, event.sessionId, 'Target session crashed before idle delivery');
897
+ const agentId = session.agentId;
898
+ await this.failMatchingDeferredLane(agentId, event.sessionId, 'Target session crashed before deferred delivery');
589
899
  }
590
- async flushIdleLane(agentId, expectedSessionId) {
591
- let result = { success: true, deliveredCount: 0 };
592
- await this.coordinator.withAgentLock(agentId, async () => {
593
- const expectedSession = this.sessions.getSession(expectedSessionId);
594
- const activeSession = this.sessions.getActiveSessionForAgent(agentId);
595
- const lane = this.idleLanes.get(agentId);
596
- if (!lane ||
597
- lane.sessionId !== expectedSessionId ||
598
- expectedSession?.status !== 'running' ||
599
- expectedSession.activityState !== 'idle' ||
600
- activeSession?.id !== expectedSessionId) {
601
- return;
900
+ async flushDeferredLane(agentId, expectedSessionId, quietGraceElapsed = false) {
901
+ const claim = await this.coordinator.withAgentLock(agentId, async () => this.claimDeferredLaneUnderAgentLock(agentId, expectedSessionId, quietGraceElapsed));
902
+ if (!claim)
903
+ return { success: true, deliveredCount: 0 };
904
+ return this.deliverDeferredClaim(claim);
905
+ }
906
+ async deliverDeferredClaim(claim) {
907
+ const result = await this.deliverBatchToTarget(claim.agentId, claim.messages, claim.separator, { sessionId: claim.sessionId, tmuxSessionName: claim.tmuxSessionName }, claim.quietSnapshot ?? undefined, claim);
908
+ await this.coordinator.withAgentLock(claim.agentId, async () => this.completeDeferredClaimUnderAgentLock(claim, result));
909
+ return result;
910
+ }
911
+ claimDeferredLaneUnderAgentLock(agentId, expectedSessionId, quietGraceElapsed) {
912
+ const lane = this.deferredLanes.get(agentId);
913
+ const expectedSession = this.sessions.getSession(expectedSessionId);
914
+ const activeSession = this.sessions.getActiveSessionForAgent(agentId);
915
+ if (!lane ||
916
+ lane.activeClaim ||
917
+ lane.sessionId !== expectedSessionId ||
918
+ expectedSession?.status !== 'running' ||
919
+ expectedSession.tmuxSessionId !== lane.tmuxSessionName ||
920
+ activeSession?.id !== expectedSessionId) {
921
+ return null;
922
+ }
923
+ let quietSnapshot = null;
924
+ if (lane.requiredGeneration !== null) {
925
+ quietSnapshot = this.humanPromptState.getQuietSnapshot(lane.tmuxSessionName);
926
+ if (!quietGraceElapsed ||
927
+ !quietSnapshot ||
928
+ quietSnapshot.expectedGeneration !== lane.requiredGeneration ||
929
+ !lane.quietBaseline ||
930
+ !this.quietSnapshotsEqual(lane.quietBaseline, quietSnapshot)) {
931
+ const current = this.humanPromptState.getState(lane.tmuxSessionName);
932
+ if (current.phase === 'inactive') {
933
+ lane.requiredGeneration = null;
934
+ lane.quietBaseline = null;
935
+ }
936
+ else {
937
+ this.rebindLaneGeneration(lane, current.generation);
938
+ if (current.phase === 'awaiting_stable_idle') {
939
+ this.ensureHumanQuietTimerUnderAgentLock(lane, true);
940
+ }
941
+ return null;
942
+ }
602
943
  }
603
- this.idleLanes.delete(agentId);
604
- result = await this.deliverBatch(agentId, [...lane.messages], lane.separator);
944
+ }
945
+ if (lane.messages.some((message) => message.requiresProviderIdle) &&
946
+ expectedSession.activityState !== 'idle') {
947
+ if (lane.requiredGeneration !== null) {
948
+ this.ensureHumanQuietTimerUnderAgentLock(lane, true);
949
+ }
950
+ return null;
951
+ }
952
+ this.clearDeferredLaneTimer(lane);
953
+ let resolveCompletion;
954
+ const completion = new Promise((resolve) => {
955
+ resolveCompletion = resolve;
605
956
  });
606
- return result;
957
+ const claim = {
958
+ agentId,
959
+ sessionId: lane.sessionId,
960
+ tmuxSessionName: lane.tmuxSessionName,
961
+ projectId: lane.projectId,
962
+ messages: [...lane.messages],
963
+ separator: lane.separator,
964
+ quietSnapshot: lane.requiredGeneration !== null ? quietSnapshot : null,
965
+ state: { phase: 'preparing' },
966
+ completion,
967
+ resolveCompletion,
968
+ };
969
+ lane.activeClaim = claim;
970
+ return claim;
607
971
  }
608
- async failMatchingIdleLane(agentId, sessionId, reason) {
609
- const lane = this.idleLanes.get(agentId);
610
- if (!lane || lane.sessionId !== sessionId)
972
+ completeDeferredClaimUnderAgentLock(claim, result) {
973
+ if (claim.state.phase === 'complete')
611
974
  return;
612
- this.idleLanes.delete(agentId);
613
- await this.failIdleLane(lane, reason);
975
+ const cancellationReason = claim.state.phase === 'preparing' ? claim.state.cancellationReason : undefined;
976
+ const lane = this.deferredLanes.get(claim.agentId);
977
+ const ownsLane = lane?.sessionId === claim.sessionId && lane.activeClaim === claim ? lane : null;
978
+ if (ownsLane)
979
+ ownsLane.activeClaim = null;
980
+ if (cancellationReason) {
981
+ claim.state = { phase: 'complete', outcome: 'failed' };
982
+ claim.resolveCompletion(null);
983
+ return;
984
+ }
985
+ if (ownsLane && result.outcome !== 'deferred') {
986
+ const claimedIds = new Set(claim.messages.map((message) => message.logEntryId));
987
+ ownsLane.messages.splice(0, ownsLane.messages.length, ...ownsLane.messages.filter((message) => !claimedIds.has(message.logEntryId)));
988
+ }
989
+ const outcome = result.success
990
+ ? (result.outcome ?? 'delivered')
991
+ : 'failed';
992
+ claim.state = { phase: 'complete', outcome };
993
+ if (!ownsLane) {
994
+ const failureLane = claim.detachReason && result.outcome === 'deferred'
995
+ ? this.copyDeferredLaneWithMessages(claim, claim.messages)
996
+ : null;
997
+ claim.resolveCompletion(failureLane);
998
+ return;
999
+ }
1000
+ if (ownsLane.messages.length === 0) {
1001
+ this.clearDeferredLaneTimer(ownsLane);
1002
+ this.deferredLanes.delete(claim.agentId);
1003
+ this.broadcastPoolsUpdate();
1004
+ claim.resolveCompletion(null);
1005
+ return;
1006
+ }
1007
+ const state = this.humanPromptState.getState(ownsLane.tmuxSessionName);
1008
+ if (state.phase === 'inactive') {
1009
+ ownsLane.requiredGeneration = null;
1010
+ ownsLane.quietBaseline = null;
1011
+ setTimeout(() => {
1012
+ void this.flushDeferredLane(claim.agentId, ownsLane.sessionId).catch((error) => logger.error({ agentId: claim.agentId, error }, 'Deferred follow-up delivery failed'));
1013
+ }, 0);
1014
+ }
1015
+ else {
1016
+ this.rebindLaneGeneration(ownsLane, state.generation);
1017
+ if (state.phase === 'awaiting_stable_idle') {
1018
+ this.ensureHumanQuietTimerUnderAgentLock(ownsLane, true);
1019
+ }
1020
+ }
1021
+ this.broadcastPoolsUpdate();
1022
+ claim.resolveCompletion(null);
1023
+ }
1024
+ async failMatchingDeferredLane(agentId, sessionId, reason, notifySenders = true) {
1025
+ const detachment = await this.coordinator.withAgentLock(agentId, async () => {
1026
+ const lane = this.deferredLanes.get(agentId);
1027
+ return !lane || lane.sessionId !== sessionId
1028
+ ? null
1029
+ : this.detachDeferredLaneUnderAgentLock(lane, reason);
1030
+ });
1031
+ if (detachment) {
1032
+ await this.settleDeferredLaneDetachment(detachment, notifySenders);
1033
+ }
614
1034
  }
615
- async failIdleLane(lane, reason, notifySenders = true) {
1035
+ detachDeferredLaneUnderAgentLock(lane, reason) {
1036
+ if (this.deferredLanes.get(lane.agentId) === lane) {
1037
+ this.deferredLanes.delete(lane.agentId);
1038
+ }
1039
+ this.clearDeferredLaneTimer(lane);
1040
+ const claim = lane.activeClaim;
1041
+ lane.activeClaim = null;
1042
+ if (!claim || claim.state.phase === 'complete') {
1043
+ return {
1044
+ reason,
1045
+ failureLane: this.copyDeferredLaneWithMessages(lane, lane.messages),
1046
+ claimCompletion: null,
1047
+ };
1048
+ }
1049
+ claim.detachReason = reason;
1050
+ if (claim.state.phase === 'preparing') {
1051
+ claim.state = { phase: 'preparing', cancellationReason: reason };
1052
+ return {
1053
+ reason,
1054
+ failureLane: this.copyDeferredLaneWithMessages(lane, lane.messages),
1055
+ claimCompletion: null,
1056
+ };
1057
+ }
1058
+ const claimedIds = new Set(claim.messages.map((message) => message.logEntryId));
1059
+ const unclaimedMessages = lane.messages.filter((message) => !claimedIds.has(message.logEntryId));
1060
+ return {
1061
+ reason,
1062
+ failureLane: unclaimedMessages.length > 0
1063
+ ? this.copyDeferredLaneWithMessages(lane, unclaimedMessages)
1064
+ : null,
1065
+ claimCompletion: claim.completion,
1066
+ };
1067
+ }
1068
+ async settleDeferredLaneDetachment(detachment, notifySenders = true) {
1069
+ if (detachment.failureLane) {
1070
+ await this.failDeferredLane(detachment.failureLane, detachment.reason, notifySenders);
1071
+ }
1072
+ if (!detachment.claimCompletion)
1073
+ return;
1074
+ const lateFailureLane = await detachment.claimCompletion;
1075
+ if (lateFailureLane) {
1076
+ await this.failDeferredLane(lateFailureLane, detachment.reason, notifySenders);
1077
+ }
1078
+ }
1079
+ copyDeferredLaneWithMessages(lane, messages) {
1080
+ return {
1081
+ sessionId: lane.sessionId,
1082
+ tmuxSessionName: lane.tmuxSessionName,
1083
+ agentId: lane.agentId,
1084
+ projectId: lane.projectId,
1085
+ messages: [...messages],
1086
+ separator: lane.separator,
1087
+ requiredGeneration: null,
1088
+ quietTimer: null,
1089
+ quietBaseline: null,
1090
+ activeClaim: null,
1091
+ };
1092
+ }
1093
+ async failDeferredLane(lane, reason, notifySenders = true) {
1094
+ this.clearDeferredLaneTimer(lane);
616
1095
  const batchId = (0, crypto_1.randomUUID)();
617
1096
  const failureDisclosure = (0, delivery_failure_disclosure_1.getStrictestFailureDisclosure)(lane.messages);
618
1097
  const batchFailure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(failureDisclosure, reason, 'no_active_session');
@@ -634,7 +1113,7 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
634
1113
  sessionId: lane.sessionId,
635
1114
  messageCount: lane.messages.length,
636
1115
  error: batchFailure.error,
637
- }, 'Idle delivery lane discarded');
1116
+ }, 'Deferred delivery lane discarded');
638
1117
  if (!notifySenders)
639
1118
  return;
640
1119
  await this.failureNotifier
@@ -644,6 +1123,75 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
644
1123
  error: this.disclosedLogError(failureDisclosure, error),
645
1124
  }, 'Failure notification error (best-effort)'));
646
1125
  }
1126
+ clearPoolTimers(pool) {
1127
+ if (pool.timer)
1128
+ clearTimeout(pool.timer);
1129
+ if (pool.maxWaitTimer)
1130
+ clearTimeout(pool.maxWaitTimer);
1131
+ pool.timer = null;
1132
+ pool.maxWaitTimer = null;
1133
+ }
1134
+ clearDeferredLaneTimer(lane) {
1135
+ if (lane.quietTimer)
1136
+ clearTimeout(lane.quietTimer);
1137
+ lane.quietTimer = null;
1138
+ lane.quietBaseline = null;
1139
+ }
1140
+ rebindLaneGeneration(lane, generation) {
1141
+ if (lane.requiredGeneration !== generation)
1142
+ this.clearDeferredLaneTimer(lane);
1143
+ lane.requiredGeneration = generation;
1144
+ for (const message of lane.messages)
1145
+ message.heldGeneration = generation;
1146
+ }
1147
+ ensureHumanQuietTimerUnderAgentLock(lane, restart = false) {
1148
+ if (lane.activeClaim)
1149
+ return;
1150
+ if (restart)
1151
+ this.clearDeferredLaneTimer(lane);
1152
+ if (lane.quietTimer)
1153
+ return;
1154
+ const snapshot = this.humanPromptState.getQuietSnapshot(lane.tmuxSessionName);
1155
+ if (!snapshot || snapshot.expectedGeneration !== lane.requiredGeneration)
1156
+ return;
1157
+ lane.quietBaseline = snapshot;
1158
+ lane.quietTimer = setTimeout(() => {
1159
+ lane.quietTimer = null;
1160
+ void this.flushDeferredLane(lane.agentId, lane.sessionId, true).catch((error) => logger.error({ agentId: lane.agentId, error }, 'Human-quiet delivery failed'));
1161
+ }, exports.HUMAN_DRAFT_IDLE_GRACE_MS);
1162
+ }
1163
+ quietSnapshotsEqual(left, right) {
1164
+ return (left.expectedGeneration === right.expectedGeneration &&
1165
+ left.executedInputEpoch === right.executedInputEpoch &&
1166
+ left.meaningfulOutputEpoch === right.meaningfulOutputEpoch);
1167
+ }
1168
+ async moveMessagesToDeferredLaneUnderAgentLock(agentId, projectId, separator, activeSession, messages, generation) {
1169
+ if (!activeSession.tmuxSessionId)
1170
+ return;
1171
+ let lane = this.deferredLanes.get(agentId);
1172
+ if (!lane) {
1173
+ lane = {
1174
+ sessionId: activeSession.id,
1175
+ tmuxSessionName: activeSession.tmuxSessionId,
1176
+ agentId,
1177
+ projectId,
1178
+ messages: [],
1179
+ separator,
1180
+ requiredGeneration: generation,
1181
+ quietTimer: null,
1182
+ quietBaseline: null,
1183
+ activeClaim: null,
1184
+ };
1185
+ this.deferredLanes.set(agentId, lane);
1186
+ }
1187
+ lane.separator = separator;
1188
+ lane.messages.push(...messages);
1189
+ this.rebindLaneGeneration(lane, generation);
1190
+ const state = this.humanPromptState.getState(lane.tmuxSessionName);
1191
+ if (state.phase === 'awaiting_stable_idle')
1192
+ this.ensureHumanQuietTimerUnderAgentLock(lane);
1193
+ this.broadcastPoolsUpdate();
1194
+ }
647
1195
  async deliverBatch(agentId, messages, separator = this.config.separator) {
648
1196
  const batchId = (0, crypto_1.randomUUID)();
649
1197
  const failureDisclosure = (0, delivery_failure_disclosure_1.getStrictestFailureDisclosure)(messages);
@@ -675,21 +1223,43 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
675
1223
  }, 'Failure notification error (best-effort)'));
676
1224
  return { success: false, discardedCount: messages.length, reason: batchFailure.error };
677
1225
  }
678
- const tmuxSessionId = session.tmuxSessionId;
679
- const baseText = messages.map((m) => m.text).join(separator);
1226
+ return this.deliverBatchToTarget(agentId, messages, separator, {
1227
+ sessionId: session.id,
1228
+ tmuxSessionName: session.tmuxSessionId,
1229
+ });
1230
+ }
1231
+ async deliverBatchToTarget(agentId, messages, separator, target, quietSnapshot, claim) {
1232
+ const batchId = (0, crypto_1.randomUUID)();
1233
+ const failureDisclosure = (0, delivery_failure_disclosure_1.getStrictestFailureDisclosure)(messages);
1234
+ const baseText = messages.map((message) => message.text).join(separator);
680
1235
  const submitKeys = messages[messages.length - 1]?.submitKeys ?? ['Enter'];
681
1236
  try {
682
1237
  const postPasteDelayMs = await this.providerAdapterFactory.getPostPasteDelayMsForAgent(agentId);
683
- const result = await this.terminalIO.deliver({ name: tmuxSessionId }, baseText, {
684
- agentId,
685
- submitKeys,
686
- postPasteDelayMs,
687
- });
1238
+ const result = claim
1239
+ ? await this.terminalIO.deliverGuarded({ name: target.tmuxSessionName }, baseText, { agentId, submitKeys, postPasteDelayMs }, quietSnapshot, {
1240
+ canStartMutation: () => canStartDeferredClaimMutation(claim),
1241
+ markMutationStarted: () => {
1242
+ if (canStartDeferredClaimMutation(claim)) {
1243
+ claim.state = { phase: 'mutating' };
1244
+ }
1245
+ },
1246
+ })
1247
+ : await this.terminalIO.deliver({ name: target.tmuxSessionName }, baseText, {
1248
+ agentId,
1249
+ submitKeys,
1250
+ postPasteDelayMs,
1251
+ });
1252
+ const cancellation = deferredClaimCancellationResult(claim, messages.length);
1253
+ if (cancellation)
1254
+ return cancellation;
1255
+ if ('deferred' in result) {
1256
+ return { success: true, deliveredCount: 0, outcome: 'deferred' };
1257
+ }
688
1258
  const deliveredAt = Date.now();
689
1259
  const status = result.confirmed ? 'delivered' : 'unconfirmed';
690
1260
  const entries = [];
691
- for (const msg of messages) {
692
- this.messageLog.update(msg.logEntryId, {
1261
+ for (const message of messages) {
1262
+ this.messageLog.update(message.logEntryId, {
693
1263
  status,
694
1264
  batchId,
695
1265
  deliveredAt,
@@ -698,7 +1268,7 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
698
1268
  retryCount: result.retryCount,
699
1269
  failureCode: result.confirmed ? undefined : 'paste_not_confirmed',
700
1270
  });
701
- const entry = this.messageLog.getById(msg.logEntryId);
1271
+ const entry = this.messageLog.getById(message.logEntryId);
702
1272
  if (entry)
703
1273
  entries.push(entry);
704
1274
  }
@@ -709,13 +1279,6 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
709
1279
  this.activityStream.broadcastUnconfirmed(batchId, entries);
710
1280
  }
711
1281
  this.broadcastPoolsUpdate();
712
- logger.info({
713
- agentId,
714
- sessionId: session.id,
715
- messageCount: messages.length,
716
- batchId,
717
- confirmed: result.confirmed,
718
- }, 'Batch delivered to agent session');
719
1282
  return {
720
1283
  success: true,
721
1284
  deliveredCount: messages.length,
@@ -724,72 +1287,140 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
724
1287
  }
725
1288
  catch (error) {
726
1289
  const errorMsg = error instanceof Error ? error.message : String(error);
1290
+ const cancellation = deferredClaimCancellationResult(claim, messages.length);
1291
+ if (cancellation)
1292
+ return cancellation;
727
1293
  const failureCode = errorMsg.includes('send keys')
728
1294
  ? 'send_keys_failed'
729
1295
  : 'tmux_error';
730
1296
  const batchFailure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(failureDisclosure, errorMsg, failureCode);
731
- logger.error({ agentId, sessionId: session.id, error: batchFailure.error }, 'Failed to deliver batch to agent session');
732
- for (const msg of messages) {
733
- const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(msg.failureDisclosure, errorMsg, failureCode);
734
- this.messageLog.update(msg.logEntryId, {
1297
+ logger.error({ agentId, sessionId: target.sessionId, error: batchFailure.error }, 'Failed to deliver batch to exact agent session');
1298
+ for (const message of messages) {
1299
+ const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(message.failureDisclosure, errorMsg, failureCode);
1300
+ this.messageLog.update(message.logEntryId, {
735
1301
  status: 'failed',
736
1302
  batchId,
737
1303
  error: failure.error,
738
1304
  failureCode: failure.failureCode,
739
1305
  });
740
- const entry = this.messageLog.getById(msg.logEntryId);
1306
+ const entry = this.messageLog.getById(message.logEntryId);
741
1307
  if (entry)
742
1308
  this.activityStream.broadcastFailed(entry);
743
1309
  }
744
1310
  this.broadcastPoolsUpdate();
745
1311
  await this.failureNotifier
746
1312
  .notifySendersOfFailure(messages, agentId, batchFailure.error)
747
- .catch((err) => logger.warn({
1313
+ .catch((notifyError) => logger.warn({
748
1314
  agentId,
749
- error: failureDisclosure === 'project-safe' ? delivery_failure_disclosure_1.PROJECT_SAFE_DELIVERY_ERROR : err,
1315
+ error: failureDisclosure === 'project-safe' ? delivery_failure_disclosure_1.PROJECT_SAFE_DELIVERY_ERROR : notifyError,
750
1316
  }, 'Failure notification error (best-effort)'));
751
1317
  return { success: false, discardedCount: messages.length, reason: batchFailure.error };
752
1318
  }
753
1319
  }
754
1320
  async deliverMessage(agentId, text, submitKeys, opts) {
1321
+ return this.coordinator.withAgentLock(agentId, () => this.deliverMessageUnderAgentLock(agentId, text, submitKeys, opts));
1322
+ }
1323
+ async deliverMessageUnderAgentLock(agentId, text, submitKeys, opts) {
755
1324
  const activeSessions = await this.sessions.listActiveSessions();
756
- const session = activeSessions.find((s) => s.agentId === agentId);
757
- if (!session || !session.tmuxSessionId) {
1325
+ const session = activeSessions.find((candidate) => candidate.agentId === agentId);
1326
+ if (!session?.tmuxSessionId)
758
1327
  throw new Error(`No active session for agent ${agentId}`);
1328
+ const postPasteDelayMs = await this.providerAdapterFactory.getPostPasteDelayMsForAgent(agentId);
1329
+ if (opts?.skipConfirmation) {
1330
+ const delivery = await this.terminalIO.deliverImmediate({ name: session.tmuxSessionId }, text, {
1331
+ submitKeys,
1332
+ postPasteDelayMs,
1333
+ confirm: false,
1334
+ preKeys: opts.preKeys,
1335
+ preDelayMs: opts.preDelayMs,
1336
+ });
1337
+ return {
1338
+ nonce: delivery.nonce,
1339
+ skipped: true,
1340
+ retryCount: 0,
1341
+ sessionId: session.id,
1342
+ tmuxSessionName: session.tmuxSessionId,
1343
+ };
759
1344
  }
760
- let result = {
761
- nonce: '',
762
- retryCount: 0,
1345
+ const delivery = await this.terminalIO.deliver({ name: session.tmuxSessionId }, text, {
1346
+ agentId,
1347
+ submitKeys,
1348
+ postPasteDelayMs,
1349
+ preKeys: opts?.preKeys,
1350
+ preDelayMs: opts?.preDelayMs,
1351
+ });
1352
+ return {
1353
+ nonce: delivery.nonce,
1354
+ unconfirmed: !delivery.confirmed,
1355
+ retryCount: delivery.retryCount,
1356
+ sessionId: session.id,
1357
+ tmuxSessionName: session.tmuxSessionId,
763
1358
  };
764
- await this.coordinator.withAgentLock(agentId, async () => {
765
- const postPasteDelayMs = await this.providerAdapterFactory.getPostPasteDelayMsForAgent(agentId);
766
- if (opts?.skipConfirmation) {
767
- const delivery = await this.terminalIO.deliverImmediate({ name: session.tmuxSessionId }, text, {
768
- submitKeys,
769
- postPasteDelayMs,
770
- confirm: false,
771
- preKeys: opts?.preKeys,
772
- preDelayMs: opts?.preDelayMs,
773
- });
774
- result = { nonce: delivery.nonce, skipped: true, retryCount: 0 };
1359
+ }
1360
+ async deliverImmediateUnderAgentLock(input, logEntry) {
1361
+ try {
1362
+ const activeSession = this.sessions.getActiveSessionForAgent(input.agentId);
1363
+ const expectedSubmit = input.humanPromptSubmit && activeSession?.tmuxSessionId
1364
+ ? {
1365
+ sessionId: activeSession.id,
1366
+ tmuxSessionName: activeSession.tmuxSessionId,
1367
+ generation: this.humanPromptState.getState(activeSession.tmuxSessionId).generation,
1368
+ }
1369
+ : null;
1370
+ const delivery = await this.deliverMessageUnderAgentLock(input.agentId, input.text, input.submitKeys, {
1371
+ skipConfirmation: input.deliveryMode === 'immediate',
1372
+ preKeys: input.preKeys,
1373
+ preDelayMs: input.preDelayMs,
1374
+ });
1375
+ const status = delivery.unconfirmed ? 'unconfirmed' : 'delivered';
1376
+ const deliveredAt = Date.now();
1377
+ this.messageLog.update(logEntry.id, {
1378
+ status,
1379
+ deliveredAt,
1380
+ nonce: delivery.skipped ? undefined : delivery.nonce,
1381
+ confirmedAt: delivery.skipped || delivery.unconfirmed ? undefined : deliveredAt,
1382
+ retryCount: delivery.retryCount,
1383
+ failureCode: delivery.unconfirmed ? 'paste_not_confirmed' : undefined,
1384
+ });
1385
+ const updated = this.messageLog.getById(logEntry.id);
1386
+ if (updated) {
1387
+ if (delivery.unconfirmed)
1388
+ this.activityStream.broadcastUnconfirmed(logEntry.id, [updated]);
1389
+ else
1390
+ this.activityStream.broadcastDelivered(logEntry.id, [updated]);
775
1391
  }
776
- else {
777
- const delivery = await this.terminalIO.deliver({ name: session.tmuxSessionId }, text, {
778
- agentId,
779
- submitKeys,
780
- postPasteDelayMs,
781
- preKeys: opts?.preKeys,
782
- preDelayMs: opts?.preDelayMs,
783
- });
784
- result = {
785
- nonce: delivery.nonce,
786
- unconfirmed: !delivery.confirmed,
787
- retryCount: delivery.retryCount,
788
- };
1392
+ if (expectedSubmit &&
1393
+ expectedSubmit.sessionId === delivery.sessionId &&
1394
+ expectedSubmit.tmuxSessionName === delivery.tmuxSessionName) {
1395
+ await this.completeHumanSubmitUnderAgentLock(input.agentId, delivery.sessionId, delivery.tmuxSessionName, expectedSubmit.generation);
789
1396
  }
1397
+ return { status, logEntryId: logEntry.id };
1398
+ }
1399
+ catch (error) {
1400
+ const errorMsg = error instanceof Error ? error.message : String(error);
1401
+ const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(input.failureDisclosure, errorMsg, 'tmux_error');
1402
+ logger.error({ agentId: input.agentId, source: input.source, error: failure.error }, 'Immediate delivery failed');
1403
+ this.messageLog.update(logEntry.id, {
1404
+ status: 'failed',
1405
+ error: failure.error,
1406
+ failureCode: failure.failureCode,
1407
+ });
1408
+ const failed = this.messageLog.getById(logEntry.id);
1409
+ if (failed)
1410
+ this.activityStream.broadcastFailed(failed);
1411
+ return { status: 'failed', error: failure.error, logEntryId: logEntry.id };
1412
+ }
1413
+ }
1414
+ async completeHumanSubmitUnderAgentLock(agentId, sessionId, tmuxSessionName, expectedGeneration) {
1415
+ const transition = this.humanPromptState.transitionToAwaiting(tmuxSessionName, expectedGeneration);
1416
+ if (!transition.accepted)
1417
+ return;
1418
+ await this.handleHumanPromptStateChangedUnderAgentLock(agentId, {
1419
+ sessionId,
1420
+ tmuxSessionName,
1421
+ generation: transition.state.generation,
1422
+ phase: 'awaiting_stable_idle',
790
1423
  });
791
- logger.info({ agentId, sessionId: session.id, unconfirmed: result.unconfirmed }, 'Immediate message delivered');
792
- return result;
793
1424
  }
794
1425
  broadcastPoolsUpdate() {
795
1426
  const pools = this.getPoolDetails();
@@ -817,6 +1448,12 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
817
1448
  }
818
1449
  };
819
1450
  exports.SessionsMessagePoolService = SessionsMessagePoolService;
1451
+ __decorate([
1452
+ (0, event_emitter_1.OnEvent)(session_human_prompt_state_changed_1.sessionHumanPromptStateChangedEvent.name, { suppressErrors: false }),
1453
+ __metadata("design:type", Function),
1454
+ __metadata("design:paramtypes", [Object]),
1455
+ __metadata("design:returntype", Promise)
1456
+ ], SessionsMessagePoolService.prototype, "handleHumanPromptStateChanged", null);
820
1457
  __decorate([
821
1458
  (0, event_emitter_1.OnEvent)('session.activity.changed', { async: true }),
822
1459
  __metadata("design:type", Function),
@@ -844,6 +1481,7 @@ exports.SessionsMessagePoolService = SessionsMessagePoolService = __decorate([
844
1481
  settings_service_1.SettingsService, Object, message_activity_stream_service_1.MessageActivityStreamService,
845
1482
  provider_adapter_factory_1.ProviderAdapterFactory,
846
1483
  message_log_service_1.MessageLogService,
847
- delivery_failure_notifier_service_1.DeliveryFailureNotifierService])
1484
+ delivery_failure_notifier_service_1.DeliveryFailureNotifierService,
1485
+ human_prompt_state_service_1.HumanPromptStateService])
848
1486
  ], SessionsMessagePoolService);
849
1487
  //# sourceMappingURL=sessions-message-pool.service.js.map