@spine-event-engine/server 2.0.0-snapshot.24 → 2.0.0-snapshot.26

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 (374) hide show
  1. package/README.md +86 -19
  2. package/REFERENCE.md +538 -150
  3. package/dist/agent/agent-acceptance-receipts.d.ts +69 -0
  4. package/dist/agent/agent-acceptance-receipts.d.ts.map +1 -0
  5. package/dist/agent/agent-acceptance-receipts.js +198 -0
  6. package/dist/agent/agent-acceptance-receipts.js.map +1 -0
  7. package/dist/agent/agent-execution-capacity.d.ts +37 -9
  8. package/dist/agent/agent-execution-capacity.d.ts.map +1 -1
  9. package/dist/agent/agent-execution-capacity.js +73 -15
  10. package/dist/agent/agent-execution-capacity.js.map +1 -1
  11. package/dist/agent/agent-history.d.ts +33 -12
  12. package/dist/agent/agent-history.d.ts.map +1 -1
  13. package/dist/agent/agent-history.js +61 -18
  14. package/dist/agent/agent-history.js.map +1 -1
  15. package/dist/agent/agent-live-ai-audit.d.ts +95 -0
  16. package/dist/agent/agent-live-ai-audit.d.ts.map +1 -0
  17. package/dist/agent/agent-live-ai-audit.js +290 -0
  18. package/dist/agent/agent-live-ai-audit.js.map +1 -0
  19. package/dist/agent/agent-live-ai-runtime.d.ts +300 -0
  20. package/dist/agent/agent-live-ai-runtime.d.ts.map +1 -0
  21. package/dist/agent/agent-live-ai-runtime.js +611 -0
  22. package/dist/agent/agent-live-ai-runtime.js.map +1 -0
  23. package/dist/agent/agent-live-audit.d.ts +67 -0
  24. package/dist/agent/agent-live-audit.d.ts.map +1 -0
  25. package/dist/agent/agent-live-audit.js +121 -0
  26. package/dist/agent/agent-live-audit.js.map +1 -0
  27. package/dist/agent/agent-live-budget.d.ts +84 -0
  28. package/dist/agent/agent-live-budget.d.ts.map +1 -0
  29. package/dist/agent/agent-live-budget.js +161 -0
  30. package/dist/agent/agent-live-budget.js.map +1 -0
  31. package/dist/agent/agent-live-fault.d.ts +18 -0
  32. package/dist/agent/agent-live-fault.d.ts.map +1 -0
  33. package/dist/agent/{agent-execution-fault.js → agent-live-fault.js} +7 -7
  34. package/dist/agent/agent-live-fault.js.map +1 -0
  35. package/dist/agent/agent-live-inputs.d.ts +74 -0
  36. package/dist/agent/agent-live-inputs.d.ts.map +1 -0
  37. package/dist/agent/agent-live-inputs.js +15 -0
  38. package/dist/agent/agent-live-inputs.js.map +1 -0
  39. package/dist/agent/agent-live-interaction-events.d.ts +46 -0
  40. package/dist/agent/agent-live-interaction-events.d.ts.map +1 -0
  41. package/dist/agent/agent-live-interaction-events.js +104 -0
  42. package/dist/agent/agent-live-interaction-events.js.map +1 -0
  43. package/dist/agent/agent-live-mcp-controls.d.ts +129 -0
  44. package/dist/agent/agent-live-mcp-controls.d.ts.map +1 -0
  45. package/dist/agent/agent-live-mcp-controls.js +228 -0
  46. package/dist/agent/agent-live-mcp-controls.js.map +1 -0
  47. package/dist/agent/agent-live-mcp-host.d.ts +88 -0
  48. package/dist/agent/agent-live-mcp-host.d.ts.map +1 -0
  49. package/dist/agent/{agent-mcp-host.js → agent-live-mcp-host.js} +1 -1
  50. package/dist/agent/agent-live-mcp-host.js.map +1 -0
  51. package/dist/agent/{agent-mcp-runtime.d.ts → agent-live-mcp-runtime.d.ts} +6 -6
  52. package/dist/agent/agent-live-mcp-runtime.d.ts.map +1 -0
  53. package/dist/agent/{agent-mcp-runtime.js → agent-live-mcp-runtime.js} +13 -42
  54. package/dist/agent/agent-live-mcp-runtime.js.map +1 -0
  55. package/dist/agent/agent-live-read-runtime.d.ts +72 -0
  56. package/dist/agent/agent-live-read-runtime.d.ts.map +1 -0
  57. package/dist/agent/agent-live-read-runtime.js +122 -0
  58. package/dist/agent/agent-live-read-runtime.js.map +1 -0
  59. package/dist/agent/agent-model-selection.d.ts +21 -34
  60. package/dist/agent/agent-model-selection.d.ts.map +1 -1
  61. package/dist/agent/agent-model-selection.js +61 -78
  62. package/dist/agent/agent-model-selection.js.map +1 -1
  63. package/dist/browser/browser-server.d.ts +163 -0
  64. package/dist/browser/browser-server.d.ts.map +1 -1
  65. package/dist/browser/browser-server.js +579 -188
  66. package/dist/browser/browser-server.js.map +1 -1
  67. package/dist/browser/durable-subscription-bindings.d.ts +5 -0
  68. package/dist/browser/durable-subscription-bindings.d.ts.map +1 -1
  69. package/dist/browser/durable-subscription-bindings.js +226 -38
  70. package/dist/browser/durable-subscription-bindings.js.map +1 -1
  71. package/dist/browser/options.d.ts +5 -0
  72. package/dist/browser/options.d.ts.map +1 -1
  73. package/dist/bus/command-bus.d.ts +17 -18
  74. package/dist/bus/command-bus.d.ts.map +1 -1
  75. package/dist/bus/command-bus.js +97 -54
  76. package/dist/bus/command-bus.js.map +1 -1
  77. package/dist/bus/command-errors.d.ts +20 -4
  78. package/dist/bus/command-errors.d.ts.map +1 -1
  79. package/dist/bus/command-errors.js +47 -7
  80. package/dist/bus/command-errors.js.map +1 -1
  81. package/dist/bus/event-bus.d.ts +20 -4
  82. package/dist/bus/event-bus.d.ts.map +1 -1
  83. package/dist/bus/event-bus.js +166 -14
  84. package/dist/bus/event-bus.js.map +1 -1
  85. package/dist/context/bounded-context.d.ts +169 -98
  86. package/dist/context/bounded-context.d.ts.map +1 -1
  87. package/dist/context/bounded-context.js +837 -503
  88. package/dist/context/bounded-context.js.map +1 -1
  89. package/dist/context/entity-inbox.d.ts +7 -2
  90. package/dist/context/entity-inbox.d.ts.map +1 -1
  91. package/dist/context/entity-inbox.js +22 -13
  92. package/dist/context/entity-inbox.js.map +1 -1
  93. package/dist/context/local-inbox-handoff.d.ts +34 -34
  94. package/dist/context/local-inbox-handoff.d.ts.map +1 -1
  95. package/dist/context/local-inbox-handoff.js +108 -150
  96. package/dist/context/local-inbox-handoff.js.map +1 -1
  97. package/dist/context/projection-handoff.d.ts +2 -2
  98. package/dist/context/projection-handoff.d.ts.map +1 -1
  99. package/dist/context/projection-handoff.js +10 -8
  100. package/dist/context/projection-handoff.js.map +1 -1
  101. package/dist/context/tenant-index.d.ts +36 -31
  102. package/dist/context/tenant-index.d.ts.map +1 -1
  103. package/dist/context/tenant-index.js +123 -125
  104. package/dist/context/tenant-index.js.map +1 -1
  105. package/dist/context/tenant-unknown-error.d.ts +33 -0
  106. package/dist/context/tenant-unknown-error.d.ts.map +1 -0
  107. package/dist/context/tenant-unknown-error.js +75 -0
  108. package/dist/context/tenant-unknown-error.js.map +1 -0
  109. package/dist/delivery/agent-delivery-scope.d.ts +66 -0
  110. package/dist/delivery/agent-delivery-scope.d.ts.map +1 -0
  111. package/dist/delivery/agent-delivery-scope.js +44 -0
  112. package/dist/delivery/agent-delivery-scope.js.map +1 -0
  113. package/dist/delivery/delivery-builder.d.ts +25 -1
  114. package/dist/delivery/delivery-builder.d.ts.map +1 -1
  115. package/dist/delivery/delivery-builder.js +89 -0
  116. package/dist/delivery/delivery-builder.js.map +1 -1
  117. package/dist/delivery/delivery-monitor.d.ts +1 -1
  118. package/dist/delivery/delivery-monitor.js +1 -1
  119. package/dist/delivery/delivery-run-control.d.ts +6 -0
  120. package/dist/delivery/delivery-run-control.d.ts.map +1 -1
  121. package/dist/delivery/delivery-run-control.js +10 -0
  122. package/dist/delivery/delivery-run-control.js.map +1 -1
  123. package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
  124. package/dist/delivery/delivery-supervisor.js +28 -4
  125. package/dist/delivery/delivery-supervisor.js.map +1 -1
  126. package/dist/delivery/delivery-tenant.d.ts +17 -0
  127. package/dist/delivery/delivery-tenant.d.ts.map +1 -0
  128. package/dist/delivery/delivery-tenant.js +50 -0
  129. package/dist/delivery/delivery-tenant.js.map +1 -0
  130. package/dist/delivery/delivery.d.ts +96 -2
  131. package/dist/delivery/delivery.d.ts.map +1 -1
  132. package/dist/delivery/delivery.js +693 -53
  133. package/dist/delivery/delivery.js.map +1 -1
  134. package/dist/delivery/inbox-records.d.ts.map +1 -1
  135. package/dist/delivery/inbox-records.js +9 -16
  136. package/dist/delivery/inbox-records.js.map +1 -1
  137. package/dist/delivery/inbox-storage.d.ts +6 -0
  138. package/dist/delivery/inbox-storage.d.ts.map +1 -1
  139. package/dist/delivery/inbox-storage.js +43 -6
  140. package/dist/delivery/inbox-storage.js.map +1 -1
  141. package/dist/delivery/inbox.d.ts.map +1 -1
  142. package/dist/delivery/inbox.js +3 -2
  143. package/dist/delivery/inbox.js.map +1 -1
  144. package/dist/delivery/reception-observation.d.ts +12 -4
  145. package/dist/delivery/reception-observation.d.ts.map +1 -1
  146. package/dist/delivery/reception-observation.js +14 -3
  147. package/dist/delivery/reception-observation.js.map +1 -1
  148. package/dist/delivery/sharded-work-registry.d.ts +6 -0
  149. package/dist/delivery/sharded-work-registry.d.ts.map +1 -1
  150. package/dist/delivery/sharded-work-registry.js +41 -4
  151. package/dist/delivery/sharded-work-registry.js.map +1 -1
  152. package/dist/entity/entity-storage-descriptor.d.ts.map +1 -1
  153. package/dist/entity/entity-storage-descriptor.js +7 -4
  154. package/dist/entity/entity-storage-descriptor.js.map +1 -1
  155. package/dist/entity/entity.d.ts +59 -0
  156. package/dist/entity/entity.d.ts.map +1 -1
  157. package/dist/entity/entity.js +60 -0
  158. package/dist/entity/entity.js.map +1 -1
  159. package/dist/handler/app-query-handler.d.ts +60 -0
  160. package/dist/handler/app-query-handler.d.ts.map +1 -0
  161. package/dist/{bus/saved-dispatcher-binding.js → handler/app-query-handler.js} +8 -12
  162. package/dist/handler/app-query-handler.js.map +1 -0
  163. package/dist/handler/generated-registry-discovery.d.ts +6 -0
  164. package/dist/handler/generated-registry-discovery.d.ts.map +1 -1
  165. package/dist/handler/generated-registry-discovery.js +71 -2
  166. package/dist/handler/generated-registry-discovery.js.map +1 -1
  167. package/dist/handler/handler-decorators.d.ts +8 -29
  168. package/dist/handler/handler-decorators.d.ts.map +1 -1
  169. package/dist/handler/handler-decorators.js +33 -210
  170. package/dist/handler/handler-decorators.js.map +1 -1
  171. package/dist/handler/spine-metadata-inventory.d.ts +99 -0
  172. package/dist/handler/spine-metadata-inventory.d.ts.map +1 -0
  173. package/dist/handler/spine-metadata-inventory.js +162 -0
  174. package/dist/handler/spine-metadata-inventory.js.map +1 -0
  175. package/dist/index.d.ts +9 -5
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +6 -3
  178. package/dist/index.js.map +1 -1
  179. package/dist/integration/external-messages.d.ts.map +1 -1
  180. package/dist/integration/external-messages.js +9 -15
  181. package/dist/integration/external-messages.js.map +1 -1
  182. package/dist/integration/integration-broker.d.ts.map +1 -1
  183. package/dist/integration/integration-broker.js +112 -6
  184. package/dist/integration/integration-broker.js.map +1 -1
  185. package/dist/integration/third-party-context.d.ts.map +1 -1
  186. package/dist/integration/third-party-context.js +7 -10
  187. package/dist/integration/third-party-context.js.map +1 -1
  188. package/dist/repository/primitive-id.js +26 -11
  189. package/dist/repository/primitive-id.js.map +1 -1
  190. package/dist/repository/repository.d.ts +75 -60
  191. package/dist/repository/repository.d.ts.map +1 -1
  192. package/dist/repository/repository.js +1757 -1818
  193. package/dist/repository/repository.js.map +1 -1
  194. package/dist/runtime/runtime.d.ts +13 -0
  195. package/dist/runtime/runtime.d.ts.map +1 -1
  196. package/dist/runtime/runtime.js +18 -0
  197. package/dist/runtime/runtime.js.map +1 -1
  198. package/dist/runtime/signal-metadata.d.ts.map +1 -1
  199. package/dist/runtime/signal-metadata.js +6 -6
  200. package/dist/runtime/signal-metadata.js.map +1 -1
  201. package/dist/runtime/signal-publisher.d.ts +0 -6
  202. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  203. package/dist/runtime/signal-publisher.js +1 -9
  204. package/dist/runtime/signal-publisher.js.map +1 -1
  205. package/dist/runtime/standalone-handler-runtime.d.ts +0 -7
  206. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  207. package/dist/runtime/standalone-handler-runtime.js +13 -99
  208. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  209. package/dist/server/managed-delivery-gate.d.ts +34 -0
  210. package/dist/server/managed-delivery-gate.d.ts.map +1 -0
  211. package/dist/server/managed-delivery-gate.js +70 -0
  212. package/dist/server/managed-delivery-gate.js.map +1 -0
  213. package/dist/server/managed-server-application.d.ts +12 -11
  214. package/dist/server/managed-server-application.d.ts.map +1 -1
  215. package/dist/server/managed-server-application.js +48 -29
  216. package/dist/server/managed-server-application.js.map +1 -1
  217. package/dist/server/node-coordinator.d.ts +18 -5
  218. package/dist/server/node-coordinator.d.ts.map +1 -1
  219. package/dist/server/node-coordinator.js +554 -84
  220. package/dist/server/node-coordinator.js.map +1 -1
  221. package/dist/server/reserved-spine-rpc-paths.json +1 -0
  222. package/dist/server/server-environment.d.ts +220 -25
  223. package/dist/server/server-environment.d.ts.map +1 -1
  224. package/dist/server/server-environment.js +734 -145
  225. package/dist/server/server-environment.js.map +1 -1
  226. package/dist/server/server-log.d.ts +7 -0
  227. package/dist/server/server-log.d.ts.map +1 -1
  228. package/dist/server/server-log.js +14 -1
  229. package/dist/server/server-log.js.map +1 -1
  230. package/dist/server/server.d.ts +60 -29
  231. package/dist/server/server.d.ts.map +1 -1
  232. package/dist/server/server.js +155 -201
  233. package/dist/server/server.js.map +1 -1
  234. package/dist/services/app-query-execution.d.ts +53 -0
  235. package/dist/services/app-query-execution.d.ts.map +1 -0
  236. package/dist/services/app-query-execution.js +408 -0
  237. package/dist/services/app-query-execution.js.map +1 -0
  238. package/dist/services/query-reader.d.ts +32 -7
  239. package/dist/services/query-reader.d.ts.map +1 -1
  240. package/dist/services/query-reader.js +22 -2
  241. package/dist/services/query-reader.js.map +1 -1
  242. package/dist/services/read-request-status.d.ts +65 -0
  243. package/dist/services/read-request-status.d.ts.map +1 -0
  244. package/dist/services/read-request-status.js +151 -0
  245. package/dist/services/read-request-status.js.map +1 -0
  246. package/dist/services/spine-services.d.ts +37 -8
  247. package/dist/services/spine-services.d.ts.map +1 -1
  248. package/dist/services/spine-services.js +1103 -470
  249. package/dist/services/spine-services.js.map +1 -1
  250. package/dist/services/trusted-reader-ingress.d.ts +92 -0
  251. package/dist/services/trusted-reader-ingress.d.ts.map +1 -0
  252. package/dist/services/trusted-reader-ingress.js +89 -0
  253. package/dist/services/trusted-reader-ingress.js.map +1 -0
  254. package/dist/spi/handler-registry.d.ts +2 -0
  255. package/dist/spi/handler-registry.d.ts.map +1 -1
  256. package/dist/spi/handler-registry.js +1 -1
  257. package/dist/spi/handler-registry.js.map +1 -1
  258. package/dist/stand/event-origin-filter.js +3 -3
  259. package/dist/stand/event-origin-filter.js.map +1 -1
  260. package/dist/stand/read-side-filter-engine.d.ts +46 -0
  261. package/dist/stand/read-side-filter-engine.d.ts.map +1 -0
  262. package/dist/stand/read-side-filter-engine.js +356 -0
  263. package/dist/stand/read-side-filter-engine.js.map +1 -0
  264. package/dist/stand/read-side-filter.d.ts +247 -0
  265. package/dist/stand/read-side-filter.d.ts.map +1 -0
  266. package/dist/stand/read-side-filter.js +39 -0
  267. package/dist/stand/read-side-filter.js.map +1 -0
  268. package/dist/stand/read-side-message.d.ts +66 -0
  269. package/dist/stand/read-side-message.d.ts.map +1 -0
  270. package/dist/stand/read-side-message.js +167 -0
  271. package/dist/stand/read-side-message.js.map +1 -0
  272. package/dist/stand/stand-direct-delivery.d.ts +161 -0
  273. package/dist/stand/stand-direct-delivery.d.ts.map +1 -0
  274. package/dist/stand/stand-direct-delivery.js +210 -0
  275. package/dist/stand/stand-direct-delivery.js.map +1 -0
  276. package/dist/stand/stand.d.ts +94 -42
  277. package/dist/stand/stand.d.ts.map +1 -1
  278. package/dist/stand/stand.js +460 -60
  279. package/dist/stand/stand.js.map +1 -1
  280. package/dist/stand/subscription-observer.d.ts.map +1 -1
  281. package/dist/stand/subscription-observer.js +157 -40
  282. package/dist/stand/subscription-observer.js.map +1 -1
  283. package/dist/stand/subscription-read-filters.d.ts +40 -0
  284. package/dist/stand/subscription-read-filters.d.ts.map +1 -0
  285. package/dist/stand/subscription-read-filters.js +91 -0
  286. package/dist/stand/subscription-read-filters.js.map +1 -0
  287. package/dist/stand/subscription-reader-binding.d.ts +20 -0
  288. package/dist/stand/subscription-reader-binding.d.ts.map +1 -0
  289. package/dist/stand/subscription-reader-binding.js +56 -0
  290. package/dist/stand/subscription-reader-binding.js.map +1 -0
  291. package/dist/stand/subscription-records.d.ts +30 -3
  292. package/dist/stand/subscription-records.d.ts.map +1 -1
  293. package/dist/stand/subscription-records.js +61 -16
  294. package/dist/stand/subscription-records.js.map +1 -1
  295. package/dist/stand/subscription-registry.d.ts +54 -15
  296. package/dist/stand/subscription-registry.d.ts.map +1 -1
  297. package/dist/stand/subscription-registry.js +164 -41
  298. package/dist/stand/subscription-registry.js.map +1 -1
  299. package/dist/stand/subscription-runtime.d.ts +12 -5
  300. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  301. package/dist/stand/subscription-runtime.js +183 -34
  302. package/dist/stand/subscription-runtime.js.map +1 -1
  303. package/dist/testing/index.d.ts.map +1 -1
  304. package/dist/testing/index.js +4 -3
  305. package/dist/testing/index.js.map +1 -1
  306. package/dist/tsconfig.tsbuildinfo +1 -1
  307. package/package.json +14 -14
  308. package/dist/agent/agent-admission.d.ts +0 -80
  309. package/dist/agent/agent-admission.d.ts.map +0 -1
  310. package/dist/agent/agent-admission.js +0 -127
  311. package/dist/agent/agent-admission.js.map +0 -1
  312. package/dist/agent/agent-ai-runtime.d.ts +0 -76
  313. package/dist/agent/agent-ai-runtime.d.ts.map +0 -1
  314. package/dist/agent/agent-ai-runtime.js +0 -1269
  315. package/dist/agent/agent-ai-runtime.js.map +0 -1
  316. package/dist/agent/agent-execution-fault.d.ts +0 -18
  317. package/dist/agent/agent-execution-fault.d.ts.map +0 -1
  318. package/dist/agent/agent-execution-fault.js.map +0 -1
  319. package/dist/agent/agent-execution-session.d.ts +0 -103
  320. package/dist/agent/agent-execution-session.d.ts.map +0 -1
  321. package/dist/agent/agent-execution-session.js +0 -238
  322. package/dist/agent/agent-execution-session.js.map +0 -1
  323. package/dist/agent/agent-interaction-audit.d.ts +0 -58
  324. package/dist/agent/agent-interaction-audit.d.ts.map +0 -1
  325. package/dist/agent/agent-interaction-audit.js +0 -114
  326. package/dist/agent/agent-interaction-audit.js.map +0 -1
  327. package/dist/agent/agent-interaction-events.d.ts +0 -40
  328. package/dist/agent/agent-interaction-events.d.ts.map +0 -1
  329. package/dist/agent/agent-interaction-events.js +0 -129
  330. package/dist/agent/agent-interaction-events.js.map +0 -1
  331. package/dist/agent/agent-mcp-budget.d.ts +0 -125
  332. package/dist/agent/agent-mcp-budget.d.ts.map +0 -1
  333. package/dist/agent/agent-mcp-budget.js +0 -566
  334. package/dist/agent/agent-mcp-budget.js.map +0 -1
  335. package/dist/agent/agent-mcp-host.d.ts +0 -125
  336. package/dist/agent/agent-mcp-host.d.ts.map +0 -1
  337. package/dist/agent/agent-mcp-host.js.map +0 -1
  338. package/dist/agent/agent-mcp-runtime.d.ts.map +0 -1
  339. package/dist/agent/agent-mcp-runtime.js.map +0 -1
  340. package/dist/agent/agent-read-runtime.d.ts +0 -70
  341. package/dist/agent/agent-read-runtime.d.ts.map +0 -1
  342. package/dist/agent/agent-read-runtime.js +0 -320
  343. package/dist/agent/agent-read-runtime.js.map +0 -1
  344. package/dist/agent/agent-revisions.d.ts +0 -35
  345. package/dist/agent/agent-revisions.d.ts.map +0 -1
  346. package/dist/agent/agent-revisions.js +0 -133
  347. package/dist/agent/agent-revisions.js.map +0 -1
  348. package/dist/agent/agent-scheduler.d.ts +0 -99
  349. package/dist/agent/agent-scheduler.d.ts.map +0 -1
  350. package/dist/agent/agent-scheduler.js +0 -398
  351. package/dist/agent/agent-scheduler.js.map +0 -1
  352. package/dist/bus/saved-dispatcher-binding.d.ts +0 -53
  353. package/dist/bus/saved-dispatcher-binding.d.ts.map +0 -1
  354. package/dist/bus/saved-dispatcher-binding.js.map +0 -1
  355. package/dist/delivery/delivery-run-coordinator.d.ts +0 -218
  356. package/dist/delivery/delivery-run-coordinator.d.ts.map +0 -1
  357. package/dist/delivery/delivery-run-coordinator.js +0 -609
  358. package/dist/delivery/delivery-run-coordinator.js.map +0 -1
  359. package/dist/delivery/parked-delivery-obligations.d.ts +0 -182
  360. package/dist/delivery/parked-delivery-obligations.d.ts.map +0 -1
  361. package/dist/delivery/parked-delivery-obligations.js +0 -390
  362. package/dist/delivery/parked-delivery-obligations.js.map +0 -1
  363. package/dist/server/environment-attachment.d.ts +0 -356
  364. package/dist/server/environment-attachment.d.ts.map +0 -1
  365. package/dist/server/environment-attachment.js +0 -2062
  366. package/dist/server/environment-attachment.js.map +0 -1
  367. package/dist/server/environment-delivery-records.d.ts +0 -77
  368. package/dist/server/environment-delivery-records.d.ts.map +0 -1
  369. package/dist/server/environment-delivery-records.js +0 -282
  370. package/dist/server/environment-delivery-records.js.map +0 -1
  371. package/dist/server/environment-delivery-worker.d.ts +0 -161
  372. package/dist/server/environment-delivery-worker.d.ts.map +0 -1
  373. package/dist/server/environment-delivery-worker.js +0 -973
  374. package/dist/server/environment-delivery-worker.js.map +0 -1
@@ -11,21 +11,18 @@
11
11
  * or implied. See the License for the specific language governing permissions and limitations under
12
12
  * the License.
13
13
  */
14
- import { createHash, randomUUID } from "node:crypto";
15
14
  import { clone, create, equals, ScalarType, toBinary, } from "@bufbuild/protobuf";
16
- import { AnySchema, FileDescriptorProtoSchema, Int32ValueSchema, Int64ValueSchema, StringValueSchema, } from "@bufbuild/protobuf/wkt";
17
- import { ValidationException, RejectionThrowable, Validate, TypeUrls, AnyMessages, Identifiers, StringifierRegistry, Time, } from "@spine-event-engine/core";
15
+ import { AnySchema, Int32ValueSchema, Int64ValueSchema, StringValueSchema, TimestampSchema, } from "@bufbuild/protobuf/wkt";
16
+ import { ValidationException, RejectionThrowable, Validate, TypeUrls, AnyPacker, Identifiers, StringifierRegistry, Time, } from "@spine-event-engine/core";
18
17
  import { CommandContextSchema, ActorContextSchema, CommandIdSchema, CommandSchema, EventContextSchema, EventIdSchema, EventSchema, MessageIdSchema, TenantIdSchema, RejectionEventContextSchema, VersionSchema, EntityOption_Kind, } from "@spine-event-engine/proto";
19
18
  import * as EntityLog from "@spine-event-engine/proto/generated/spine/system/server/entity_log_events_pb.js";
20
19
  import { EntityTypeNameSchema } from "@spine-event-engine/proto/generated/spine/system/server/entity_type_pb.js";
21
20
  import { EntityRecordSchema, } from "@spine-event-engine/proto/generated/spine/server/entity/entity_pb.js";
22
21
  import { EventStore, StorageQueryPolicy, } from "@spine-event-engine/storage";
23
- import { TenantBoundary } from "@spine-event-engine/storage/provider";
22
+ import { TenantBoundary, eventStoreAccess, StoragePreparations, } from "@spine-event-engine/storage/provider";
24
23
  import { EntityCommitStorageFactories } from "@spine-event-engine/storage/provider";
25
24
  import { AgentHistoryStorageFactories, } from "@spine-event-engine/storage/provider";
26
- import { AgentExecutionStorageFactories, } from "@spine-event-engine/storage/provider";
27
- import { AgentHistoryEntrySchema, AgentInvocationTerminatedSchema, AgentModelSelectionChangedSchema as SelectionChangedSchema, AiModelKind, AiDiagnosticIdSchema, ModelRefSchema, ToolEffect, ConversationIdSchema, } from "@spine-event-engine/proto/agent";
28
- import { AgentInvocationKeySchema, AgentExecutionStartSchema, AgentExecutionCompletionSchema, AgentInvocationBoundsSchema, AgentInvocationStatus, AgentOutgoingCommandSchema, AgentSignalKeySchema, AgentSavedDispatchTargetSchema as SavedTargetSchema, AgentSavedRepositoryFamily, AgentSavedTargetKind, } from "@spine-event-engine/proto/generated/spine/server/agent/execution_record_pb.js";
25
+ import { AgentHistoryEntrySchema, AgentInvocationTerminatedSchema, AgentModelSelectionChangedSchema as SelectionChangedSchema, AiModelKind, ModelRefSchema, ModelPreferenceSchema, ConversationRecordSchema, ConversationIdSchema, AgentHistoryCursorSchema, } from "@spine-event-engine/proto/agent";
29
26
  import { registryOptions } from "@spine-event-engine/ai/spi/runtime";
30
27
  import { CommandValidationError } from "../bus/command-errors.js";
31
28
  import { SignalPublisher } from "../runtime/signal-publisher.js";
@@ -35,21 +32,20 @@ import { CommandRoutingInternals, } from "./command-routing.js";
35
32
  import { EventRoutingInternals } from "./event-routing.js";
36
33
  import { StateUpdateRoutingInternals, } from "./state-update-routing.js";
37
34
  import { RoutingDeclarations } from "./routing-declarations.js";
35
+ import { snapshotTrustedReader, } from "@spine-event-engine/core/spi/trusted-reader";
36
+ import { Code, ConnectError } from "@connectrpc/connect";
38
37
  import { EventDispatcherOriginSchemas } from "../bus/event-dispatcher-origin-schemas.js";
39
- import { SavedDispatcherBindings, } from "../bus/saved-dispatcher-binding.js";
40
- import { Delivery } from "../delivery/delivery.js";
41
38
  import { receptionObservation } from "../delivery/reception-observation.js";
42
- import { commitFenced } from "./commit-fence.js";
43
- import { AgentHistoryReads } from "../agent/agent-history.js";
44
- import { AgentReadRuntime } from "../agent/agent-read-runtime.js";
45
- import { AgentAdmission } from "../agent/agent-admission.js";
46
- import { AgentRevisions } from "../agent/agent-revisions.js";
47
- import { AgentExecutionSession } from "../agent/agent-execution-session.js";
48
- import { AgentExecutionFault } from "../agent/agent-execution-fault.js";
39
+ import { commitFenced, withDeliveryCommitFence } from "./commit-fence.js";
40
+ import { AgentHistoryReads, } from "../agent/agent-history.js";
41
+ import { AgentLiveAiRuntime } from "../agent/agent-live-ai-runtime.js";
42
+ import { AgentLiveAudit } from "../agent/agent-live-audit.js";
43
+ import { AgentLiveBudget } from "../agent/agent-live-budget.js";
44
+ import { AgentLiveReadRuntime } from "../agent/agent-live-read-runtime.js";
45
+ import { AgentLiveInteractionEvents } from "../agent/agent-live-interaction-events.js";
46
+ import { AgentLiveFault } from "../agent/agent-live-fault.js";
47
+ import { AgentDeliveryScope } from "../delivery/agent-delivery-scope.js";
49
48
  import { AgentModelSelection } from "../agent/agent-model-selection.js";
50
- import { AgentAiRuntime, AgentUncertainAttemptError } from "../agent/agent-ai-runtime.js";
51
- import { AgentInteractionEvents } from "../agent/agent-interaction-events.js";
52
- import { AgentInteractionAudit } from "../agent/agent-interaction-audit.js";
53
49
  import { AgentAiBindings } from "../agent/agent-ai-binding.js";
54
50
  import { InboxTargets } from "../delivery/inbox.js";
55
51
  import { ShardIndex } from "../delivery/shard-index.js";
@@ -150,12 +146,13 @@ export class Repository {
150
146
  return this.#metadata.schema;
151
147
  }
152
148
  /**
153
- * Sets whether future state-history rows are appended for this repository.
149
+ * Sets state-history storage for future acquisitions. Admitted stores retain
150
+ * the setting captured when they began, and prior configurations stay reusable.
154
151
  *
155
152
  * This JVM-parity switch is for controlled administration/testing, not
156
153
  * routine request-time behavior. It never deletes or reconstructs history.
157
154
  *
158
- * @param enabled Whether later successful stores append state-history rows.
155
+ * @param enabled Whether newly admitted stores append state-history rows.
159
156
  */
160
157
  setStateHistoryEnabled(enabled) {
161
158
  if (typeof enabled !== "boolean") {
@@ -165,12 +162,6 @@ export class Repository {
165
162
  if (configuration.stateHistory === enabled)
166
163
  return;
167
164
  configuration.stateHistory = enabled;
168
- const handles = repositoryEntityHandles.get(this);
169
- if (handles !== undefined) {
170
- for (const handle of handles.values())
171
- handle.close();
172
- handles.clear();
173
- }
174
165
  }
175
166
  /**
176
167
  * Returns descriptor metadata for this repository's Entity state.
@@ -216,19 +207,6 @@ export class Repository {
216
207
  agentHistory(id, scope) {
217
208
  return RepositoryAgentReads.history(this, id, scope);
218
209
  }
219
- /**
220
- * Reads the exact recorded phase for one accepted Command or Event.
221
- * The trusted application must authorize the caller before using this read.
222
- *
223
- * @param this Registered Agent repository.
224
- * @param id Typed Agent identifier.
225
- * @param source Original Command or Event ID.
226
- * @param scope Explicit tenant boundary; pass `{}` in a single-tenant Bounded Context.
227
- * @returns Recorded phase, or `undefined` when no invocation was recorded.
228
- */
229
- agentExecution(id, source, scope) {
230
- return RepositoryAgentReads.execution(this, id, source, scope);
231
- }
232
210
  /**
233
211
  * Routes a command to one entity ID without invoking a handler.
234
212
  *
@@ -282,7 +260,14 @@ const RepositoryAgentReads = {
282
260
  : validated;
283
261
  const tenant = scope.tenantId === undefined ? undefined : RepositoryTenants.require(scope.tenantId);
284
262
  const context = RepositoryTenants.storageContextForTenant(runtime.context, tenant);
285
- return { runtime, context, id: copiedId };
263
+ const readSide = {
264
+ ...(scope.actorContext === undefined
265
+ ? {}
266
+ : { actorContext: clone(ActorContextSchema, scope.actorContext) }),
267
+ ...(scope.reader === undefined ? {} : snapshotTrustedReader(scope.reader)),
268
+ ...(scope.signal === undefined ? {} : { signal: scope.signal }),
269
+ };
270
+ return { runtime, context, id: copiedId, readSide };
286
271
  },
287
272
  /**
288
273
  * Returns a detached public history request before provider access.
@@ -293,7 +278,9 @@ const RepositoryAgentReads = {
293
278
  request(request) {
294
279
  return {
295
280
  pageSize: request.pageSize,
296
- ...(request.cursor === undefined ? {} : { cursor: request.cursor }),
281
+ ...(request.cursor === undefined
282
+ ? {}
283
+ : { cursor: clone(AgentHistoryCursorSchema, request.cursor) }),
297
284
  };
298
285
  },
299
286
  /**
@@ -337,28 +324,95 @@ const RepositoryAgentReads = {
337
324
  * @returns Detached history entries and opaque continuation.
338
325
  */
339
326
  async historyPage(repository, captured, view, request) {
327
+ this.checkCanceled(captured.readSide.signal);
340
328
  if (repositoryRuntimes.get(repository) !== captured.runtime)
341
329
  throw new Error("Agent read requires an active repository.");
330
+ await captured.runtime.onAdmit(captured.context.tenantId);
331
+ this.checkCanceled(captured.readSide.signal);
332
+ const page = await RepositoryStorage.track(repository, () => this.loadHistoryPage(repository, captured, view, request));
333
+ this.checkCanceled(captured.readSide.signal);
334
+ return page;
335
+ },
336
+ /**
337
+ * Rejects canceled public history reads even when no policy callback runs.
338
+ * @param signal Current caller cancellation, if supplied.
339
+ */
340
+ checkCanceled(signal) {
341
+ if (signal?.aborted)
342
+ throw new ConnectError("Read canceled.", Code.Canceled);
343
+ },
344
+ /**
345
+ * Reads a retained Agent page and transforms only its outward domain values.
346
+ *
347
+ * @param repository Registered Agent repository.
348
+ * @param captured Stable Entity, tenant, and reader facts.
349
+ * @param view Indexed history category.
350
+ * @param request Detached page request.
351
+ * @returns Visible entries and original continuation.
352
+ */
353
+ async loadHistoryPage(repository, captured, view, request) {
342
354
  const input = RepositoryStorage.entityStorageInput(repository, captured.context);
343
- const history = AgentHistoryStorageFactories.create(captured.runtime.storageFactory, {
344
- context: captured.context,
345
- stateType: repository.stateSchema.typeName,
346
- id: { key: input.id.key },
347
- });
348
- try {
349
- const page = await AgentHistoryReads.readBound({
350
- storage: history,
351
- entityId: captured.id,
352
- scope: repositoryAccess.agentHistoryScope(captured.context, input, captured.id),
353
- }, view, request);
354
- return {
355
- items: page.items,
356
- ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
357
- };
358
- }
359
- finally {
360
- history.close();
361
- }
355
+ const history = RepositoryStorage.openRepositoryAgentHistory(repository, captured.runtime.storageFactory, input);
356
+ const page = await AgentHistoryReads.readBound({
357
+ storage: history,
358
+ entityId: captured.id,
359
+ scope: repositoryAccess.agentHistoryScope(captured.context, input, captured.id),
360
+ }, view, request);
361
+ return this.visibleHistory(captured.runtime.stand, page, captured.id, view, request, captured.context.tenantId, captured.readSide);
362
+ },
363
+ /**
364
+ * Transforms returned Agent domain roots while retaining the original page cursor.
365
+ *
366
+ * @param stand Producing context's read policy.
367
+ * @param page Captured history page before outward transformation.
368
+ * @param entityId Agent identifier used for this call.
369
+ * @param view Requested history category.
370
+ * @param request Original page request.
371
+ * @param tenantId Effective tenant of this Agent.
372
+ * @param readSide Trusted in-process caller facts captured with the reader.
373
+ * @returns Visible entries with the provider's original continuation.
374
+ */
375
+ async visibleHistory(stand, page, entityId, view, request, tenantId, readSide = {}) {
376
+ const facts = {
377
+ operation: { kind: "history", history: { source: "agent", entityId, view, request } },
378
+ ...(tenantId === undefined ? {} : { effectiveTenant: tenantId }),
379
+ ...readSide,
380
+ };
381
+ const items = [];
382
+ for (const entry of page.items) {
383
+ const visible = await this.visibleEntry(stand, entry, facts);
384
+ if (visible !== undefined)
385
+ items.push(visible);
386
+ }
387
+ return { items, ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }) };
388
+ },
389
+ /**
390
+ * Transforms one retained Agent entry without changing its wrapper or cursor.
391
+ *
392
+ * @param stand Producing context's outward read policy.
393
+ * @param entry Captured indexed history entry.
394
+ * @param facts Actual read operation and trusted invocation facts.
395
+ * @returns Visible entry, or undefined when policy removed its root.
396
+ */
397
+ async visibleEntry(stand, entry, facts) {
398
+ const item = entry.item;
399
+ let visible;
400
+ if (item.case === "conversationRecord") {
401
+ const value = await standAccess.filterState(stand, ConversationRecordSchema, item.value, facts);
402
+ if (value !== null)
403
+ visible = { case: "conversationRecord", value };
404
+ }
405
+ else if (item.case === "systemEvent" || item.case === "domainEvent") {
406
+ const value = await standAccess.filterEvent(stand, item.value, facts);
407
+ if (value !== null)
408
+ visible =
409
+ item.case === "systemEvent"
410
+ ? { case: "systemEvent", value }
411
+ : { case: "domainEvent", value };
412
+ }
413
+ return visible === undefined
414
+ ? undefined
415
+ : create(AgentHistoryEntrySchema, { ...entry, item: visible });
362
416
  },
363
417
  /**
364
418
  * Returns one typed category from the full Proto history oneof.
@@ -379,76 +433,6 @@ const RepositoryAgentReads = {
379
433
  ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
380
434
  };
381
435
  },
382
- /**
383
- * Reads the exact saved invocation and exposes only its phase.
384
- *
385
- * @param repository Registered Agent repository.
386
- * @param id Typed Agent identifier.
387
- * @param source Original Command or Event ID.
388
- * @param scope Explicit tenant selection.
389
- * @returns Current phase, or `undefined` for no saved invocation.
390
- */
391
- async execution(repository, id, source, scope) {
392
- const captured = this.capture(repository, id, scope);
393
- const sourceSignal = this.source(source);
394
- const input = RepositoryStorage.entityStorageInput(repository, captured.context);
395
- const key = create(AgentInvocationKeySchema, {
396
- scope: { stateType: repository.stateSchema.typeName, agentKey: input.id.key(captured.id) },
397
- sourceSignal,
398
- });
399
- if (repositoryRuntimes.get(repository) !== captured.runtime)
400
- throw new Error("Agent read requires an active repository.");
401
- const storage = AgentExecutionStorageFactories.create(captured.runtime.storageFactory, {
402
- entity: input,
403
- stateType: repository.stateSchema.typeName,
404
- });
405
- try {
406
- const record = await storage.read(key);
407
- return record === undefined ? undefined : this.status(record.status);
408
- }
409
- finally {
410
- storage.close();
411
- }
412
- },
413
- /**
414
- * Copies an original signal identifier into the private execution key.
415
- *
416
- * @param source Original Command or Event ID.
417
- * @returns Typed private source key.
418
- */
419
- source(source) {
420
- if (source.$typeName === CommandIdSchema.typeName && source.uuid.length > 0)
421
- return create(AgentSignalKeySchema, {
422
- id: { case: "command", value: clone(CommandIdSchema, source) },
423
- });
424
- if (source.$typeName === EventIdSchema.typeName && source.value.length > 0)
425
- return create(AgentSignalKeySchema, {
426
- id: { case: "event", value: clone(EventIdSchema, source) },
427
- });
428
- throw new TypeError("Agent execution requires an original CommandId or EventId.");
429
- },
430
- /**
431
- * Returns the public status for a private execution phase.
432
- *
433
- * @param status Saved private phase.
434
- * @returns Public phase string.
435
- */
436
- status(status) {
437
- switch (status) {
438
- case AgentInvocationStatus.AGENT_INVOCATION_ACCEPTED:
439
- return "accepted";
440
- case AgentInvocationStatus.AGENT_INVOCATION_ACTIVE:
441
- return "active";
442
- case AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_COMMANDS:
443
- return "completed-pending-commands";
444
- case AgentInvocationStatus.AGENT_INVOCATION_COMPLETED:
445
- return "completed";
446
- case AgentInvocationStatus.AGENT_INVOCATION_TERMINATED:
447
- return "terminated";
448
- default:
449
- throw new Error("Agent execution provider returned an unsupported phase.");
450
- }
451
- },
452
436
  };
453
437
  const repositorySnapshots = new WeakMap();
454
438
  const repositoryCreators = new WeakMap();
@@ -459,11 +443,10 @@ const repositoryEntityInboxTargets = new WeakMap();
459
443
  const repositoryProjectionInboxTargets = new WeakMap();
460
444
  const repositoryProjectionDirect = new WeakMap();
461
445
  const repositoryRuntimes = new WeakMap();
462
- const repositoryEntityHandles = new WeakMap();
446
+ const repositoryStorageLifetimes = new WeakMap();
463
447
  const entityStateHistoryCaches = new WeakMap();
464
448
  const repositoryHistoryConfigurations = new WeakMap();
465
449
  const repositoryAiConfigurations = new WeakMap();
466
- const repositoryAgentCodeRevisions = new WeakMap();
467
450
  const repositoryDispatchGuards = new WeakMap();
468
451
  /**
469
452
  * Installs repository routing, history, and callback state after identity validation.
@@ -493,7 +476,7 @@ const RepositoryRegistration = {
493
476
  RepositoryRegistration.installTargets(repository, routing);
494
477
  },
495
478
  /**
496
- * Captures Agent capability and code revisions during repository registration.
479
+ * Captures Agent capability policy during repository registration.
497
480
  *
498
481
  * @typeParam EntityType Concrete generated Entity constructor.
499
482
  * @param repository Registered Entity repository for this operation.
@@ -505,8 +488,6 @@ const RepositoryRegistration = {
505
488
  return;
506
489
  if (options.ai !== undefined)
507
490
  repositoryAiConfigurations.set(repository, options.ai);
508
- if (options.agentCodeRevision !== undefined)
509
- repositoryAgentCodeRevisions.set(repository, options.agentCodeRevision);
510
491
  },
511
492
  /**
512
493
  * Registers durable Entity and direct Projection delivery targets.
@@ -555,6 +536,24 @@ export const repositoryAccess = Object.freeze({
555
536
  }
556
537
  return RepositoryIdentity.cloneRepositorySnapshot(snapshot);
557
538
  },
539
+ /**
540
+ * Prepares actual Entity and Agent families from the captured repository definition.
541
+ * @param repository Registered repository model.
542
+ * @param context Complete selected storage scope.
543
+ * @param factory Configured provider factory.
544
+ * @param mode Creation or compatibility inspection.
545
+ * @returns Completion after all required physical families are ready.
546
+ */
547
+ async prepareStorage(repository, context, factory, mode) {
548
+ const input = RepositoryStorage.entityStorageInput(repository, context);
549
+ const preparation = StoragePreparations.for(factory);
550
+ if (repository.entityFamily !== "agent") {
551
+ await preparation.prepareEntity(input, mode);
552
+ return;
553
+ }
554
+ await preparation.prepareAgentCommit(input, mode);
555
+ await preparation.prepareAgentHistory({ context, stateType: input.sourceType.typeName, id: { key: input.id.key } }, mode);
556
+ },
558
557
  /**
559
558
  * Reads schemas for Events this repository can emit.
560
559
  *
@@ -568,32 +567,12 @@ export const repositoryAccess = Object.freeze({
568
567
  }
569
568
  return schemas;
570
569
  },
571
- producedCommandSchemas(repository) {
572
- const routing = repositoryRoutings.get(repository);
573
- if (routing === undefined)
574
- throw new TypeError("Produced Command schemas require a Repository instance.");
575
- return routing.producedCommandSchemas;
576
- },
570
+ /**
571
+ * Returns the AI registry configured for this repository, if present.
572
+ */
577
573
  agentConfiguration(repository) {
578
574
  const ai = repositoryAiConfigurations.get(repository);
579
- const codeRevision = repositoryAgentCodeRevisions.get(repository);
580
- return {
581
- ...(ai === undefined ? {} : { ai }),
582
- ...(codeRevision === undefined ? {} : { codeRevision }),
583
- };
584
- },
585
- runAcceptedAgent(repository, tenantId, key, signal) {
586
- return AgentExecutionRunner.run(repository, tenantId, key, signal);
587
- },
588
- pendingAcceptedAgents(repository, tenantId, after, count) {
589
- const runtime = repositoryRuntimes.get(repository);
590
- if (runtime === undefined || repository.entityFamily !== "agent")
591
- throw new Error("Agent pending discovery requires a registered Agent repository.");
592
- const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
593
- const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
594
- if (storage.agentExecution === undefined)
595
- throw new Error("Agent pending discovery requires execution storage.");
596
- return storage.agentExecution.pending({ count, ...(after === undefined ? {} : { after }) });
575
+ return ai === undefined ? {} : { ai };
597
576
  },
598
577
  /**
599
578
  * Opens a scoped provider read without restoring an application Entity.
@@ -612,19 +591,17 @@ export const repositoryAccess = Object.freeze({
612
591
  if (runtime === undefined)
613
592
  throw new Error("Agent history requires an active repository.");
614
593
  const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
594
+ await runtime.onAdmit(context.tenantId);
615
595
  const input = RepositoryStorage.entityStorageInput(repository, context);
616
- const history = AgentHistoryStorageFactories.create(runtime.storageFactory, {
617
- context,
618
- stateType: repository.stateSchema.typeName,
619
- id: { key: input.id.key },
620
- });
621
- try {
622
- return await history.read({ entityId, view, ...read });
623
- }
624
- finally {
625
- history.close();
626
- }
596
+ return RepositoryStorage.track(repository, () => RepositoryStorage.openRepositoryAgentHistory(repository, runtime.storageFactory, input).read({
597
+ entityId,
598
+ view,
599
+ ...read,
600
+ }));
627
601
  },
602
+ /**
603
+ * Reads one tenant-admitted Agent history page through retained history storage.
604
+ */
628
605
  async agentHistoryPage(repository, entityId, request, tenantId) {
629
606
  if (repository.entityFamily !== "agent")
630
607
  throw new TypeError("Agent history requires an Agent repository.");
@@ -632,25 +609,19 @@ export const repositoryAccess = Object.freeze({
632
609
  if (runtime === undefined)
633
610
  throw new Error("Agent history requires an active repository.");
634
611
  const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
612
+ await runtime.onAdmit(context.tenantId);
635
613
  const input = RepositoryStorage.entityStorageInput(repository, context);
636
- const history = AgentHistoryStorageFactories.create(runtime.storageFactory, {
637
- context,
638
- stateType: repository.stateSchema.typeName,
639
- id: { key: input.id.key },
640
- });
641
- try {
642
- return await AgentHistoryReads.readBound({
614
+ return RepositoryStorage.track(repository, () => {
615
+ const history = RepositoryStorage.openRepositoryAgentHistory(repository, runtime.storageFactory, input);
616
+ return AgentHistoryReads.readBound({
643
617
  storage: history,
644
618
  entityId,
645
619
  scope: this.agentHistoryScope(context, input, entityId),
646
620
  }, { kind: "full" }, request);
647
- }
648
- finally {
649
- history.close();
650
- }
621
+ });
651
622
  },
652
623
  /**
653
- * Computes the opaque cursor scope for a registered Agent history read.
624
+ * Returns the opaque cursor scope for a registered Agent history read.
654
625
  */
655
626
  agentHistoryScope(context, input, entityId) {
656
627
  return {
@@ -759,6 +730,7 @@ export const repositoryAccess = Object.freeze({
759
730
  */
760
731
  bindRuntime(repository, runtime) {
761
732
  repositoryRuntimes.set(repository, Object.freeze(runtime));
733
+ repositoryStorageLifetimes.set(repository, RepositoryStorage.newLifetime());
762
734
  },
763
735
  /**
764
736
  * Removes runtime dispatch state and closes tracked storage handles.
@@ -768,12 +740,18 @@ export const repositoryAccess = Object.freeze({
768
740
  clearRuntime(repository) {
769
741
  repositoryRuntimes.delete(repository);
770
742
  repositoryDispatchGuards.delete(repository);
771
- const handles = repositoryEntityHandles.get(repository);
772
- if (handles !== undefined) {
773
- for (const handle of handles.values())
774
- handle.close();
775
- handles.clear();
776
- }
743
+ return RepositoryStorage.closeRuntime(repository);
744
+ },
745
+ /**
746
+ * Closes idle repository storage during synchronous assembly rollback.
747
+ *
748
+ * @param repository Repository to detach.
749
+ */
750
+ clearRuntimeIdle(repository) {
751
+ RepositoryStorage.assertIdle(repository);
752
+ repositoryRuntimes.delete(repository);
753
+ repositoryDispatchGuards.delete(repository);
754
+ RepositoryStorage.closeRuntimeIdle(repository);
777
755
  },
778
756
  });
779
757
  const inboxDedupMs = 30_000;
@@ -931,14 +909,12 @@ class RepositoryReadScope {
931
909
  throw new Error("Repository read access has finished.");
932
910
  if (query.schema !== this.#repository.stateSchema)
933
911
  throw new TypeError("Repository query must select the receiving Entity state schema.");
912
+ await this.#runtime.onAdmit(this.#context.tenantId);
934
913
  const plan = RepositoryReadPredicates.plan(query.buildPlan());
935
- const storage = RepositoryStorage.openReadEntityStorage(this.#runtime.storageFactory, RepositoryStorage.entityStorageInput(this.#repository, this.#context));
936
- try {
937
- return await storage.current.query(plan);
938
- }
939
- finally {
940
- storage.close();
941
- }
914
+ return RepositoryStorage.track(this.#repository, () => {
915
+ const storage = RepositoryStorage.repositoryReadEntityStorage(this.#repository, this.#runtime.storageFactory, RepositoryStorage.entityStorageInput(this.#repository, this.#context));
916
+ return storage.current.query(plan);
917
+ });
942
918
  }
943
919
  }
944
920
  /**
@@ -1046,7 +1022,20 @@ class AggregateExecutionSupport {
1046
1022
  ? {}
1047
1023
  : { tenantId: this.#storageContext.tenantId });
1048
1024
  const entity = this.#instantiateAggregate(entityId, current);
1049
- RepositoryHistoryInternals.bindEntityHistory(entity, storage, entityId, this.#repository.stateSchema);
1025
+ RepositoryHistoryInternals.bindEntityHistory(this.#repository, entity, storage, entityId, this.#repository.stateSchema, this.#storageContext.tenantId);
1026
+ return this.#loadedAggregate(entityId, current, entity, storage, storageInput);
1027
+ }
1028
+ /**
1029
+ * Returns one restored Aggregate with its captured storage configuration.
1030
+ *
1031
+ * @param entityId Aggregate identifier.
1032
+ * @param current Stored Aggregate snapshot, if present.
1033
+ * @param entity Restored Aggregate instance.
1034
+ * @param storage Retained Entity storage.
1035
+ * @param storageInput Captured storage configuration.
1036
+ * @returns Loaded Aggregate for command execution.
1037
+ */
1038
+ #loadedAggregate(entityId, current, entity, storage, storageInput) {
1050
1039
  return Object.freeze({
1051
1040
  commits: storage.commits,
1052
1041
  current: current === undefined
@@ -1135,9 +1124,7 @@ class AggregateExecutionSupport {
1135
1124
  entity: loaded.storageInput,
1136
1125
  entityId,
1137
1126
  next: record,
1138
- ...(RepositoryStorage.historyConfiguration(this.#repository).stateHistory
1139
- ? { states: [record] }
1140
- : {}),
1127
+ ...(loaded.storageInput.stateHistory ? { states: [record] } : {}),
1141
1128
  diagnostics: diagnostics.map((event) => clone(EventSchema, event)),
1142
1129
  events,
1143
1130
  });
@@ -1273,7 +1260,7 @@ class AggregateCommandExecution {
1273
1260
  this.#publishCommandDispatch(intake.route.entityId);
1274
1261
  let produced;
1275
1262
  try {
1276
- produced = await this.#invokeAssignee(loaded.entity, intake.assignee.handler, intake.message, EntityInvocation.commandHandlerContext(this.#command));
1263
+ produced = await this.#invokeAssignee(loaded.entity, intake.assignee.handler, intake.message, EntityInvocation.commandHandlerContext(this.#command), this.#command.context?.actorContext);
1277
1264
  }
1278
1265
  catch (error) {
1279
1266
  return this.#postAssigneeRejection(intake, error);
@@ -1330,26 +1317,45 @@ class AggregateCommandExecution {
1330
1317
  * @param handler Assignee declaration to invoke.
1331
1318
  * @param message Decoded Command message.
1332
1319
  * @param context Copied Command context for the handler.
1320
+ * @param actorContext Actor metadata of the Command signal.
1333
1321
  * @returns The handler's raw result after a successful commit.
1334
1322
  */
1335
- async #invokeAssignee(entity, handler, message, context) {
1336
- transactionalEntityAccess.start(entity);
1323
+ async #invokeAssignee(entity, handler, message, context, actorContext) {
1324
+ const releaseActor = entityHistoryAccess.bindActor(entity, actorContext);
1325
+ let started = false;
1337
1326
  try {
1338
- const produced = await EntityInvocation.invokeEntityMethod(entity, handler.methodName, message, handler.parameterCount, context);
1339
- const signals = this.#support.normalizeProducedSignals(produced);
1340
- if (signals.length === 0)
1341
- throw new Error("Repository aggregate command handlers must return at least one event.");
1342
- RepositoryHandlers.requireDeclaredOutputs(handler, signals);
1343
- const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, true));
1344
- if (commit.status === "rejected") {
1345
- throw new TransitionValidationError(commit.validation.error);
1346
- }
1347
- return produced;
1327
+ transactionalEntityAccess.start(entity);
1328
+ started = true;
1329
+ return await this.#invokeAndCommitAssignee(entity, handler, message, context);
1348
1330
  }
1349
1331
  catch (error) {
1350
- transactionalEntityAccess.rollback(entity);
1332
+ if (started)
1333
+ transactionalEntityAccess.rollback(entity);
1351
1334
  throw error;
1352
1335
  }
1336
+ finally {
1337
+ releaseActor();
1338
+ }
1339
+ }
1340
+ /**
1341
+ * Checks a declared Aggregate result and commits its accepted transition.
1342
+ *
1343
+ * @param entity Current Aggregate draft.
1344
+ * @param handler Declared Command handler.
1345
+ * @param message Decoded domain Command.
1346
+ * @param context Detached signal context supplied to the handler.
1347
+ * @returns Raw handler result after commit.
1348
+ */
1349
+ async #invokeAndCommitAssignee(entity, handler, message, context) {
1350
+ const produced = await EntityInvocation.invokeEntityMethod(entity, handler.methodName, message, handler.parameterCount, context);
1351
+ const signals = this.#support.normalizeProducedSignals(produced);
1352
+ if (signals.length === 0)
1353
+ throw receptionObservation.recordPostHandlerFailure(new Error("Repository aggregate command handlers must return at least one event."));
1354
+ RepositoryHandlers.requireDeclaredOutputs(handler, signals);
1355
+ const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, true));
1356
+ if (commit.status === "rejected")
1357
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1358
+ return produced;
1353
1359
  }
1354
1360
  /**
1355
1361
  * Binds each produced result to the target and pre-dispatch version.
@@ -1394,7 +1400,7 @@ class AggregateCommandExecution {
1394
1400
  }
1395
1401
  return create(EventSchema, {
1396
1402
  id: metadata.id,
1397
- message: AnyMessages.pack(schema, message),
1403
+ message: RepositorySignals.packProduced(schema, message),
1398
1404
  context: metadata.context,
1399
1405
  });
1400
1406
  }
@@ -1524,16 +1530,23 @@ class AggregateEventExecution {
1524
1530
  async #invokeHandlers(entityId, loaded, intake) {
1525
1531
  const eventContext = EntityInvocation.eventHandlerContext(this.#event);
1526
1532
  const events = [];
1527
- transactionalEntityAccess.start(loaded.entity);
1533
+ const releaseActor = entityHistoryAccess.bindActor(loaded.entity, this.#runtime.signalMetadata.originFromEvent(this.#event).actorContext);
1534
+ let started = false;
1528
1535
  try {
1536
+ transactionalEntityAccess.start(loaded.entity);
1537
+ started = true;
1529
1538
  await this.#invokeReactors(entityId, loaded, intake, eventContext, events);
1530
1539
  await this.#commitEntity(loaded.entity, events.length > 0);
1531
1540
  return Object.freeze(events);
1532
1541
  }
1533
1542
  catch (error) {
1534
- transactionalEntityAccess.rollback(loaded.entity);
1543
+ if (started)
1544
+ transactionalEntityAccess.rollback(loaded.entity);
1535
1545
  throw error;
1536
1546
  }
1547
+ finally {
1548
+ releaseActor();
1549
+ }
1537
1550
  }
1538
1551
  /**
1539
1552
  * Invokes each reactor, checks its declared output, and collects bound Events.
@@ -1564,7 +1577,7 @@ class AggregateEventExecution {
1564
1577
  async #commitEntity(entity, producedEvents) {
1565
1578
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, producedEvents));
1566
1579
  if (commit.status === "rejected")
1567
- throw new TransitionValidationError(commit.validation.error);
1580
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1568
1581
  }
1569
1582
  /**
1570
1583
  * Binds reactor results using the pre-dispatch Aggregate version.
@@ -1597,7 +1610,7 @@ class AggregateEventExecution {
1597
1610
  });
1598
1611
  return create(EventSchema, {
1599
1612
  id: metadata.id,
1600
- message: AnyMessages.pack(schema, signal),
1613
+ message: RepositorySignals.packProduced(schema, signal),
1601
1614
  context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId, version),
1602
1615
  });
1603
1616
  }
@@ -1805,9 +1818,7 @@ class ProjectionEventExecution {
1805
1818
  entity: loaded.storageInput,
1806
1819
  entityId,
1807
1820
  next: record,
1808
- ...(RepositoryStorage.historyConfiguration(this.#repository).stateHistory
1809
- ? { states: [record] }
1810
- : {}),
1821
+ ...(loaded.storageInput.stateHistory ? { states: [record] } : {}),
1811
1822
  });
1812
1823
  }
1813
1824
  /**
@@ -1839,20 +1850,26 @@ class ProjectionEventExecution {
1839
1850
  * @returns Completion after the subscriber transaction commits.
1840
1851
  */
1841
1852
  async #invokeSubscribers(entity, subscribers, packedMessage) {
1842
- transactionalEntityAccess.start(entity);
1853
+ const releaseActor = entityHistoryAccess.bindActor(entity, this.#runtime.signalMetadata.originFromEvent(this.#event).actorContext);
1854
+ let started = false;
1843
1855
  try {
1856
+ transactionalEntityAccess.start(entity);
1857
+ started = true;
1844
1858
  for (const subscriber of subscribers) {
1845
1859
  await this.#invokeSubscriber(entity, subscriber, packedMessage);
1846
1860
  }
1847
1861
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
1848
- if (commit.status === "rejected") {
1849
- throw new TransitionValidationError(commit.validation.error);
1850
- }
1862
+ if (commit.status === "rejected")
1863
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1851
1864
  }
1852
1865
  catch (error) {
1853
- transactionalEntityAccess.rollback(entity);
1866
+ if (started)
1867
+ transactionalEntityAccess.rollback(entity);
1854
1868
  throw error;
1855
1869
  }
1870
+ finally {
1871
+ releaseActor();
1872
+ }
1856
1873
  }
1857
1874
  /**
1858
1875
  * Invokes one Event subscriber and checks its raw return value.
@@ -1867,7 +1884,7 @@ class ProjectionEventExecution {
1867
1884
  const context = EntityInvocation.eventHandlerContext(this.#event);
1868
1885
  const result = await EntityInvocation.invokeEntityMethod(entity, subscriber.handler.methodName, message, subscriber.handler.parameterCount, context);
1869
1886
  if (result !== undefined)
1870
- throw new Error(`Projection subscriber "${subscriber.handler.methodName}" must not return signals.`);
1887
+ throw receptionObservation.recordPostHandlerFailure(new Error(`Projection subscriber "${subscriber.handler.methodName}" must not return signals.`));
1871
1888
  }
1872
1889
  /**
1873
1890
  * Invokes state subscribers in one Entity transaction.
@@ -1878,22 +1895,40 @@ class ProjectionEventExecution {
1878
1895
  * @returns Completion after the subscriber transaction commits.
1879
1896
  */
1880
1897
  async #invokeStateSubscribers(entity, subscribers, state) {
1881
- transactionalEntityAccess.start(entity);
1898
+ const releaseActor = entityHistoryAccess.bindActor(entity, this.#runtime.signalMetadata.originFromEvent(this.#event).actorContext);
1899
+ let started = false;
1882
1900
  try {
1883
- for (const subscriber of subscribers) {
1884
- const context = EntityInvocation.eventHandlerContext(this.#event);
1885
- const result = await EntityInvocation.invokeEntityMethod(entity, subscriber.handler.methodName, state, subscriber.handler.parameterCount, context);
1886
- if (result !== undefined)
1887
- throw new Error(`Projection subscriber "${subscriber.handler.methodName}" must not return signals.`);
1888
- }
1901
+ transactionalEntityAccess.start(entity);
1902
+ started = true;
1903
+ await this.#runStateSubscribers(entity, subscribers, state);
1889
1904
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
1890
1905
  if (commit.status === "rejected")
1891
- throw new TransitionValidationError(commit.validation.error);
1906
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1892
1907
  }
1893
1908
  catch (error) {
1894
- transactionalEntityAccess.rollback(entity);
1909
+ if (started)
1910
+ transactionalEntityAccess.rollback(entity);
1895
1911
  throw error;
1896
1912
  }
1913
+ finally {
1914
+ releaseActor();
1915
+ }
1916
+ }
1917
+ /**
1918
+ * Delivers one state update to each declared Projection subscriber.
1919
+ *
1920
+ * @param entity Projection currently in its transaction.
1921
+ * @param subscribers Subscribers selected for the source state.
1922
+ * @param state Decoded source Entity state.
1923
+ * @returns Completion after all subscribers accept the state.
1924
+ */
1925
+ async #runStateSubscribers(entity, subscribers, state) {
1926
+ for (const subscriber of subscribers) {
1927
+ const context = EntityInvocation.eventHandlerContext(this.#event);
1928
+ const result = await EntityInvocation.invokeEntityMethod(entity, subscriber.handler.methodName, state, subscriber.handler.parameterCount, context);
1929
+ if (result !== undefined)
1930
+ throw receptionObservation.recordPostHandlerFailure(new Error(`Projection subscriber "${subscriber.handler.methodName}" must not return signals.`));
1931
+ }
1897
1932
  }
1898
1933
  /**
1899
1934
  * Restores or creates a Projection and binds its storage history.
@@ -1972,7 +2007,11 @@ const ProcessManagerQueries = Object.freeze({
1972
2007
  * @returns Detached Projection state snapshots.
1973
2008
  */
1974
2009
  async load(stand, registeredSchema, plan, destination, query, schema) {
1975
- const results = await QueryReader.read(stand, registeredSchema, plan, destination, 10_000, query);
2010
+ const results = await QueryReader.read(stand, registeredSchema, plan, destination, 10_000, query, {
2011
+ operation: { kind: "query", query },
2012
+ ...(query.context === undefined ? {} : { actorContext: query.context }),
2013
+ ...(destination === undefined ? {} : { effectiveTenant: destination }),
2014
+ });
1976
2015
  return Object.freeze(results.map((result) => clone(schema, result.state)));
1977
2016
  },
1978
2017
  });
@@ -2034,13 +2073,7 @@ class ProcessManagerExecutionSupport {
2034
2073
  const priorNumber = loaded.current?.version?.number ?? 0;
2035
2074
  if (version.number === priorNumber) {
2036
2075
  await this.#appendUnchangedDiagnostics(loaded, diagnostics);
2037
- const store = new EventStore(this.#runtime.context, this.#runtime.storageFactory);
2038
- try {
2039
- await store.appendAll(producedEvents);
2040
- }
2041
- finally {
2042
- store.close();
2043
- }
2076
+ await RepositoryStorage.eventStore(this.#repository, this.#runtime).appendAll(producedEvents);
2044
2077
  await this.#appendAgentEvents(loaded, producedEvents);
2045
2078
  return true;
2046
2079
  }
@@ -2127,7 +2160,7 @@ class ProcessManagerExecutionSupport {
2127
2160
  entity: loaded.storageInput,
2128
2161
  entityId,
2129
2162
  next: record,
2130
- ...(history.stateHistory ? { states: [record] } : {}),
2163
+ ...(loaded.storageInput.stateHistory ? { states: [record] } : {}),
2131
2164
  ...(history.processManagerEventHistory
2132
2165
  ? { diagnostics: diagnostics.map((event) => clone(EventSchema, event)) }
2133
2166
  : {}),
@@ -2150,1268 +2183,706 @@ class ProcessManagerExecutionSupport {
2150
2183
  }
2151
2184
  }
2152
2185
  /**
2153
- * Advances admitted Agent work only under a provider claim and exact record fence.
2186
+ * Binds normal outgoing Agent signals from one live turn.
2154
2187
  */
2155
- const AgentExecutionRunner = {
2188
+ const AgentLiveOutputs = {
2156
2189
  /**
2157
- * Processes one accepted Agent transition under its provider claim.
2190
+ * Stores only committed domain Events in the Agent's emitted history.
2158
2191
  *
2159
- * @param repository Registered Agent repository.
2160
- * @param tenantId Tenant selected for this invocation.
2161
- * @param key Exact accepted invocation key.
2162
- * @param signal Cancellation signal for the current accepted execution.
2163
- * @returns When the fenced repository operation completes.
2192
+ * @param outputs Prepared outgoing domain signals.
2193
+ * @returns Committed domain Event rows for Agent history.
2164
2194
  */
2165
- async run(repository, tenantId, key, signal) {
2166
- if (signal?.aborted)
2167
- return;
2168
- const runtime = repositoryRuntimes.get(repository);
2169
- if (runtime?.ai === undefined || repository.entityFamily !== "agent")
2170
- throw new Error("Agent execution requires a bound Agent repository and AI registry.");
2171
- const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
2172
- const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
2173
- const executions = storage.agentExecution;
2174
- if (executions === undefined)
2175
- throw new Error("Agent execution storage is required.");
2176
- const token = randomUUID();
2177
- const claimed = await executions.claim(key, token, addMillis(Time.currentTime(), 30_000));
2178
- if (claimed === undefined)
2179
- return;
2180
- const session = new AgentExecutionSession(executions, claimed.record, token, claimed.preferences);
2181
- await this.runClaimed(repository, runtime, tenantId, session, signal);
2195
+ emittedHistory(outputs) {
2196
+ return outputs.flatMap((output) => {
2197
+ if (output.case !== "event" || output.value.context?.rejection !== undefined)
2198
+ return [];
2199
+ const event = output.value;
2200
+ const occurredAt = event.context?.timestamp;
2201
+ if (occurredAt === undefined)
2202
+ throw new Error("Agent emitted Event requires its original timestamp.");
2203
+ return [
2204
+ create(AgentHistoryEntrySchema, {
2205
+ occurredAt,
2206
+ item: { case: "domainEvent", value: event },
2207
+ }),
2208
+ ];
2209
+ });
2182
2210
  },
2183
2211
  /**
2184
- * Updates and releases one exact claim while its saved work advances.
2212
+ * Binds one declared result using its generated Event or Command role.
2185
2213
  *
2186
2214
  * @param repository Registered Entity repository for this operation.
2187
2215
  * @param runtime Bounded Context storage and publication services.
2188
- * @param tenantId Tenant selected for this accepted signal.
2189
- * @param session Fenced Agent execution session.
2190
- * @param signal Cancellation signal for the current accepted execution.
2191
- * @returns When the fenced repository operation completes.
2216
+ * @param routing Registered generated handler routing metadata.
2217
+ * @param source Original accepted source signal.
2218
+ * @param entityId Typed recipient Entity identifier.
2219
+ * @param version Entity version associated with the signal.
2220
+ * @param signal Original domain signal or execution cancellation signal.
2221
+ * @returns Typed outgoing signal with its original envelope.
2192
2222
  */
2193
- async runClaimed(repository, runtime, tenantId, session, signal) {
2194
- const abort = () => {
2195
- session.stop();
2196
- };
2197
- signal?.addEventListener("abort", abort, { once: true });
2198
- if (signal?.aborted)
2199
- abort();
2200
- const renewal = setInterval(() => {
2201
- // spine-log-boundary: server.agent_lease_renewal
2202
- void session.renew(addMillis(Time.currentTime(), 30_000)).catch(() => undefined);
2203
- }, 10_000);
2204
- try {
2205
- if (session.signal.aborted)
2206
- return;
2207
- await this.advanceClaimed(repository, runtime, tenantId, session);
2208
- }
2209
- catch (error) {
2210
- if (signal?.aborted)
2211
- return;
2212
- await this.handleRunFailure(repository, runtime, tenantId, session, error);
2213
- }
2214
- finally {
2215
- clearInterval(renewal);
2216
- signal?.removeEventListener("abort", abort);
2217
- session.stop();
2218
- }
2223
+ bindOutput(repository, runtime, routing, source, entityId, version, signal) {
2224
+ const typeName = EntityInvocation.messageTypeName(signal);
2225
+ const eventSchema = routing.producedEventSchemas.find((entry) => entry.typeName === typeName);
2226
+ if (eventSchema !== undefined)
2227
+ return this.bindEventOutput(repository, runtime, source, entityId, version, signal, eventSchema);
2228
+ const schema = routing.producedCommandSchemas.find((entry) => entry.typeName === typeName);
2229
+ if (schema === undefined)
2230
+ throw new Error(`Agent cannot emit undeclared signal "${typeName}".`);
2231
+ return this.bindCommandOutput(runtime, source, signal, schema);
2219
2232
  },
2220
2233
  /**
2221
- * Handles transient failures while giving expired work a final audit.
2234
+ * Creates the original domain Event envelope and producer context.
2222
2235
  *
2223
2236
  * @param repository Registered Entity repository for this operation.
2224
2237
  * @param runtime Bounded Context storage and publication services.
2225
- * @param tenantId Tenant selected for this accepted signal.
2226
- * @param session Fenced Agent execution session.
2227
- * @param error Failure raised while processing this accepted signal.
2228
- * @returns When the fenced repository operation completes.
2238
+ * @param source Original accepted source signal.
2239
+ * @param entityId Typed recipient Entity identifier.
2240
+ * @param version Entity version associated with the signal.
2241
+ * @param signal Original domain signal or execution cancellation signal.
2242
+ * @param schema Generated message descriptor for the signal.
2243
+ * @returns Typed original Event envelope for one normal EventBus handoff.
2229
2244
  */
2230
- async handleRunFailure(repository, runtime, tenantId, session, error) {
2231
- if (session.record().status ===
2232
- AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_COMMANDS ||
2233
- session.record().status === AgentInvocationStatus.AGENT_INVOCATION_COMPLETED)
2234
- throw error;
2235
- if (error instanceof AgentUncertainAttemptError) {
2236
- const reason = this.hasUnresolvedToolWrite(session.record())
2237
- ? "TOOL_OUTCOME_UNKNOWN"
2238
- : "MODEL_OUTCOME_UNKNOWN";
2239
- return this.terminate(repository, runtime, tenantId, session, reason);
2240
- }
2241
- if (error instanceof AgentExecutionFault) {
2242
- if (session.signal.aborted)
2243
- throw error;
2244
- return this.terminate(repository, runtime, tenantId, session, error.reason);
2245
- }
2246
- if (this.deadlineExpired(session.record()))
2247
- return this.terminate(repository, runtime, tenantId, session, "DEADLINE_EXCEEDED");
2248
- throw error;
2245
+ bindEventOutput(repository, runtime, source, entityId, version, signal, schema) {
2246
+ const metadata = source.case === "command"
2247
+ ? runtime.signalMetadata.eventFromCommand(source.value, { version: version.number })
2248
+ : runtime.signalMetadata.eventFromEvent(source.value, { version: version.number });
2249
+ const event = create(EventSchema, {
2250
+ id: metadata.id,
2251
+ message: RepositorySignals.packValidated(schema, signal),
2252
+ context: RepositorySignals.eventContextWithProducer(metadata.context, repository, entityId, version),
2253
+ });
2254
+ return { case: "event", value: event };
2249
2255
  },
2250
2256
  /**
2251
- * Processes one claimed record according to its saved completion or request state.
2257
+ * Creates the original Command envelope from its accepted origin.
2252
2258
  *
2253
- * @param repository Registered Entity repository for this operation.
2254
2259
  * @param runtime Bounded Context storage and publication services.
2255
- * @param tenantId Tenant selected for this accepted signal.
2256
- * @param session Fenced Agent execution session.
2257
- * @returns When the fenced repository operation completes.
2258
- */
2259
- async advanceClaimed(repository, runtime, tenantId, session) {
2260
- if (this.hasUnresolvedPhysicalRequest(session.record()))
2261
- return this.terminate(repository, runtime, tenantId, session, this.hasUnresolvedToolWrite(session.record())
2262
- ? "TOOL_OUTCOME_UNKNOWN"
2263
- : "MODEL_OUTCOME_UNKNOWN");
2264
- if (session.record().status === AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_COMMANDS)
2265
- return this.deliverCommands(runtime, session);
2266
- if (this.deadlineExpired(session.record()))
2267
- return this.terminate(repository, runtime, tenantId, session, "DEADLINE_EXCEEDED");
2268
- return this.execute(repository, runtime, tenantId, session);
2269
- },
2270
- /**
2271
- * Tests the original saved deadline without granting recovery fresh time.
2272
- *
2273
- * @param record Durable Agent execution record.
2274
- * @returns Whether the checked condition holds.
2260
+ * @param source Original accepted source signal.
2261
+ * @param signal Original domain signal or execution cancellation signal.
2262
+ * @param schema Generated message descriptor for the signal.
2263
+ * @returns Typed original Command envelope for durable delivery.
2275
2264
  */
2276
- deadlineExpired(record) {
2277
- const deadline = record.started?.deadline;
2278
- return (deadline !== undefined &&
2279
- Time.currentTimeMillis() >=
2280
- Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000));
2265
+ bindCommandOutput(runtime, source, signal, schema) {
2266
+ const metadata = source.case === "command"
2267
+ ? runtime.signalMetadata.commandFromCommand(source.value)
2268
+ : runtime.signalMetadata.commandFromEvent(source.value);
2269
+ const command = create(CommandSchema, {
2270
+ id: metadata.id,
2271
+ message: RepositorySignals.packValidated(schema, signal),
2272
+ context: metadata.context,
2273
+ });
2274
+ return { case: "command", value: command };
2281
2275
  },
2276
+ };
2277
+ /**
2278
+ * Waits only for a current in-process handler invocation.
2279
+ */
2280
+ const AgentLiveHandler = {
2282
2281
  /**
2283
- * Checks for a dispatched model reservation whose response was never saved.
2282
+ * Awaits an application handler within its live deadline and current session.
2284
2283
  *
2285
- * @param record Durable Agent execution record.
2286
- * @returns Whether the checked condition holds.
2284
+ * @typeParam Value Handler result.
2285
+ * @param work Already-started generated handler invocation.
2286
+ * @param signal Cancellation when live authority ends.
2287
+ * @param deadlineEpochMs Live invocation deadline.
2288
+ * @returns Handler result only while its session and deadline remain valid.
2287
2289
  */
2288
- hasUnresolvedPhysicalRequest(record) {
2289
- const model = record.journal.some((entry) => {
2290
- if (entry.evidence.case !== "attempt")
2291
- return false;
2292
- const attempt = entry.evidence.value;
2293
- return (attempt.transportReserved &&
2294
- attempt.response.case === undefined &&
2295
- !this.hasSavedOperationResult(record, attempt.operation?.value));
2296
- });
2297
- return model || this.hasUnresolvedToolWrite(record);
2298
- },
2299
- /**
2300
- * Checks for a dispatched write with no recorded tool result.
2301
- *
2302
- * @param record Durable Agent execution record.
2303
- * @returns Whether the checked condition holds.
2304
- */
2305
- hasUnresolvedToolWrite(record) {
2306
- return record.journal.some((entry) => {
2307
- if (entry.evidence.case !== "tool")
2308
- return false;
2309
- const tool = entry.evidence.value;
2310
- return (tool.request?.effect === ToolEffect.WRITE &&
2311
- tool.response === undefined &&
2312
- (tool.dispatched || tool.outcomeUnknown) &&
2313
- !this.hasSavedOperationResult(record, tool.operation?.value));
2290
+ async awaitHandler(work, signal, deadlineEpochMs) {
2291
+ let timer;
2292
+ let abort = () => undefined;
2293
+ const cutoff = new Promise((_resolve, reject) => {
2294
+ abort = () => {
2295
+ reject(new Error("Agent handler session ended."));
2296
+ };
2297
+ const tick = () => {
2298
+ if (deadlineEpochMs === undefined)
2299
+ return;
2300
+ const remaining = deadlineEpochMs - Time.currentTimeMillis();
2301
+ if (remaining <= 0)
2302
+ reject(new Error("Agent handler deadline expired."));
2303
+ else
2304
+ timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
2305
+ };
2306
+ signal.addEventListener("abort", abort, { once: true });
2307
+ if (signal.aborted)
2308
+ abort();
2309
+ else
2310
+ tick();
2314
2311
  });
2312
+ try {
2313
+ const result = await Promise.race([work, cutoff]);
2314
+ if (signal.aborted ||
2315
+ (deadlineEpochMs !== undefined && Time.currentTimeMillis() >= deadlineEpochMs))
2316
+ throw new Error("Agent handler deadline or session ended.");
2317
+ return result;
2318
+ }
2319
+ finally {
2320
+ clearTimeout(timer);
2321
+ signal.removeEventListener("abort", abort);
2322
+ }
2315
2323
  },
2316
- /**
2317
- * Checks for a settled named result even when physical evidence is incomplete.
2318
- *
2319
- * @param record Durable Agent execution record.
2320
- * @param id Saved signal, operation, or Entity identifier.
2321
- * @returns Whether the checked condition holds.
2322
- */
2323
- hasSavedOperationResult(record, id) {
2324
- return record.journal.some((entry) => entry.evidence.case === "operation" &&
2325
- entry.evidence.value.operation?.value === id &&
2326
- entry.evidence.value.result.case !== undefined);
2327
- },
2328
- /**
2329
- * Records termination of an unreplayable physical request under the provider fence.
2330
- *
2331
- * @param repository Registered Entity repository for this operation.
2332
- * @param runtime Bounded Context storage and publication services.
2333
- * @param tenantId Tenant selected for this accepted signal.
2334
- * @param session Fenced Agent execution session.
2335
- * @param reason Safe terminal reason retained in Agent audit.
2336
- * @returns When the fenced repository operation completes.
2337
- */
2338
- async terminate(repository, runtime, tenantId, session, reason) {
2339
- const record = session.record();
2340
- const event = this.terminationEvent(repository, runtime, tenantId, record, reason);
2341
- const occurredAt = event.context?.timestamp;
2342
- if (occurredAt === undefined)
2343
- throw new Error("Agent termination Event requires Time.");
2344
- runtime.registerSystemEventSchema(AgentInvocationTerminatedSchema);
2345
- await session.update((next) => {
2346
- next.status = AgentInvocationStatus.AGENT_INVOCATION_TERMINATED;
2347
- next.terminalFailure = create(AiDiagnosticIdSchema, { value: randomUUID() });
2348
- return next;
2349
- }, [
2350
- create(AgentHistoryEntrySchema, {
2351
- occurredAt,
2352
- item: { case: "systemEvent", value: event },
2353
- }),
2354
- ]);
2355
- await runtime.publishAgentSystemEvent(event);
2356
- },
2357
- /**
2358
- * Builds one original termination envelope for a saved terminal reason.
2359
- *
2360
- * @param repository Registered Entity repository for this operation.
2361
- * @param runtime Bounded Context storage and publication services.
2362
- * @param tenantId Tenant selected for this accepted signal.
2363
- * @param record Durable Agent execution record.
2364
- * @param reason Safe terminal reason retained in Agent audit.
2365
- * @returns Original AgentInvocationTerminated System Event envelope.
2366
- */
2367
- terminationEvent(repository, runtime, tenantId, record, reason) {
2368
- const accepted = record.accepted;
2369
- if (accepted?.recipientId === undefined)
2370
- throw new Error("Agent termination requires the original typed recipient.");
2371
- const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2372
- const unresolvedToolCalls = this.unresolvedToolCalls(record);
2373
- const payload = create(AgentInvocationTerminatedSchema, {
2374
- agent: scope.agent,
2375
- sourceSignal: scope.source,
2376
- reason,
2377
- unresolvedAttempts: this.unresolvedAttempts(record),
2378
- unresolvedToolCalls,
2379
- terminatedAt: Time.currentTime(),
2324
+ };
2325
+ /**
2326
+ * Runs one receipt-admitted Agent signal without an execution record.
2327
+ */
2328
+ class AgentLiveRunner {
2329
+ repository;
2330
+ runtime;
2331
+ tenantId;
2332
+ entityId;
2333
+ invocation;
2334
+ handlers;
2335
+ signal;
2336
+ checkSession;
2337
+ #deadlineEpochMs;
2338
+ #deadlineSampled = false;
2339
+ /**
2340
+ * Binds one original signal, resolved handlers, and the current repository runtime.
2341
+ *
2342
+ * @param checkSession Callback that validates the current execution session.
2343
+ * @param entityId Typed Entity identifier.
2344
+ * @param handlers Registered generated handlers.
2345
+ * @param invocation Captured live Agent invocation.
2346
+ * @param repository Repository configured for this Entity.
2347
+ * @param runtime Current bounded-context runtime.
2348
+ * @param signal Signal or cancellation signal for this operation.
2349
+ * @param tenantId Captured tenant identifier.
2350
+ */
2351
+ constructor(repository, runtime, tenantId, entityId, invocation, handlers, signal, checkSession) {
2352
+ this.repository = repository;
2353
+ this.runtime = runtime;
2354
+ this.tenantId = tenantId;
2355
+ this.entityId = entityId;
2356
+ this.invocation = invocation;
2357
+ this.handlers = handlers;
2358
+ this.signal = signal;
2359
+ this.checkSession = checkSession;
2360
+ }
2361
+ /**
2362
+ * Starts the serialized turn by loading current state and committed model preferences.
2363
+ *
2364
+ * @returns Completion after admitted Agent work finishes.
2365
+ */
2366
+ async run() {
2367
+ if (this.signal.aborted)
2368
+ return;
2369
+ await this.checkSession();
2370
+ await this.runtime.onAdmit(this.tenantId);
2371
+ await RepositoryStorage.track(this.repository, async () => {
2372
+ const loaded = await new ProcessManagerExecutionSupport(this.repository, this.runtime).load(this.entityId, this.tenantId === undefined ? {} : { tenantId: this.tenantId });
2373
+ const expected = await loaded.commits.readAgentPreferences(this.entityId);
2374
+ if (this.signal.aborted)
2375
+ return;
2376
+ const ai = this.runtime.ai;
2377
+ if (ai === undefined)
2378
+ throw new Error("Live Agent execution requires an AI registry.");
2379
+ const preferences = expected?.map((entry) => clone(ModelPreferenceSchema, entry)) ?? [];
2380
+ const audit = this.audit(loaded);
2381
+ let selected;
2382
+ try {
2383
+ selected = await this.select(preferences);
2384
+ }
2385
+ catch (error) {
2386
+ if (!(error instanceof AgentLiveFault) || error.reason !== "MODEL_USE_DENIED")
2387
+ throw error;
2388
+ await this.recordModelDenial(audit, error);
2389
+ throw error;
2390
+ }
2391
+ const budget = new AgentLiveBudget(this.limits(ai));
2392
+ await this.dispatchStart(loaded);
2393
+ const produced = await this.produce(loaded, selected, preferences, audit, budget);
2394
+ this.signal.throwIfAborted();
2395
+ await this.checkSession();
2396
+ await this.complete(loaded, expected, preferences, produced);
2380
2397
  });
2381
- return AgentInteractionEvents.envelope(accepted, runtime.signalMetadata, accepted.recipientId, AgentInvocationTerminatedSchema, payload);
2382
- },
2398
+ }
2383
2399
  /**
2384
- * Returns IDs only for physically reserved model requests without saved outcomes.
2400
+ * Writes only definite pre-handler model-use denial with its original source.
2385
2401
  *
2386
- * @param record Durable execution journal.
2387
- * @returns Original attempt IDs for unresolved physical sends.
2402
+ * @param audit Retained history audit for this invocation.
2403
+ * @param denial Typed model-use denial to record.
2404
+ * @returns Completion after the exact pre-handler denial is saved to Agent history.
2388
2405
  */
2389
- unresolvedAttempts(record) {
2390
- return record.journal.flatMap((entry) => {
2391
- if (entry.evidence.case !== "attempt")
2392
- return [];
2393
- const attempt = entry.evidence.value;
2394
- return attempt.transportReserved &&
2395
- attempt.response.case === undefined &&
2396
- !this.hasSavedOperationResult(record, attempt.operation?.value) &&
2397
- attempt.attempt !== undefined
2398
- ? [attempt.attempt]
2399
- : [];
2406
+ async recordModelDenial(audit, denial) {
2407
+ const agent = create(MessageIdSchema, {
2408
+ id: this.invocation.recipientId,
2409
+ typeUrl: TypeUrls.derive(this.repository.stateSchema),
2400
2410
  });
2401
- },
2402
- /**
2403
- * Returns exact IDs for dispatched write effects without saved results.
2404
- *
2405
- * @param record Durable Agent execution record.
2406
- * @returns Original ToolCall IDs for unresolved dispatched writes.
2407
- */
2408
- unresolvedToolCalls(record) {
2409
- return record.journal.flatMap((entry) => {
2410
- if (entry.evidence.case !== "tool")
2411
- return [];
2412
- const tool = entry.evidence.value;
2413
- return tool.request?.effect === ToolEffect.WRITE &&
2414
- tool.response === undefined &&
2415
- (tool.dispatched || tool.outcomeUnknown) &&
2416
- tool.request.call !== undefined
2417
- ? [tool.request.call]
2418
- : [];
2411
+ const fact = create(AgentInvocationTerminatedSchema, {
2412
+ agent,
2413
+ sourceSignal: AgentLiveInteractionEvents.sourceRef(this.invocation),
2414
+ reason: "MODEL_USE_DENIED",
2415
+ terminatedAt: Time.currentTime(),
2419
2416
  });
2420
- },
2421
- /**
2422
- * Persists start facts, runs selected handlers, and conditionally completes a no-op.
2423
- *
2424
- * @param repository Agent registration.
2425
- * @param runtime Bound services.
2426
- * @param tenantId Complete tenant, when applicable.
2427
- * @param session Fenced Agent execution session.
2428
- * @returns When the fenced repository operation completes.
2429
- */
2430
- async execute(repository, runtime, tenantId, session) {
2431
- const accepted = session.record().accepted;
2432
- if (accepted?.key === undefined || accepted.recipientId === undefined)
2433
- throw new Error("Claimed Agent invocation lacks accepted source and recipient.");
2434
- const entityId = InboxMessages.targetEntityId(accepted.recipientId, repository.idField);
2435
- const support = new ProcessManagerExecutionSupport(repository, runtime);
2436
- const loaded = await support.load(entityId, tenantId === undefined ? {} : { tenantId });
2437
- const handlers = this.handlers(repository, accepted);
2438
- const configuration = repositoryAccess.agentConfiguration(repository);
2439
- if (runtime.ai === undefined)
2440
- throw new Error("Agent execution requires a bound AI registry.");
2441
- this.assertRevisions(repository, accepted, handlers, runtime.ai, configuration);
2442
- const record = session.record().started === undefined
2443
- ? await this.start(repository, runtime, session, loaded, entityId, tenantId)
2444
- : await this.selectMissingModels(repository, runtime, session, accepted, tenantId);
2445
- if (session.signal.aborted)
2446
- return;
2447
- const outputs = await this.completeExecution(repository, runtime, tenantId, session, loaded, accepted, handlers, entityId, record.started?.initialVersion ?? create(VersionSchema));
2448
- await this.deliverInitial(runtime, session, outputs);
2449
- },
2450
- /**
2451
- * Creates or rejects the draft before one conditional Entity completion.
2452
- *
2453
- * @param repository Registered Entity repository for this operation.
2454
- * @param runtime Bounded Context storage and publication services.
2455
- * @param tenantId Tenant selected for this accepted signal.
2456
- * @param session Fenced Agent execution session.
2457
- * @param loaded Loaded Entity and its repository storage state.
2458
- * @param accepted Durable original signal and saved handler bindings.
2459
- * @param handlers Generated handlers in their saved order.
2460
- * @param entityId Typed recipient Entity identifier.
2461
- * @param initialVersion Entity version before selected handlers run.
2462
- * @returns Record after conditional Entity and execution completion.
2463
- */
2464
- async completeExecution(repository, runtime, tenantId, session, loaded, accepted, handlers, entityId, initialVersion) {
2465
- const outputs = await this.outputsOrRejection(repository, runtime, tenantId, session, loaded.entity, accepted, handlers, entityId, initialVersion);
2466
- await this.complete(repository, runtime, tenantId, session, loaded, entityId, outputs);
2467
- return outputs;
2468
- },
2417
+ await audit.saveDenial(fact, denial);
2418
+ }
2469
2419
  /**
2470
- * Rejects a Command draft and persists only its declared rejection.
2420
+ * Converts only a declared Command rejection into a normal outgoing Event.
2471
2421
  *
2472
- * @param repository Registered Entity repository for this operation.
2473
- * @param runtime Bounded Context storage and publication services.
2474
- * @param tenantId Tenant selected for this accepted signal.
2475
- * @param session Fenced Agent execution session.
2476
- * @param entity Loaded recipient Entity instance.
2477
- * @param accepted Durable original signal and saved handler bindings.
2478
- * @param handlers Generated handlers in their saved order.
2479
- * @param entityId Typed recipient Entity identifier.
2480
- * @param initialVersion Entity version before selected handlers run.
2481
- * @returns Prepared outgoing signals, or only the declared rejection.
2422
+ * @param audit Retained history audit for this invocation.
2423
+ * @param budget Invocation-wide resource budget.
2424
+ * @param loaded Current Entity and retained storage for this turn.
2425
+ * @param preferences Current or staged model preferences.
2426
+ * @param selected Models or selection retained for this invocation.
2427
+ * @returns The declared rejection Event, when a Command rejects.
2482
2428
  */
2483
- async outputsOrRejection(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion) {
2429
+ async produce(loaded, selected, preferences, audit, budget) {
2484
2430
  try {
2485
- return await this.produceOutputs(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion);
2431
+ return {
2432
+ case: "produced",
2433
+ values: await this.invoke(loaded, selected, preferences, audit, budget),
2434
+ };
2486
2435
  }
2487
2436
  catch (error) {
2488
- if (!RejectionThrowable.is(error) || accepted.signal.case !== "command")
2437
+ if (!RejectionThrowable.is(error) || this.invocation.source.case !== "command")
2489
2438
  throw error;
2490
- const handler = handlers[0];
2439
+ const handler = this.handlers[0];
2491
2440
  if (handler === undefined)
2492
2441
  throw new Error("Agent rejection requires a Command assignee.");
2493
2442
  RepositorySignals.requireDeclaredRejection(handler.handler, error);
2494
- session.discardPreferenceChanges();
2495
- return [this.rejectionOutput(runtime, repository, accepted.signal.value, entityId, error)];
2443
+ preferences.length = 0;
2444
+ const output = RepositorySignals.rejectionEvent(this.runtime, this.repository, this.invocation.source.value, this.entityId, error);
2445
+ return { case: "rejected", output: { case: "event", value: output } };
2496
2446
  }
2497
- },
2498
- /**
2499
- * Wraps the original rejection Event for one normal EventBus handoff.
2500
- *
2501
- * @param runtime Bounded Context storage and publication services.
2502
- * @param repository Registered Entity repository for this operation.
2503
- * @param command Original typed Command envelope.
2504
- * @param entityId Typed recipient Entity identifier.
2505
- * @param error Failure raised while processing this accepted signal.
2506
- * @returns Prepared original rejection Event for one normal EventBus handoff.
2507
- */
2508
- rejectionOutput(runtime, repository, command, entityId, error) {
2509
- return {
2510
- case: "event",
2511
- value: RepositorySignals.rejectionEvent(runtime, repository, command, entityId, error),
2512
- };
2513
- },
2514
- /**
2515
- * Restores handler results and binds their declared original output envelopes.
2516
- *
2517
- * @param repository Registered Entity repository for this operation.
2518
- * @param runtime Bounded Context storage and publication services.
2519
- * @param tenantId Tenant selected for this accepted signal.
2520
- * @param session Fenced Agent execution session.
2521
- * @param entity Loaded recipient Entity instance.
2522
- * @param accepted Durable original signal and saved handler bindings.
2523
- * @param handlers Generated handlers in their saved order.
2524
- * @param entityId Typed recipient Entity identifier.
2525
- * @param initialVersion Entity version before selected handlers run.
2526
- * @returns Prepared outgoing signals from accepted handler results.
2527
- */
2528
- async produceOutputs(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion) {
2529
- const produced = await this.invoke(repository, runtime, tenantId, session, entity, accepted, handlers);
2530
- return this.bindOutputs(repository, runtime, accepted, entityId, initialVersion, produced);
2531
- },
2532
- /**
2533
- * Commits changed state and the fenced execution record as one provider mutation.
2534
- *
2535
- * @param repository Agent registration.
2536
- * @param runtime Bounded Context Stand and provider services.
2537
- * @param tenantId Complete delivery tenant.
2538
- * @param session Serialized provider claim and current record.
2539
- * @param loaded Entity after handler transaction commit.
2540
- * @param entityId Original typed recipient.
2541
- * @param outputs Original emitted Event or Command envelopes.
2542
- * @returns Complete record after the atomic provider mutation.
2543
- */
2544
- async complete(repository, runtime, tenantId, session, loaded, entityId, outputs) {
2545
- await this.authorizePreferenceChanges(repository, runtime, tenantId, session);
2546
- const version = RepositoryEntities.repositoryVersion(loaded.entity);
2547
- const preferences = session.preferences();
2548
- const emitted = this.emittedHistory(outputs);
2549
- const preferenceEvents = this.preferenceEvents(repository, runtime, tenantId, session);
2550
- const historyEntries = [...emitted, ...this.preferenceHistory(preferenceEvents)];
2551
- const makeNext = (record) => this.completionFacts(record, version, outputs, preferences);
2552
- const completed = await this.commitCompletion(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries);
2553
- for (const event of preferenceEvents)
2554
- await runtime.publishAgentSystemEvent(event);
2555
- return completed;
2556
- },
2447
+ }
2557
2448
  /**
2558
- * Checks effective staged model changes before any completion mutation.
2449
+ * Finds each enabled model from the preference image loaded at turn start.
2559
2450
  *
2560
- * @param repository Agent registration and model policy.
2561
- * @param runtime Bounded Context and AI registry.
2562
- * @param tenantId Accepted tenant when applicable.
2563
- * @param session Fenced Agent execution and staged preferences.
2564
- * @returns When every effective change is authorized.
2451
+ * @param preferences Current or staged model preferences.
2452
+ * @returns The selected model image for the current Agent turn.
2565
2453
  */
2566
- async authorizePreferenceChanges(repository, runtime, tenantId, session) {
2567
- const policy = repositoryAccess.agentConfiguration(repository).ai;
2568
- if (policy?.authorizeSelection === undefined)
2569
- return;
2570
- const accepted = session.record().accepted;
2571
- if (runtime.ai === undefined || accepted === undefined)
2572
- throw new Error("Agent model preference requires accepted start facts.");
2573
- const deadlineMs = this.savedModelDeadline(session, "preference");
2574
- const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2575
- for (const kind of [AiModelKind.GENERATION, AiModelKind.DECISION]) {
2576
- const previous = this.preferenceModel(session.initialPreferences(), kind);
2577
- const selected = this.preferenceModel(session.preferences(), kind);
2578
- if (previous === undefined && selected === undefined)
2579
- continue;
2580
- if (previous !== undefined &&
2581
- selected !== undefined &&
2582
- equals(ModelRefSchema, previous, selected))
2454
+ async select(preferences) {
2455
+ const ai = this.runtime.ai;
2456
+ const configuration = repositoryAccess.agentConfiguration(this.repository).ai;
2457
+ if (ai === undefined || configuration === undefined)
2458
+ throw new Error("Live Agent execution requires registered model policy.");
2459
+ const scope = AgentModelSelection.scopeLive(this.invocation, this.repository.stateSchema, this.runtime.signalMetadata, this.tenantId);
2460
+ const selected = new Map();
2461
+ for (const kind of ["generation", "decision"]) {
2462
+ if (!configuration.models.some((model) => model.definition.kind === kind))
2583
2463
  continue;
2584
- await AgentModelSelection.authorizeSelection(runtime.ai, policy, scope, selected, session.signal, deadlineMs);
2464
+ await this.checkSession();
2465
+ const value = await AgentModelSelection.selectLiveKind(ai, configuration, scope, preferences, this.signal, this.deadline(ai), kind, this.invocation.source.value.message);
2466
+ selected.set(value.kind, value);
2585
2467
  }
2586
- },
2587
- /**
2588
- * Reads an optional saved model deadline and detects incomplete persisted bounds.
2589
- *
2590
- * @param session Fenced Agent execution session.
2591
- * @param context Selection or preference authorization context.
2592
- * @returns Saved absolute deadline, when configured.
2593
- */
2594
- savedModelDeadline(session, context) {
2595
- const record = session.record();
2596
- const deadline = record.started?.deadline;
2597
- if (deadline === undefined && record.started?.bounds?.deadlineMillis !== undefined)
2598
- throw new Error(`Agent model ${context} deadline was not saved.`);
2599
- return deadline === undefined
2600
- ? undefined
2601
- : Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000);
2602
- },
2603
- /**
2604
- * Applies the same provider fence to preference events and Entity completion.
2605
- *
2606
- * @param repository Registered Entity repository for this operation.
2607
- * @param runtime Bounded Context storage and publication services.
2608
- * @param tenantId Tenant selected for this accepted signal.
2609
- * @param session Fenced Agent execution session.
2610
- * @param loaded Loaded Entity and its repository storage state.
2611
- * @param entityId Typed recipient Entity identifier.
2612
- * @param makeNext Callback constructing the next durable execution record.
2613
- * @param historyEntries Agent history rows included in the fenced mutation.
2614
- * @returns Saved execution record after conditional completion.
2615
- */
2616
- commitCompletion(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries) {
2617
- return RepositoryEntities.repositoryChanged(loaded.entity)
2618
- ? this.commitChanged(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries)
2619
- : session.complete(makeNext, historyEntries);
2620
- },
2621
- /**
2622
- * Wraps original preference Events as indexed Agent history entries.
2623
- *
2624
- * @param events Original Event envelopes retained for diagnostics.
2625
- * @returns Typed Agent history rows for saved preference changes.
2626
- */
2627
- preferenceHistory(events) {
2628
- return events.map((event) => {
2629
- const occurredAt = event.context?.timestamp;
2630
- if (occurredAt === undefined)
2631
- throw new Error("Agent preference Event requires Time.");
2632
- return create(AgentHistoryEntrySchema, {
2633
- occurredAt,
2634
- item: { case: "systemEvent", value: event },
2635
- });
2636
- });
2637
- },
2638
- /**
2639
- * Captures only effective preference changes committed by this signal.
2640
- *
2641
- * @param repository Registered Entity repository for this operation.
2642
- * @param runtime Bounded Context storage and publication services.
2643
- * @param tenantId Tenant selected for this accepted signal.
2644
- * @param session Fenced Agent execution session.
2645
- * @returns Original model-selection System Events.
2646
- */
2647
- preferenceEvents(repository, runtime, tenantId, session) {
2648
- const accepted = session.record().accepted;
2649
- if (accepted?.recipientId === undefined)
2650
- throw new Error("Agent preference requires accepted work.");
2651
- const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2652
- return [AiModelKind.GENERATION, AiModelKind.DECISION].flatMap((kind) => {
2653
- const previous = this.preferenceModel(session.initialPreferences(), kind);
2654
- const selected = this.preferenceModel(session.preferences(), kind);
2655
- if (previous === undefined && selected === undefined)
2656
- return [];
2657
- if (previous !== undefined &&
2658
- selected !== undefined &&
2659
- equals(ModelRefSchema, previous, selected))
2660
- return [];
2661
- return [this.preferenceEvent(accepted, scope, kind, previous, selected, runtime)];
2662
- });
2663
- },
2468
+ return selected;
2469
+ }
2664
2470
  /**
2665
- * Builds one effective selection change with its original causal context.
2471
+ * Captures the configured live invocation deadline.
2666
2472
  *
2667
- * @param accepted Durable original signal and saved handler bindings.
2668
- * @param scope Accepted actor, source, Bounded Context, and tenant.
2669
- * @param kind Generation or decision model kind.
2670
- * @param previous Previously selected model deployment.
2671
- * @param selected Newly selected model deployment.
2672
- * @param runtime Bounded Context storage and publication services.
2673
- * @returns Original model-selection System Event.
2473
+ * @param ai Configured AI capability.
2474
+ * @returns The sampled live invocation deadline, when configured.
2674
2475
  */
2675
- preferenceEvent(accepted, scope, kind, previous, selected, runtime) {
2676
- const recipientId = accepted.recipientId;
2677
- if (recipientId === undefined)
2678
- throw new Error("Agent preference requires a recipient.");
2679
- const payload = create(SelectionChangedSchema, {
2680
- agent: scope.agent,
2681
- sourceSignal: scope.source,
2682
- kind,
2683
- ...(previous === undefined ? {} : { previousModel: previous }),
2684
- ...(selected === undefined ? {} : { selectedModel: selected }),
2685
- changedAt: Time.currentTime(),
2686
- });
2687
- runtime.registerSystemEventSchema(SelectionChangedSchema);
2688
- return AgentInteractionEvents.envelope(accepted, runtime.signalMetadata, recipientId, SelectionChangedSchema, payload);
2689
- },
2476
+ deadline(ai) {
2477
+ if (this.#deadlineSampled)
2478
+ return this.#deadlineEpochMs;
2479
+ this.#deadlineSampled = true;
2480
+ const duration = this.limits(ai).deadlineMs;
2481
+ this.#deadlineEpochMs =
2482
+ duration === undefined ? undefined : Time.currentTimeMillis() + duration;
2483
+ return this.#deadlineEpochMs;
2484
+ }
2690
2485
  /**
2691
- * Reads an explicit model selection, including inherited defaults as absence.
2486
+ * Applies only the narrower per-repository bounds to the shared registry policy.
2692
2487
  *
2693
- * @param preferences Current Agent instance model preferences.
2694
- * @param kind Generation or decision model kind.
2695
- * @returns Selected deployment reference, or inherited default.
2488
+ * @param ai Configured AI capability.
2489
+ * @returns The current configured limits for the start audit.
2696
2490
  */
2697
- preferenceModel(preferences, kind) {
2698
- const selected = preferences.find((entry) => entry.kind === kind);
2699
- return selected?.selection.case === "model" ? selected.selection.value : undefined;
2700
- },
2701
- /**
2702
- * Builds the immutable completion image and emitted domain history.
2703
- *
2704
- * @param record Durable Agent execution record.
2705
- * @param version Entity version associated with the signal.
2706
- * @param outputs Prepared outgoing domain signals.
2707
- * @param preferences Current Agent instance model preferences.
2708
- * @returns Record completed with original outputs and resulting version.
2709
- */
2710
- completionFacts(record, version, outputs, preferences) {
2711
- const initialVersion = record.started?.initialVersion;
2712
- if (initialVersion === undefined)
2713
- throw new Error("Agent completion requires its started Version.");
2714
- const commands = outputs
2715
- .filter((output) => output.case === "command")
2716
- .map((output) => create(AgentOutgoingCommandSchema, { command: output.value }));
2717
- record.status =
2718
- commands.length === 0
2719
- ? AgentInvocationStatus.AGENT_INVOCATION_COMPLETED
2720
- : AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_COMMANDS;
2721
- record.completion = create(AgentExecutionCompletionSchema, {
2722
- initialVersion,
2723
- resultingVersion: version,
2724
- preferences: [...preferences],
2725
- commands,
2726
- });
2727
- return record;
2728
- },
2491
+ limits(ai) {
2492
+ const global = registryOptions(ai).invocationLimits ?? {};
2493
+ const local = repositoryAccess.agentConfiguration(this.repository).ai?.invocationLimits ?? {};
2494
+ const limits = {};
2495
+ const keys = [
2496
+ "operations",
2497
+ "modelRequests",
2498
+ "toolCalls",
2499
+ "recordedReads",
2500
+ "deadlineMs",
2501
+ "totalInputBytes",
2502
+ "totalOutputBytes",
2503
+ ];
2504
+ for (const key of keys) {
2505
+ const first = global[key];
2506
+ const second = local[key];
2507
+ const value = first === undefined ? second : second === undefined ? first : Math.min(first, second);
2508
+ if (value !== undefined)
2509
+ limits[key] = value;
2510
+ }
2511
+ return limits;
2512
+ }
2729
2513
  /**
2730
- * Stores only committed domain Events in the Agent's emitted history.
2514
+ * Binds one original System audit writer to retained Agent history.
2731
2515
  *
2732
- * @param outputs Prepared outgoing domain signals.
2733
- * @returns Committed domain Event rows for Agent history.
2516
+ * @param loaded Current Entity and retained storage for this turn.
2517
+ * @returns The history writer bound to this Agent and source.
2734
2518
  */
2735
- emittedHistory(outputs) {
2736
- return outputs.flatMap((output) => {
2737
- if (output.case !== "event" || output.value.context?.rejection !== undefined)
2738
- return [];
2739
- const event = output.value;
2740
- const occurredAt = event.context?.timestamp;
2741
- if (occurredAt === undefined)
2742
- throw new Error("Agent emitted Event requires its original timestamp.");
2743
- return [
2744
- create(AgentHistoryEntrySchema, {
2745
- occurredAt,
2746
- item: { case: "domainEvent", value: event },
2747
- }),
2748
- ];
2519
+ audit(loaded) {
2520
+ const history = loaded.agentHistory;
2521
+ if (history === undefined)
2522
+ throw new Error("Live Agent execution requires history storage.");
2523
+ return new AgentLiveAudit(this.invocation, this.repository.stateSchema, this.entityId, loaded.storageInput.id.key(this.entityId), history, this.runtime.signalMetadata, this.runtime.registerSystemEventSchema, this.runtime.publishAgentSystemEvent, async () => {
2524
+ this.signal.throwIfAborted();
2525
+ await this.checkSession();
2526
+ this.signal.throwIfAborted();
2749
2527
  });
2750
- },
2528
+ }
2751
2529
  /**
2752
- * Commits changed Entity state with the already prepared execution image.
2530
+ * Writes the genuine dispatch System row before publishing its original Event.
2753
2531
  *
2754
- * @param repository Registered Entity repository for this operation.
2755
- * @param runtime Bounded Context storage and publication services.
2756
- * @param tenantId Tenant selected for this accepted signal.
2757
- * @param session Fenced Agent execution session.
2758
- * @param loaded Loaded Entity and its repository storage state.
2759
- * @param entityId Typed recipient Entity identifier.
2760
- * @param makeNext Callback constructing the next durable execution record.
2761
- * @param historyEntries Agent history rows included in the fenced mutation.
2762
- * @returns Saved execution record after the changed Entity commit.
2532
+ * @param loaded Current Entity and retained storage for this turn.
2533
+ * @returns Completion after the original dispatch System Event is saved.
2763
2534
  */
2764
- async commitChanged(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries) {
2765
- const version = RepositoryEntities.repositoryVersion(loaded.entity);
2766
- const state = RepositoryEntities.repositoryState(loaded.entity);
2767
- const lifecycle = RepositoryEntities.repositoryLifecycle(loaded.entity);
2768
- const deferred = await standAccess.deferUpdate(runtime.stand, repository.stateSchema, state, RepositoryStand.standUpdateOptions(tenantId, version, lifecycle), repository.metadata);
2769
- const prepared = RepositoryStand.preparedRecord(repository, deferred, entityId);
2770
- return this.commitDeferred(repository, session, loaded, entityId, makeNext, historyEntries, deferred, prepared);
2771
- },
2535
+ async dispatchStart(loaded) {
2536
+ await this.checkSession();
2537
+ const source = this.invocation.source;
2538
+ const event = source.case === "command"
2539
+ ? HandlerDispatchPublisher.commandEvent(this.runtime, this.repository, source.value, this.entityId)
2540
+ : HandlerDispatchPublisher.reactorEvent(this.runtime, this.repository, source.value, this.entityId);
2541
+ const occurredAt = event.context?.timestamp;
2542
+ if (occurredAt === undefined || loaded.agentHistory === undefined)
2543
+ throw new Error("Agent dispatch audit requires timestamp and history.");
2544
+ await loaded.agentHistory.appendAll(this.entityId, [
2545
+ create(AgentHistoryEntrySchema, { occurredAt, item: { case: "systemEvent", value: event } }),
2546
+ ]);
2547
+ await this.checkSession();
2548
+ if (source.case === "command")
2549
+ HandlerDispatchPublisher.publishCommand(this.runtime, event);
2550
+ else
2551
+ HandlerDispatchPublisher.publishReactor(this.runtime, event);
2552
+ }
2772
2553
  /**
2773
- * Applies the deferred Stand image only after fenced provider completion.
2554
+ * Executes current generated handlers inside the Entity's local transaction.
2774
2555
  *
2775
- * @param repository Registered Entity repository for this operation.
2776
- * @param session Fenced Agent execution session.
2777
- * @param loaded Loaded Entity and its repository storage state.
2778
- * @param entityId Typed recipient Entity identifier.
2779
- * @param makeNext Callback constructing the next durable execution record.
2780
- * @param historyEntries Agent history rows included in the fenced mutation.
2781
- * @param deferred Prepared Stand update awaiting conditional commit.
2782
- * @param prepared Prepared conditional Entity commit.
2783
- * @returns Saved execution record after the deferred Entity commit.
2556
+ * @param audit Retained history audit for this invocation.
2557
+ * @param budget Invocation-wide resource budget.
2558
+ * @param loaded Current Entity and retained storage for this turn.
2559
+ * @param preferences Current or staged model preferences.
2560
+ * @param selected Models or selection retained for this invocation.
2561
+ * @returns The typed model result with its operation identifier.
2784
2562
  */
2785
- async commitDeferred(repository, session, loaded, entityId, makeNext, historyEntries, deferred, prepared) {
2563
+ async invoke(loaded, selected, preferences, audit, budget) {
2564
+ transactionalEntityAccess.start(loaded.entity);
2786
2565
  try {
2787
- const completed = await session.complete(makeNext, historyEntries, {
2788
- context: loaded.storageInput.context,
2789
- entity: loaded.storageInput,
2790
- entityId,
2791
- next: prepared,
2792
- ...(RepositoryStorage.historyConfiguration(repository).stateHistory
2793
- ? { states: [prepared] }
2794
- : {}),
2795
- });
2796
- deferred.notify();
2797
- return completed;
2566
+ const produced = [];
2567
+ for (const handler of this.handlers)
2568
+ produced.push(...(await this.invokeHandler(loaded, handler, selected, preferences, audit, budget)));
2569
+ const commit = await commitFenced(loaded.entity, (current) => transactionalEntityAccess.commit(current));
2570
+ if (commit.status === "rejected")
2571
+ throw new TransitionValidationError(commit.validation.error);
2572
+ return produced;
2798
2573
  }
2799
2574
  catch (error) {
2800
- deferred.cancel();
2575
+ transactionalEntityAccess.rollback(loaded.entity);
2801
2576
  throw error;
2802
2577
  }
2803
- },
2804
- /**
2805
- * Binds declared generated Event and Command results in handler order.
2806
- *
2807
- * @param repository Agent declaration.
2808
- * @param runtime Signal metadata factory.
2809
- * @param accepted Original accepted envelope.
2810
- * @param entityId Typed recipient.
2811
- * @param version Entity Version before this transition.
2812
- * @param produced Handler results in declaration order.
2813
- * @returns Original outgoing envelopes ready for one conditional completion.
2814
- */
2815
- bindOutputs(repository, runtime, accepted, entityId, version, produced) {
2816
- const source = accepted.signal;
2817
- if (source.case !== "command" && source.case !== "event")
2818
- throw new Error("Agent output requires its accepted source signal.");
2819
- const routing = repositoryRoutings.get(repository);
2820
- if (routing === undefined)
2821
- throw new Error("Agent output requires registered schemas.");
2822
- return produced.map((signal) => this.bindOutput(repository, runtime, routing, source, entityId, version, signal));
2823
- },
2824
- /**
2825
- * Binds one declared result using its generated Event or Command role.
2826
- *
2827
- * @param repository Registered Entity repository for this operation.
2828
- * @param runtime Bounded Context storage and publication services.
2829
- * @param routing Registered generated handler routing metadata.
2830
- * @param source Original accepted source signal.
2831
- * @param entityId Typed recipient Entity identifier.
2832
- * @param version Entity version associated with the signal.
2833
- * @param signal Original domain signal or execution cancellation signal.
2834
- * @returns Typed outgoing signal with its original envelope.
2835
- */
2836
- bindOutput(repository, runtime, routing, source, entityId, version, signal) {
2837
- const typeName = EntityInvocation.messageTypeName(signal);
2838
- const eventSchema = routing.producedEventSchemas.find((entry) => entry.typeName === typeName);
2839
- if (eventSchema !== undefined)
2840
- return this.bindEventOutput(repository, runtime, source, entityId, version, signal, eventSchema);
2841
- const schema = routing.producedCommandSchemas.find((entry) => entry.typeName === typeName);
2842
- if (schema === undefined)
2843
- throw new Error(`Agent cannot emit undeclared signal "${typeName}".`);
2844
- return this.bindCommandOutput(runtime, source, signal, schema);
2845
- },
2846
- /**
2847
- * Creates the original domain Event envelope and producer context.
2848
- *
2849
- * @param repository Registered Entity repository for this operation.
2850
- * @param runtime Bounded Context storage and publication services.
2851
- * @param source Original accepted source signal.
2852
- * @param entityId Typed recipient Entity identifier.
2853
- * @param version Entity version associated with the signal.
2854
- * @param signal Original domain signal or execution cancellation signal.
2855
- * @param schema Generated message descriptor for the signal.
2856
- * @returns Typed original Event envelope for one normal EventBus handoff.
2857
- */
2858
- bindEventOutput(repository, runtime, source, entityId, version, signal, schema) {
2859
- if (source.case !== "command" && source.case !== "event")
2860
- throw new Error("Missing source.");
2861
- const metadata = source.case === "command"
2862
- ? runtime.signalMetadata.eventFromCommand(source.value, { version: version.number })
2863
- : runtime.signalMetadata.eventFromEvent(source.value, { version: version.number });
2864
- const event = create(EventSchema, {
2865
- id: metadata.id,
2866
- message: AnyMessages.pack(schema, signal),
2867
- context: RepositorySignals.eventContextWithProducer(metadata.context, repository, entityId, version),
2868
- });
2869
- return { case: "event", value: event };
2870
- },
2871
- /**
2872
- * Creates the original Command envelope from its accepted origin.
2873
- *
2874
- * @param runtime Bounded Context storage and publication services.
2875
- * @param source Original accepted source signal.
2876
- * @param signal Original domain signal or execution cancellation signal.
2877
- * @param schema Generated message descriptor for the signal.
2878
- * @returns Typed original Command envelope for durable delivery.
2879
- */
2880
- bindCommandOutput(runtime, source, signal, schema) {
2881
- if (source.case !== "command" && source.case !== "event")
2882
- throw new Error("Missing source.");
2883
- const metadata = source.case === "command"
2884
- ? runtime.signalMetadata.commandFromCommand(source.value)
2885
- : runtime.signalMetadata.commandFromEvent(source.value);
2886
- const command = create(CommandSchema, {
2887
- id: metadata.id,
2888
- message: AnyMessages.pack(schema, signal),
2889
- context: metadata.context,
2890
- });
2891
- return { case: "command", value: command };
2892
- },
2578
+ }
2893
2579
  /**
2894
- * Dispatches every emitted Event to the normal EventBus and advances saved Commands.
2580
+ * Executes one handler with live AI and read facades scoped to its returned work.
2895
2581
  *
2896
- * @param runtime Bound normal bus acceptance callbacks.
2897
- * @param session Fenced record containing pending saved envelopes.
2898
- * @param outputs Locally produced outputs in original handler order.
2899
- * @returns When the fenced repository operation completes.
2582
+ * @param audit Retained history audit for this invocation.
2583
+ * @param budget Invocation-wide resource budget.
2584
+ * @param handler Selected generated handler.
2585
+ * @param loaded Current Entity and retained storage for this turn.
2586
+ * @param preferences Current or staged model preferences.
2587
+ * @param selected Models or selection retained for this invocation.
2588
+ * @returns The declared handler result scoped to live capabilities.
2900
2589
  */
2901
- async deliverInitial(runtime, session, outputs) {
2902
- let commandIndex = 0;
2903
- let failed = false;
2904
- let firstFailure;
2905
- for (const output of outputs) {
2906
- try {
2907
- if (output.case === "event") {
2908
- await runtime.publishAgentEvent(output.value);
2909
- }
2910
- else {
2911
- await this.deliverCommand(runtime, session, commandIndex);
2912
- }
2913
- }
2914
- catch (error) {
2915
- if (!failed) {
2916
- firstFailure = error;
2917
- failed = true;
2918
- }
2919
- }
2920
- if (output.case === "command")
2921
- commandIndex += 1;
2590
+ async invokeHandler(loaded, handler, selected, preferences, audit, budget) {
2591
+ const { facade, actor, deadline } = this.handlerFacade(selected, preferences, audit, budget);
2592
+ const historyScope = repositoryAccess.agentHistoryScope(loaded.storageInput.context, loaded.storageInput, this.entityId);
2593
+ const reads = new AgentLiveReadRuntime(historyScope, budget, this.checkSession);
2594
+ const release = this.bind(loaded.entity, facade, reads, actor);
2595
+ try {
2596
+ const output = await AgentLiveHandler.awaitHandler(this.callHandler(loaded.entity, handler), this.signal, deadline);
2597
+ reads.finish();
2598
+ facade.finish();
2599
+ return output;
2922
2600
  }
2923
- if (failed)
2924
- throw firstFailure;
2925
- },
2926
- /**
2927
- * Retries pending saved Commands without rerunning the Agent handler.
2928
- *
2929
- * @param runtime Bound normal Command bus callbacks.
2930
- * @param session Fenced record containing saved Commands.
2931
- * @returns Completion after pending Command attempts.
2932
- */
2933
- async deliverCommands(runtime, session) {
2934
- for (const [index] of (session.record().completion?.commands ?? []).entries()) {
2935
- if (session.signal.aborted)
2936
- return;
2937
- await this.deliverCommand(runtime, session, index);
2601
+ finally {
2602
+ facade.close();
2603
+ release();
2938
2604
  }
2939
- },
2605
+ }
2940
2606
  /**
2941
- * Sends one saved Command under the current claim and records its acknowledgement.
2607
+ * Creates live AI and actor capabilities to one generated handler.
2942
2608
  *
2943
- * @param runtime Bound normal Command bus callbacks.
2944
- * @param session Fenced record containing saved Commands.
2945
- * @param index Command position in the saved completion.
2946
- * @returns Completion after the Command is acknowledged or already delivered.
2609
+ * @param audit Retained history audit for this invocation.
2610
+ * @param budget Invocation-wide resource budget.
2611
+ * @param preferences Current or staged model preferences.
2612
+ * @param selected Models or selection retained for this invocation.
2613
+ * @returns The handler facade with live AI and actor capabilities.
2947
2614
  */
2948
- async deliverCommand(runtime, session, index) {
2949
- const output = session.record().completion?.commands[index];
2950
- if (output === undefined || output.delivered)
2951
- return;
2952
- const command = output.command;
2953
- if (command === undefined)
2954
- throw new Error("Agent outgoing Command has no original envelope.");
2955
- const key = this.outputKey(output);
2956
- const plan = output.plan ?? (await runtime.prepareSavedCommand(command));
2957
- if (output.plan === undefined) {
2958
- await session.update((current) => {
2959
- const pending = current.completion?.commands[index];
2960
- if (pending === undefined ||
2961
- pending.plan !== undefined ||
2962
- pending.command?.id?.uuid !== command.id?.uuid)
2963
- throw new Error("Saved Agent Command changed before plan installation.");
2964
- pending.plan = plan;
2965
- return current;
2966
- });
2967
- }
2968
- await runtime.publishSavedCommand(command, plan);
2969
- await session.markDelivered([key]);
2970
- runtime.recordAcceptedSaved(command);
2971
- },
2615
+ handlerFacade(selected, preferences, audit, budget) {
2616
+ const ai = this.runtime.ai;
2617
+ const policy = repositoryAccess.agentConfiguration(this.repository).ai;
2618
+ if (ai === undefined || policy === undefined)
2619
+ throw new Error("Agent AI policy is missing.");
2620
+ const scope = AgentModelSelection.scopeLive(this.invocation, this.repository.stateSchema, this.runtime.signalMetadata, this.tenantId);
2621
+ const facade = new AgentLiveAiRuntime(ai, policy, scope, selected, budget, this.signal, this.deadline(ai), (kind, model) => {
2622
+ this.stage(preferences, kind, model);
2623
+ }, audit, this.checkSession);
2624
+ return { facade, actor: scope.actor, deadline: this.deadline(ai) };
2625
+ }
2972
2626
  /**
2973
- * Returns the typed original ID of one completed outgoing Command.
2627
+ * Prepares a per-kind selection without changing the observed preference image.
2974
2628
  *
2975
- * @param output Prepared outgoing Command.
2976
- * @returns Original durable Command ID.
2629
+ * @param kind Declared model or operation kind.
2630
+ * @param model Selected AI model for this invocation.
2631
+ * @param preferences Current or staged model preferences.
2977
2632
  */
2978
- outputKey(output) {
2979
- if (output.command?.id !== undefined)
2980
- return output.command.id;
2981
- throw new Error("Agent outgoing Command requires its original typed ID.");
2982
- },
2633
+ stage(preferences, kind, model) {
2634
+ const index = preferences.findIndex((entry) => entry.kind === kind);
2635
+ if (index >= 0)
2636
+ preferences.splice(index, 1);
2637
+ if (model !== undefined)
2638
+ preferences.push(create(ModelPreferenceSchema, { kind, selection: { case: "model", value: model } }));
2639
+ }
2983
2640
  /**
2984
- * Validates every stored binding against the current generated selectors.
2641
+ * Binds current protected history, query, and AI capabilities to one handler.
2985
2642
  *
2986
- * @param repository Agent registration.
2987
- * @param accepted Immutable source and ordered bindings.
2988
- * @returns Current matching handler metadata.
2643
+ * @param actor Captured signal actor context.
2644
+ * @param entity Current Entity instance.
2645
+ * @param facade Handler-facing operation facade.
2646
+ * @param reads Retained Agent history reader.
2647
+ * @returns The handler bound to current protected history and AI capabilities.
2989
2648
  */
2990
- handlers(repository, accepted) {
2991
- const routing = repositoryRoutings.get(repository);
2992
- if (routing === undefined)
2993
- throw new Error("Agent execution requires registered handlers.");
2994
- const current = this.selectedHandlers(routing, accepted);
2995
- if (accepted.handlers.length !== current.length || current.length === 0)
2996
- throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent handler binding changed before replay.");
2997
- for (const [ordinal, handler] of current.entries()) {
2998
- const binding = accepted.handlers[ordinal];
2999
- if (binding === undefined || !AgentAdmission.matchesHandler(binding, handler, ordinal))
3000
- throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent handler binding changed before replay.");
2649
+ bind(entity, facade, reads, actor) {
2650
+ const history = AgentHistoryReads.withReadInterceptor(entity, (scope, request, live, maxBytes, present) => reads.history(scope, request, live, maxBytes, present));
2651
+ let query;
2652
+ try {
2653
+ query = ProcessManagerQueries.bind(entity, this.runtime, actor, this.tenantId, (schema, request, live) => reads.query(schema, request, live));
2654
+ const ai = AgentAiBindings.bind(entity, facade);
2655
+ return () => {
2656
+ ai();
2657
+ query?.();
2658
+ history();
2659
+ };
3001
2660
  }
3002
- return current;
3003
- },
3004
- /**
3005
- * Finds current Command or Event handlers from the immutable source envelope.
3006
- *
3007
- * @param routing Registered generated handler routing metadata.
3008
- * @param accepted Durable original signal and saved handler bindings.
3009
- * @returns Generated handlers selected for the accepted signal.
3010
- */
3011
- selectedHandlers(routing, accepted) {
3012
- if (accepted.signal.case === "command") {
3013
- const binding = accepted.handlers[0];
3014
- const current = routing.commandReadiness?.findCommandAssignee(binding?.signalType ?? "");
3015
- return current === undefined ? [] : [current.registeredHandler];
3016
- }
3017
- if (accepted.signal.case !== "event")
3018
- throw new Error("Agent source envelope is missing.");
3019
- const event = accepted.signal.value;
3020
- const packed = EntityInvocation.requireSignalMessage(event.message, "event");
3021
- const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
3022
- const value = EntityInvocation.unpackRequired(packed, schema, "event");
3023
- const external = event.context?.external === true;
3024
- return [
3025
- ...routing.eventReactors(schema.typeName, value, external),
3026
- ...routing.commandReactions(schema.typeName, value, external),
3027
- ];
3028
- },
3029
- /**
3030
- * Records start facts and the original dispatch System Event before handler execution.
3031
- *
3032
- * @param repository Agent registration.
3033
- * @param runtime Bound services.
3034
- * @param session Fenced provider claim and current record.
3035
- * @param loaded Restored Entity and initial Version.
3036
- * @param entityId Original typed recipient.
3037
- * @param tenantId Accepted delivery tenant when applicable.
3038
- * @returns Updated record image for exact subsequent CAS.
3039
- */
3040
- async start(repository, runtime, session, loaded, entityId, tenantId) {
3041
- const accepted = session.record().accepted;
3042
- if (accepted === undefined)
3043
- throw new Error("Agent start requires accepted work.");
3044
- const started = this.startFacts(repository, runtime, accepted, loaded);
3045
- const audit = this.startAudit(runtime, repository, accepted, entityId);
3046
- await session.update((current) => {
3047
- current.started = started;
3048
- return current;
3049
- }, [this.startHistory(audit)]);
3050
- if (accepted.signal.case === "command")
3051
- HandlerDispatchPublisher.publishCommand(runtime, audit);
3052
- else
3053
- HandlerDispatchPublisher.publishReactor(runtime, audit);
3054
- return this.selectMissingModels(repository, runtime, session, accepted, tenantId);
3055
- },
3056
- /**
3057
- * Stores the original dispatch audit envelope in mandatory Agent history.
3058
- *
3059
- * @param audit Original Agent System event envelope.
3060
- * @returns Typed Agent history row for the original dispatch Event.
3061
- */
3062
- startHistory(audit) {
3063
- const occurredAt = audit.context?.timestamp;
3064
- if (occurredAt === undefined)
3065
- throw new Error("Agent dispatch audit requires its timestamp.");
3066
- return create(AgentHistoryEntrySchema, {
3067
- occurredAt,
3068
- item: { case: "systemEvent", value: audit },
3069
- });
3070
- },
3071
- /**
3072
- * Verifies admission revisions and fixes whole-invocation limits before execution.
3073
- *
3074
- * @param repository Registered Entity repository for this operation.
3075
- * @param runtime Bounded Context storage and publication services.
3076
- * @param accepted Durable original signal and saved handler bindings.
3077
- * @param loaded Loaded Entity and its repository storage state.
3078
- * @returns Durable deadline, initial version, and whole-invocation bounds.
3079
- */
3080
- startFacts(repository, runtime, accepted, loaded) {
3081
- const configuration = repositoryAccess.agentConfiguration(repository);
3082
- const ai = runtime.ai;
3083
- if (accepted.key === undefined || ai === undefined || configuration.ai === undefined)
3084
- throw new Error("Agent execution start requires accepted work and AI policy.");
3085
- const limits = registryOptions(ai).invocationLimits ?? {};
3086
- return create(AgentExecutionStartSchema, {
3087
- ...(limits.deadlineMs === undefined
3088
- ? {}
3089
- : { deadline: addMillis(Time.currentTime(), limits.deadlineMs) }),
3090
- initialVersion: RepositoryEntities.priorVersion(loaded.current),
3091
- bounds: this.invocationBounds(limits),
3092
- });
3093
- },
3094
- /**
3095
- * Persists each model kind without a saved connection identity.
3096
- *
3097
- * @param repository Agent registration and permitted capabilities.
3098
- * @param runtime Bound AI registry.
3099
- * @param session Current fenced execution record.
3100
- * @param accepted Original accepted signal and actor.
3101
- * @param tenantId Accepted tenant when applicable.
3102
- * @returns Record after each missing identity has been saved.
3103
- */
3104
- async selectMissingModels(repository, runtime, session, accepted, tenantId) {
3105
- const ai = runtime.ai;
3106
- const configuration = repositoryAccess.agentConfiguration(repository).ai;
3107
- if (ai === undefined || configuration === undefined)
3108
- throw new Error("Agent model selection requires saved start facts and AI policy.");
3109
- const deadlineMs = this.savedModelDeadline(session, "selection");
3110
- const source = accepted.signal.value?.message;
3111
- for (const kind of ["generation", "decision"]) {
3112
- if (!configuration.models.some((model) => model.definition.kind === kind))
3113
- continue;
3114
- const modelKind = AgentModelSelection.kind(kind);
3115
- if (session.record().started?.models.some((model) => model.kind === modelKind))
3116
- continue;
3117
- const selected = await AgentModelSelection.selectKind(ai, configuration, AgentModelSelection.scope(accepted, repository.stateSchema, tenantId), session.initialPreferences(), session.signal, deadlineMs, kind, source);
3118
- await this.saveSelectedModel(session, selected);
2661
+ catch (error) {
2662
+ query?.();
2663
+ history();
2664
+ throw error;
3119
2665
  }
3120
- return session.record();
3121
- },
2666
+ }
3122
2667
  /**
3123
- * Writes one selected model under the current Agent claim.
2668
+ * Invokes and validates one declared generated Command or Event handler.
3124
2669
  *
3125
- * @param session Fenced Agent execution session.
3126
- * @param selected Authorized deployment and connection identity.
3127
- * @returns When the selection is durable.
2670
+ * @param entity Current Entity instance.
2671
+ * @param handler Selected generated handler.
2672
+ * @returns The validated generated handler result.
3128
2673
  */
3129
- async saveSelectedModel(session, selected) {
3130
- await session.update((record) => {
3131
- if (record.started === undefined)
3132
- throw new Error("Agent start facts disappeared.");
3133
- record.started.models.push(selected);
3134
- return record;
3135
- });
3136
- },
3137
- /**
3138
- * Rejects accepted work when its exact handler and AI policy changed.
3139
- *
3140
- * @param repository Registered Entity repository for this operation.
3141
- * @param accepted Durable original signal and saved handler bindings.
3142
- * @param handlers Generated handlers in their saved order.
3143
- * @param ai Configured AI registry for this Bounded Context.
3144
- * @param configuration Repository and AI policy used for revision checks.
3145
- */
3146
- assertRevisions(repository, accepted, handlers, ai, configuration) {
3147
- if (configuration.ai === undefined)
3148
- throw new Error("Agent AI policy is required.");
3149
- const revisions = AgentRevisions.atAdmission(repository.stateSchema, handlers, ai, configuration.ai);
3150
- if (accepted.codeRevision?.value !== configuration.codeRevision ||
3151
- accepted.schemaRevision?.value !== revisions.schema ||
3152
- accepted.policyRevision?.value !== revisions.policy)
3153
- throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent code, schema, or policy changed before execution.");
3154
- },
3155
- /**
3156
- * Copies the whole-invocation budget into its durable integer fields.
3157
- *
3158
- * @param limits Configured whole-invocation limits.
3159
- * @returns Whole-transition limits persisted at invocation start.
3160
- */
3161
- invocationBounds(limits) {
3162
- return create(AgentInvocationBoundsSchema, {
3163
- ...(limits.operations === undefined ? {} : { operations: BigInt(limits.operations) }),
3164
- ...(limits.modelRequests === undefined
3165
- ? {}
3166
- : { modelRequests: BigInt(limits.modelRequests) }),
3167
- ...(limits.toolCalls === undefined ? {} : { toolCalls: BigInt(limits.toolCalls) }),
3168
- ...(limits.recordedReads === undefined
3169
- ? {}
3170
- : { recordedReads: BigInt(limits.recordedReads) }),
3171
- ...(limits.deadlineMs === undefined ? {} : { deadlineMillis: BigInt(limits.deadlineMs) }),
3172
- ...(limits.totalInputBytes === undefined
3173
- ? {}
3174
- : { totalInputBytes: BigInt(limits.totalInputBytes) }),
3175
- ...(limits.totalOutputBytes === undefined
3176
- ? {}
3177
- : { totalOutputBytes: BigInt(limits.totalOutputBytes) }),
3178
- ...(limits.maxRecoveryBytes === undefined
3179
- ? {}
3180
- : { maxRecoveryBytes: BigInt(limits.maxRecoveryBytes) }),
3181
- });
3182
- },
2674
+ async callHandler(entity, handler) {
2675
+ const source = this.invocation.source;
2676
+ const kind = source.case;
2677
+ const message = EntityInvocation.unpackRequired(EntityInvocation.requireSignalMessage(source.value.message, kind), handler.handler.schema, kind);
2678
+ const context = kind === "command"
2679
+ ? EntityInvocation.commandHandlerContext(source.value)
2680
+ : EntityInvocation.eventHandlerContext(source.value);
2681
+ const produced = await EntityInvocation.invokeEntityMethod(entity, handler.handler.methodName, message, handler.handler.parameterCount, context);
2682
+ const outputs = produced === undefined
2683
+ ? []
2684
+ : Array.isArray(produced)
2685
+ ? Array.from(produced)
2686
+ : [produced];
2687
+ RepositoryHandlers.requireDeclaredOutputs(handler.handler, outputs);
2688
+ return outputs;
2689
+ }
3183
2690
  /**
3184
- * Creates a genuine source-specific dispatch System Event before the handler.
2691
+ * Commits the observed state/preferences image and its final audit atomically.
3185
2692
  *
3186
- * @param runtime Bounded Context storage and publication services.
3187
- * @param repository Registered Entity repository for this operation.
3188
- * @param accepted Durable original signal and saved handler bindings.
3189
- * @param entityId Typed recipient Entity identifier.
3190
- * @returns Original dispatch System Event envelope.
2693
+ * @param expected Expected persisted value or version.
2694
+ * @param loaded Current Entity and retained storage for this turn.
2695
+ * @param preferences Current or staged model preferences.
2696
+ * @param produced Declared outgoing signals.
2697
+ * @returns Completion after the conditional state, preferences, and final audit commit.
3191
2698
  */
3192
- startAudit(runtime, repository, accepted, entityId) {
3193
- const source = accepted.signal;
3194
- if (source.case === "command")
3195
- return HandlerDispatchPublisher.commandEvent(runtime, repository, source.value, entityId);
3196
- if (source.case === "event")
3197
- return HandlerDispatchPublisher.reactorEvent(runtime, repository, source.value, entityId);
3198
- throw new Error("Agent start requires its accepted envelope.");
3199
- },
2699
+ async complete(loaded, expected, preferences, produced) {
2700
+ const version = RepositoryEntities.repositoryVersion(loaded.entity);
2701
+ const routing = repositoryRoutings.get(this.repository);
2702
+ if (routing === undefined)
2703
+ throw new Error("Live Agent output requires registered routing.");
2704
+ const outputs = produced.case === "rejected"
2705
+ ? [produced.output]
2706
+ : produced.values.map((item) => AgentLiveOutputs.bindOutput(this.repository, this.runtime, routing, this.invocation.source, this.entityId, version, item));
2707
+ await this.authorizePreferences(expected ?? [], preferences);
2708
+ await this.checkSession();
2709
+ const preferenceEvents = this.preferenceEvents(expected ?? [], preferences);
2710
+ const history = [
2711
+ ...AgentLiveOutputs.emittedHistory(outputs),
2712
+ ...preferenceEvents.map((event) => this.systemEntry(event)),
2713
+ ];
2714
+ await this.commit(loaded, expected, preferences, history);
2715
+ await this.checkSession();
2716
+ for (const event of preferenceEvents)
2717
+ await this.runtime.publishAgentSystemEvent(event);
2718
+ this.publish(outputs);
2719
+ }
3200
2720
  /**
3201
- * Invokes saved Command or Event bindings inside one Entity transaction.
2721
+ * Applies final conditional state and preference mutation under the observed Version.
3202
2722
  *
3203
- * @param entity Restored Agent instance.
3204
- * @param accepted Original accepted signal.
3205
- * @param handlers Validated current handler bindings.
3206
- * @param repository Registered Entity repository for this operation.
3207
- * @param runtime Bounded Context storage and publication services.
3208
- * @param tenantId Tenant selected for this accepted signal.
3209
- * @param session Fenced Agent execution session.
3210
- * @returns Outputs returned by the selected generated handlers.
2723
+ * @param expected Expected persisted value or version.
2724
+ * @param history Retained history entries or history port.
2725
+ * @param loaded Current Entity and retained storage for this turn.
2726
+ * @param preferences Current or staged model preferences.
2727
+ * @returns The atomic conditional commit result for the observed version.
3211
2728
  */
3212
- async invoke(repository, runtime, tenantId, session, entity, accepted, handlers) {
3213
- transactionalEntityAccess.start(entity);
2729
+ async commit(loaded, expected, preferences, history) {
2730
+ const deferred = await this.deferredUpdate(loaded);
3214
2731
  try {
3215
- const signals = [];
3216
- for (const [ordinal, handler] of handlers.entries()) {
3217
- signals.push(...(await this.invokeBoundHandler(repository, runtime, tenantId, session, entity, accepted, handler, ordinal)));
3218
- }
3219
- await this.commitInvokedEntity(entity);
3220
- return signals;
2732
+ const prepared = deferred === undefined
2733
+ ? undefined
2734
+ : RepositoryStand.preparedRecord(this.repository, deferred, this.entityId);
2735
+ await this.checkSession();
2736
+ await loaded.commits.commitAgent({
2737
+ entityId: this.entityId,
2738
+ expectedVersion: loaded.current?.version?.number ?? 0,
2739
+ expectedPreferences: expected,
2740
+ preferences,
2741
+ historyEntries: history,
2742
+ ...(prepared === undefined ? {} : { entityCommit: this.entityCommit(loaded, prepared) }),
2743
+ });
2744
+ deferred?.notify();
3221
2745
  }
3222
2746
  catch (error) {
3223
- transactionalEntityAccess.rollback(entity);
2747
+ deferred?.cancel();
3224
2748
  throw error;
3225
2749
  }
3226
- },
2750
+ }
3227
2751
  /**
3228
- * Validates the transaction result before Agent output completion.
2752
+ * Creates current state and its enabled history only for a changed Entity.
3229
2753
  *
3230
- * @param entity Loaded recipient Entity instance.
3231
- * @returns When the fenced repository operation completes.
2754
+ * @param loaded Current Entity and retained storage for this turn.
2755
+ * @param prepared Prepared Entity mutation and outputs.
2756
+ * @returns The changed Entity image and enabled history for the commit.
3232
2757
  */
3233
- async commitInvokedEntity(entity) {
3234
- const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
3235
- if (commit.status === "rejected")
3236
- throw new TransitionValidationError(commit.validation.error);
3237
- },
2758
+ entityCommit(loaded, prepared) {
2759
+ return {
2760
+ context: loaded.storageInput.context,
2761
+ entity: loaded.storageInput,
2762
+ entityId: this.entityId,
2763
+ next: prepared,
2764
+ ...(loaded.storageInput.stateHistory ? { states: [prepared] } : {}),
2765
+ };
2766
+ }
3238
2767
  /**
3239
- * Binds named AI and history reads around one generated handler call.
2768
+ * Prepares the changed Entity state for publication only after atomic commit.
3240
2769
  *
3241
- * @param repository Registered Entity repository for this operation.
3242
- * @param runtime Bounded Context storage and publication services.
3243
- * @param tenantId Tenant selected for this accepted signal.
3244
- * @param session Fenced Agent execution session.
3245
- * @param entity Loaded recipient Entity instance.
3246
- * @param accepted Durable original signal and saved handler bindings.
3247
- * @param handler Generated handler selected for this signal.
3248
- * @param ordinal Saved position of the selected handler.
3249
- * @returns Outputs returned by one generated handler.
3250
- */
3251
- async invokeBoundHandler(repository, runtime, tenantId, session, entity, accepted, handler, ordinal) {
3252
- const facade = this.handlerFacade(repository, runtime, tenantId, session, accepted, ordinal);
3253
- const reads = new AgentReadRuntime(session, ordinal);
3254
- const release = this.bindHandlerCapabilities(entity, runtime, tenantId, accepted, reads, facade);
3255
- try {
3256
- const deadlineEpochMs = this.handlerDeadline(session);
3257
- const signals = await this.awaitHandler(this.invokeHandler(entity, accepted, handler), session.signal, deadlineEpochMs);
3258
- reads.finish();
3259
- facade.finish();
3260
- return signals;
3261
- }
3262
- finally {
3263
- facade.close();
3264
- release();
2770
+ * @param loaded Current Entity and retained storage for this turn.
2771
+ * @returns The Stand update deferred until the atomic commit succeeds.
2772
+ */
2773
+ deferredUpdate(loaded) {
2774
+ if (!RepositoryEntities.repositoryChanged(loaded.entity))
2775
+ return Promise.resolve(undefined);
2776
+ return standAccess.deferUpdate(this.runtime.stand, this.repository.stateSchema, RepositoryEntities.repositoryState(loaded.entity), RepositoryStand.standUpdateOptions(this.tenantId, RepositoryEntities.repositoryVersion(loaded.entity), RepositoryEntities.repositoryLifecycle(loaded.entity)), this.repository.metadata);
2777
+ }
2778
+ /**
2779
+ * Checks authorization of effective changed selections before final persistence.
2780
+ *
2781
+ * @param after Proposed value after the transition.
2782
+ * @param before Persisted value before the transition.
2783
+ * @returns Completion after changed model selections pass authorization.
2784
+ */
2785
+ async authorizePreferences(before, after) {
2786
+ const ai = this.runtime.ai;
2787
+ const policy = repositoryAccess.agentConfiguration(this.repository).ai;
2788
+ if (ai === undefined || policy?.authorizeSelection === undefined)
2789
+ return;
2790
+ const scope = AgentModelSelection.scopeLive(this.invocation, this.repository.stateSchema, this.runtime.signalMetadata, this.tenantId);
2791
+ for (const kind of [AiModelKind.GENERATION, AiModelKind.DECISION]) {
2792
+ const previous = this.preference(before, kind);
2793
+ const selected = this.preference(after, kind);
2794
+ if (previous === undefined && selected === undefined)
2795
+ continue;
2796
+ if (previous !== undefined &&
2797
+ selected !== undefined &&
2798
+ equals(ModelRefSchema, previous, selected))
2799
+ continue;
2800
+ await AgentModelSelection.authorizeSelection(ai, policy, scope, selected, this.signal, this.deadline(ai));
3265
2801
  }
3266
- },
2802
+ }
3267
2803
  /**
3268
- * Binds Agent AI, indexed history and routed projection queries for one handler.
2804
+ * Returns the explicit model reference for one kind, or inherited absence.
3269
2805
  *
3270
- * @param entity Loaded recipient Entity instance.
3271
- * @param runtime Bounded Context storage and publication services.
3272
- * @param tenantId Tenant selected for this accepted signal.
3273
- * @param accepted Durable original signal and saved handler bindings.
3274
- * @param reads Handler-scoped durable read facade.
3275
- * @param facade Handler-scoped AI facade.
3276
- * @returns Release callback for handler-scoped AI and read facades.
3277
- */
3278
- bindHandlerCapabilities(entity, runtime, tenantId, accepted, reads, facade) {
3279
- const releaseHistory = AgentHistoryReads.withJournal(entity, (scope, view, request, live, maxBytes) => reads.history(scope, view, request, live, maxBytes));
3280
- let releaseQuery;
3281
- try {
3282
- releaseQuery = ProcessManagerQueries.bind(entity, runtime, accepted.actor, tenantId, (schema, query, live) => reads.query(schema, query, live));
3283
- const releaseAi = AgentAiBindings.bind(entity, facade);
3284
- return () => {
3285
- releaseQuery?.();
3286
- releaseHistory();
3287
- releaseAi();
3288
- };
3289
- }
3290
- catch (error) {
3291
- releaseQuery?.();
3292
- releaseHistory();
3293
- throw error;
3294
- }
3295
- },
2806
+ * @param kind Declared model or operation kind.
2807
+ * @param values Typed values being committed.
2808
+ * @returns The explicit model reference for the kind, or inherited absence.
2809
+ */
2810
+ preference(values, kind) {
2811
+ const selected = values.find((entry) => entry.kind === kind);
2812
+ return selected?.selection.case === "model" ? selected.selection.value : undefined;
2813
+ }
3296
2814
  /**
3297
- * Creates one handler-scoped facade from its accepted model policy.
2815
+ * Creates original selection System Events for changed committed preferences.
3298
2816
  *
3299
- * @param repository Registered Entity repository for this operation.
3300
- * @param runtime Bounded Context storage and publication services.
3301
- * @param tenantId Tenant selected for this accepted signal.
3302
- * @param session Fenced Agent execution session.
3303
- * @param accepted Durable original signal and saved handler bindings.
3304
- * @param ordinal Saved position of the selected handler.
3305
- * @returns AI facade bound to one selected handler.
3306
- */
3307
- handlerFacade(repository, runtime, tenantId, session, accepted, ordinal) {
3308
- const ai = runtime.ai;
3309
- const configuration = repositoryAccess.agentConfiguration(repository).ai;
3310
- if (ai === undefined || configuration === undefined)
3311
- throw new Error("Agent handler requires its bound AI registry.");
3312
- return new AgentAiRuntime(ai, configuration, AgentModelSelection.scope(accepted, repository.stateSchema, tenantId), session, ordinal, new AgentInteractionAudit(session, accepted, repository.stateSchema, runtime.signalMetadata, runtime.registerSystemEventSchema, runtime.publishAgentSystemEvent));
3313
- },
2817
+ * @param after Proposed value after the transition.
2818
+ * @param before Persisted value before the transition.
2819
+ * @returns The original selection System Events for changed preferences.
2820
+ */
2821
+ preferenceEvents(before, after) {
2822
+ const scope = AgentModelSelection.scopeLive(this.invocation, this.repository.stateSchema, this.runtime.signalMetadata, this.tenantId);
2823
+ return [AiModelKind.GENERATION, AiModelKind.DECISION].flatMap((kind) => {
2824
+ const previous = this.preference(before, kind);
2825
+ const selected = this.preference(after, kind);
2826
+ if (previous === undefined && selected === undefined)
2827
+ return [];
2828
+ if (previous !== undefined &&
2829
+ selected !== undefined &&
2830
+ equals(ModelRefSchema, previous, selected))
2831
+ return [];
2832
+ return [this.selectionEvent(scope, kind, previous, selected)];
2833
+ });
2834
+ }
3314
2835
  /**
3315
- * Invokes one selected generated handler and validates its declared results.
2836
+ * Creates one original committed model-selection System Event.
3316
2837
  *
3317
- * @param entity Loaded recipient Entity instance.
3318
- * @param accepted Durable original signal and saved handler bindings.
3319
- * @param handler Generated handler selected for this signal.
3320
- * @returns Declared outputs returned by one generated handler.
2838
+ * @param kind Declared model or operation kind.
2839
+ * @param previous Persisted value before the update.
2840
+ * @param scope Captured invocation or tenant scope.
2841
+ * @param selected Models or selection retained for this invocation.
2842
+ * @returns The original committed model-selection System Event.
3321
2843
  */
3322
- async invokeHandler(entity, accepted, handler) {
3323
- const source = accepted.signal;
3324
- if (source.case !== "command" && source.case !== "event")
3325
- throw new Error("Agent source envelope is missing.");
3326
- const kind = source.case;
3327
- const message = EntityInvocation.unpackRequired(EntityInvocation.requireSignalMessage(source.value.message, kind), handler.handler.schema, kind);
3328
- const context = source.case === "command"
3329
- ? EntityInvocation.commandHandlerContext(source.value)
3330
- : EntityInvocation.eventHandlerContext(source.value);
3331
- const produced = await EntityInvocation.invokeEntityMethod(entity, handler.handler.methodName, message, handler.handler.parameterCount, context);
3332
- const signals = produced === undefined
3333
- ? []
3334
- : Array.isArray(produced)
3335
- ? Array.from(produced)
3336
- : [produced];
3337
- RepositoryHandlers.requireDeclaredOutputs(handler.handler, signals);
3338
- return signals;
3339
- },
2844
+ selectionEvent(scope, kind, previous, selected) {
2845
+ const payload = create(SelectionChangedSchema, {
2846
+ agent: scope.agent,
2847
+ sourceSignal: scope.source,
2848
+ kind,
2849
+ ...(previous === undefined ? {} : { previousModel: previous }),
2850
+ ...(selected === undefined ? {} : { selectedModel: selected }),
2851
+ changedAt: Time.currentTime(),
2852
+ });
2853
+ this.runtime.registerSystemEventSchema(SelectionChangedSchema);
2854
+ return AgentLiveInteractionEvents.envelope(this.invocation, this.runtime.signalMetadata, this.invocation.recipientId, SelectionChangedSchema, payload);
2855
+ }
3340
2856
  /**
3341
- * Reads the original invocation deadline before starting an application callback.
2857
+ * Wraps one committed original System Event in indexed history.
3342
2858
  *
3343
- * @param session Current fenced execution session.
3344
- * @returns Absolute saved deadline in milliseconds.
2859
+ * @param event Original typed Event envelope.
2860
+ * @returns The indexed history entry for the original System Event.
3345
2861
  */
3346
- handlerDeadline(session) {
3347
- const record = session.record();
3348
- const deadline = record.started?.deadline;
3349
- if (deadline === undefined && record.started?.bounds?.deadlineMillis !== undefined)
3350
- throw new Error("Agent handler requires its saved deadline.");
3351
- if (session.signal.aborted || this.deadlineExpired(record))
3352
- throw new Error("Agent handler deadline or claim ended.");
3353
- return deadline === undefined
3354
- ? undefined
3355
- : Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000);
3356
- },
2862
+ systemEntry(event) {
2863
+ const occurredAt = event.context?.timestamp;
2864
+ if (occurredAt === undefined)
2865
+ throw new Error("Agent System Event requires Time.");
2866
+ return create(AgentHistoryEntrySchema, {
2867
+ occurredAt,
2868
+ item: { case: "systemEvent", value: event },
2869
+ });
2870
+ }
3357
2871
  /**
3358
- * Awaits an application handler within its saved deadline and current claim.
2872
+ * Publishes produced signals through ordinary tracked publication after commit.
3359
2873
  *
3360
- * @typeParam Value Handler result.
3361
- * @param work Already-started generated handler invocation.
3362
- * @param signal Session cancellation when claim authority ends.
3363
- * @param deadlineEpochMs Original saved invocation deadline.
3364
- * @returns Handler result only while its claim and deadline remain valid.
2874
+ * @param outputs Declared outgoing Command or Event envelopes.
3365
2875
  */
3366
- async awaitHandler(work, signal, deadlineEpochMs) {
3367
- let timer;
3368
- let abort = () => undefined;
3369
- const cutoff = new Promise((_resolve, reject) => {
3370
- abort = () => {
3371
- reject(new Error("Agent handler claim ended."));
3372
- };
3373
- const tick = () => {
3374
- if (deadlineEpochMs === undefined)
3375
- return;
3376
- const remaining = deadlineEpochMs - Time.currentTimeMillis();
3377
- if (remaining <= 0)
3378
- reject(new Error("Agent handler deadline expired."));
3379
- else
3380
- timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
3381
- };
3382
- signal.addEventListener("abort", abort, { once: true });
3383
- if (signal.aborted)
3384
- abort();
2876
+ publish(outputs) {
2877
+ for (const output of outputs) {
2878
+ if (output.case === "command")
2879
+ void this.runtime.publisher.publishCommand(output.value);
2880
+ else if (output.value.context?.rejection !== undefined)
2881
+ void this.runtime.publisher.publishRejectionEvent(output.value);
3385
2882
  else
3386
- tick();
3387
- });
3388
- try {
3389
- const result = await Promise.race([work, cutoff]);
3390
- if (signal.aborted ||
3391
- (deadlineEpochMs !== undefined && Time.currentTimeMillis() >= deadlineEpochMs))
3392
- throw new Error("Agent handler deadline or claim ended.");
3393
- return result;
3394
- }
3395
- finally {
3396
- clearTimeout(timer);
3397
- signal.removeEventListener("abort", abort);
2883
+ void this.runtime.publisher.publishReactorEvent(output.value);
3398
2884
  }
3399
- },
3400
- };
3401
- /**
3402
- * Adds a positive timeout to a Time-sampled Protobuf timestamp.
3403
- *
3404
- * @param at Provider-independent current time.
3405
- * @param millis Bounded additional milliseconds.
3406
- * @returns Absolute timestamp retaining nanosecond precision.
3407
- */
3408
- function addMillis(at, millis) {
3409
- const nanos = at.nanos + (millis % 1000) * 1_000_000;
3410
- return {
3411
- ...at,
3412
- seconds: at.seconds + BigInt(Math.floor(millis / 1000)) + BigInt(Math.floor(nanos / 1_000_000_000)),
3413
- nanos: nanos % 1_000_000_000,
3414
- };
2885
+ }
3415
2886
  }
3416
2887
  /**
3417
2888
  * Executes one Process Manager Command against its loaded Entity state.
@@ -3604,12 +3075,11 @@ class ProcessManagerCommandExecution {
3604
3075
  const produced = await EntityInvocation.invokeEntityMethod(entity, assignee.handler.methodName, message, assignee.handler.parameterCount, EntityInvocation.commandHandlerContext(this.#command));
3605
3076
  const signals = this.#support.normalizeProducedSignals(produced);
3606
3077
  if (signals.length === 0)
3607
- throw new Error("Repository command handlers must return a signal.");
3078
+ throw receptionObservation.recordPostHandlerFailure(new Error("Repository command handlers must return a signal."));
3608
3079
  RepositoryHandlers.requireDeclaredOutputs(assignee.handler, signals);
3609
3080
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, assignee.handler.kind === "command-assignment" && signals.length > 0));
3610
- if (commit.status === "rejected") {
3611
- throw new TransitionValidationError(commit.validation.error);
3612
- }
3081
+ if (commit.status === "rejected")
3082
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
3613
3083
  return signals;
3614
3084
  }
3615
3085
  catch (error) {
@@ -3647,7 +3117,7 @@ class ProcessManagerCommandExecution {
3647
3117
  });
3648
3118
  return create(EventSchema, {
3649
3119
  id: metadata.id,
3650
- message: AnyMessages.pack(schema, signal),
3120
+ message: RepositorySignals.packProduced(schema, signal),
3651
3121
  context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId, version),
3652
3122
  });
3653
3123
  }
@@ -3667,7 +3137,7 @@ class ProcessManagerCommandExecution {
3667
3137
  const metadata = this.#runtime.signalMetadata.commandFromCommand(this.#command);
3668
3138
  return create(CommandSchema, {
3669
3139
  id: metadata.id,
3670
- message: AnyMessages.pack(schema, signal),
3140
+ message: RepositorySignals.packProduced(schema, signal),
3671
3141
  context: metadata.context,
3672
3142
  });
3673
3143
  }));
@@ -3781,7 +3251,7 @@ class ProcessManagerEventExecution {
3781
3251
  *
3782
3252
  * @param entityId Target Entity identifier.
3783
3253
  * @param intake Decoded Event and matching handlers.
3784
- * @returns Completion after accepted results are published.
3254
+ * @returns Completion after accepted results are registered for publication.
3785
3255
  */
3786
3256
  async #executeEntity(entityId, intake) {
3787
3257
  const tenantOptions = RepositoryTenants.standTenantOptions(this.#runtime.context, this.#event);
@@ -3798,7 +3268,7 @@ class ProcessManagerEventExecution {
3798
3268
  return;
3799
3269
  this.#publishChangedState(loaded, entityId);
3800
3270
  this.#postEvents(events);
3801
- await this.#postCommands(commands);
3271
+ this.#postCommands(commands);
3802
3272
  }
3803
3273
  /**
3804
3274
  * Publishes a state-change System Event when the Entity changed.
@@ -3844,17 +3314,18 @@ class ProcessManagerEventExecution {
3844
3314
  const commands = [];
3845
3315
  const events = [];
3846
3316
  const releaseQuery = ProcessManagerQueries.bind(entity, this.#runtime, this.#runtime.signalMetadata.originFromEvent(this.#event).actorContext, tenantId);
3847
- transactionalEntityAccess.start(entity);
3317
+ let started = false;
3848
3318
  try {
3319
+ transactionalEntityAccess.start(entity);
3320
+ started = true;
3849
3321
  await this.#invokeEventHandlers(entityId, entity, intake, eventContext, events, commands);
3850
3322
  await this.#commitEntity(entity, events.length > 0);
3851
- return Object.freeze({
3852
- commands: Object.freeze(commands),
3853
- events: Object.freeze(events),
3854
- });
3323
+ const emitted = { commands: Object.freeze(commands), events: Object.freeze(events) };
3324
+ return Object.freeze(emitted);
3855
3325
  }
3856
3326
  catch (error) {
3857
- transactionalEntityAccess.rollback(entity);
3327
+ if (started)
3328
+ transactionalEntityAccess.rollback(entity);
3858
3329
  throw error;
3859
3330
  }
3860
3331
  finally {
@@ -3914,7 +3385,7 @@ class ProcessManagerEventExecution {
3914
3385
  async #commitEntity(entity, producedEvents) {
3915
3386
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, producedEvents));
3916
3387
  if (commit.status === "rejected")
3917
- throw new TransitionValidationError(commit.validation.error);
3388
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
3918
3389
  }
3919
3390
  /**
3920
3391
  * Binds reactor results as Entity Events.
@@ -3946,7 +3417,7 @@ class ProcessManagerEventExecution {
3946
3417
  });
3947
3418
  return create(EventSchema, {
3948
3419
  id: metadata.id,
3949
- message: AnyMessages.pack(schema, signal),
3420
+ message: RepositorySignals.packProduced(schema, signal),
3950
3421
  context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId, version),
3951
3422
  });
3952
3423
  }
@@ -3966,7 +3437,7 @@ class ProcessManagerEventExecution {
3966
3437
  const metadata = this.#runtime.signalMetadata.commandFromEvent(this.#event);
3967
3438
  return create(CommandSchema, {
3968
3439
  id: metadata.id,
3969
- message: AnyMessages.pack(schema, signal),
3440
+ message: RepositorySignals.packProduced(schema, signal),
3970
3441
  context: metadata.context,
3971
3442
  });
3972
3443
  }));
@@ -3975,11 +3446,11 @@ class ProcessManagerEventExecution {
3975
3446
  * Publishes each produced Command in handler order.
3976
3447
  *
3977
3448
  * @param commands Bound Commands to publish.
3978
- * @returns Completion after all Commands are published.
3979
3449
  */
3980
- async #postCommands(commands) {
3450
+ #postCommands(commands) {
3981
3451
  for (const command of commands) {
3982
- await this.#runtime.publisher.publishCommand(command);
3452
+ // spine-log-boundary: server.process_manager_command_follow_up
3453
+ void this.#runtime.publisher.publishCommand(command);
3983
3454
  }
3984
3455
  }
3985
3456
  /**
@@ -4284,6 +3755,15 @@ Object.freeze(RepositoryIdentity);
4284
3755
  * Internal entity invocation operations.
4285
3756
  */
4286
3757
  const EntityInvocation = {
3758
+ /**
3759
+ * Returns a rejected Entity transition as an exact reception failure.
3760
+ *
3761
+ * @param validationError The rejected transition detail.
3762
+ * @returns The original transition error for propagation.
3763
+ */
3764
+ rejectedTransition(validationError) {
3765
+ return receptionObservation.recordPostHandlerFailure(new TransitionValidationError(validationError));
3766
+ },
4287
3767
  /**
4288
3768
  * Reads a generated message's type name or rejects malformed output.
4289
3769
  *
@@ -4361,7 +3841,7 @@ const EntityInvocation = {
4361
3841
  * @returns Decoded generated message.
4362
3842
  */
4363
3843
  unpackRequired(message, schema, signalKind) {
4364
- const unpacked = AnyMessages.unpack(message, schema);
3844
+ const unpacked = AnyPacker.tryUnpack(message, schema);
4365
3845
  if (unpacked === undefined) {
4366
3846
  throw new Error(`Repository ${signalKind} execution requires a readable message.`);
4367
3847
  }
@@ -4399,9 +3879,9 @@ const RepositoryEntities = {
4399
3879
  bindStorage(repository, runtime, tenantId, entityId, entity) {
4400
3880
  const storageInput = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
4401
3881
  const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, storageInput);
4402
- RepositoryHistoryInternals.bindEntityHistory(entity, storage, entityId, repository.stateSchema);
3882
+ RepositoryHistoryInternals.bindEntityHistory(repository, entity, storage, entityId, repository.stateSchema, storageInput.context.tenantId);
4403
3883
  if (repository.entityFamily === "agent")
4404
- this.bindAgentHistory(entity, entityId, storageInput, storage.agentHistory);
3884
+ this.bindAgentHistory(repository, entity, entityId, storageInput, storage.agentHistory);
4405
3885
  return {
4406
3886
  commits: storage.commits,
4407
3887
  events: storage.events,
@@ -4412,24 +3892,61 @@ const RepositoryEntities = {
4412
3892
  /**
4413
3893
  * Attaches the indexed history port to one restored Agent.
4414
3894
  *
3895
+ * @param repository Repository tracking the captured runtime.
4415
3896
  * @param entity Restored Agent instance.
4416
3897
  * @param entityId Typed Agent identifier.
4417
3898
  * @param input Entity storage scope.
4418
3899
  * @param history Mandatory Agent history port.
4419
3900
  */
4420
- bindAgentHistory(entity, entityId, input, history) {
3901
+ bindAgentHistory(repository, entity, entityId, input, history) {
4421
3902
  if (history === undefined)
4422
3903
  throw new Error("Agent history storage is required.");
4423
- AgentHistoryReads.bind(entity, {
3904
+ const lifetime = RepositoryStorage.lifetime(repository);
3905
+ const runtime = repositoryRuntimes.get(repository);
3906
+ if (runtime === undefined)
3907
+ throw new Error("Agent history requires its active runtime.");
3908
+ AgentHistoryReads.bind(entity, this.agentHistoryBinding(repository, entity, entityId, input, history, runtime, lifetime));
3909
+ },
3910
+ /**
3911
+ * Captures retained Agent history admission and outward filtering in one binding.
3912
+ *
3913
+ * @param repository Repository tracking the active runtime.
3914
+ * @param entity Restored Agent instance.
3915
+ * @param entityId Typed Agent identifier.
3916
+ * @param input Retained storage scope.
3917
+ * @param history Indexed Agent history handle.
3918
+ * @param runtime Current context services.
3919
+ * @param lifetime Repository storage lifetime.
3920
+ * @returns History binding with an invocation-time actor snapshot.
3921
+ */
3922
+ agentHistoryBinding(repository, entity, entityId, input, history, runtime, lifetime) {
3923
+ return {
4424
3924
  storage: history,
4425
3925
  entityId,
4426
- scope: {
4427
- context: input.context.name,
4428
- tenant: JSON.stringify(input.context.tenantId ?? null),
4429
- repository: input.sourceType.typeName,
4430
- entity: input.id.key(entityId),
3926
+ onAdmit: async (onRead) => {
3927
+ if (repositoryRuntimes.get(repository) !== runtime)
3928
+ throw new Error("Agent history requires its active runtime.");
3929
+ await runtime.onAdmit(input.context.tenantId);
3930
+ return RepositoryStorage.track(repository, onRead, lifetime);
4431
3931
  },
4432
- });
3932
+ captureActor: () => entityHistoryAccess.currentActor(entity),
3933
+ filterPage: (page, view, request, actorContext) => RepositoryAgentReads.visibleHistory(runtime.stand, page, entityId, view, request, input.context.tenantId, actorContext === undefined ? {} : { actorContext }),
3934
+ scope: this.agentHistoryScope(input, entityId),
3935
+ };
3936
+ },
3937
+ /**
3938
+ * Returns the exact retained Agent repository and typed ID for cursor validation.
3939
+ * @param input Captured Agent storage scope.
3940
+ * @param entityId Typed Agent identifier.
3941
+ * @returns Stable scope encoded in history cursors.
3942
+ */
3943
+ agentHistoryScope(input, entityId) {
3944
+ return {
3945
+ context: input.context.name,
3946
+ tenant: JSON.stringify(input.context.tenantId ?? null),
3947
+ repository: input.sourceType.typeName,
3948
+ entity: input.id.key(entityId),
3949
+ };
4433
3950
  },
4434
3951
  /**
4435
3952
  * Restores an Entity from Stand or creates a fresh instance at Version zero.
@@ -4535,6 +4052,34 @@ Object.freeze(RepositoryEntities);
4535
4052
  * Internal repository signals operations.
4536
4053
  */
4537
4054
  const RepositorySignals = {
4055
+ /**
4056
+ * Packs one handler-produced signal and records only its validation failure.
4057
+ *
4058
+ * @typeParam Schema Generated schema for the signal.
4059
+ * @param schema Generated schema for the signal.
4060
+ * @param message Handler-produced message.
4061
+ * @returns Validated packed signal.
4062
+ */
4063
+ packProduced(schema, message) {
4064
+ try {
4065
+ return RepositorySignals.packValidated(schema, message);
4066
+ }
4067
+ catch (error) {
4068
+ throw receptionObservation.recordPostHandlerFailure(error);
4069
+ }
4070
+ },
4071
+ /**
4072
+ * Packs a repository-produced value after checking its domain constraints.
4073
+ *
4074
+ * @typeParam Schema Generated schema for the value.
4075
+ * @param schema Generated schema for the value.
4076
+ * @param message Repository-produced message to pack.
4077
+ * @returns Validated packed message.
4078
+ */
4079
+ packValidated(schema, message) {
4080
+ Validate.check(schema, message);
4081
+ return AnyPacker.pack(schema, message);
4082
+ },
4538
4083
  /**
4539
4084
  * Validates that a rejection type was declared by the invoked handler.
4540
4085
  *
@@ -4622,7 +4167,7 @@ const RepositorySignals = {
4622
4167
  const metadata = runtime.signalMetadata.eventFromCommand(command, {});
4623
4168
  return create(EventSchema, {
4624
4169
  id: metadata.id,
4625
- message: AnyMessages.pack(rejection.schema, rejection.messageThrown()),
4170
+ message: RepositorySignals.packValidated(rejection.schema, rejection.messageThrown()),
4626
4171
  context: create(EventContextSchema, {
4627
4172
  ...RepositorySignals.eventContextWithProducer(metadata.context, repository, entityId),
4628
4173
  rejection: create(RejectionEventContextSchema, {
@@ -4686,7 +4231,7 @@ class EntityStateChangePublishing {
4686
4231
  this.#publish(runtime, () => runtime.signalMetadata.eventFromCommand(command, {
4687
4232
  version,
4688
4233
  }), {
4689
- id: AnyMessages.pack(CommandIdSchema, command.id),
4234
+ id: RepositorySignals.packValidated(CommandIdSchema, command.id),
4690
4235
  typeUrl: command.message?.typeUrl ?? "",
4691
4236
  }, { repository, entityId, oldState, oldLifecycle, newState, lifecycle, version });
4692
4237
  }
@@ -4707,7 +4252,7 @@ class EntityStateChangePublishing {
4707
4252
  this.#publish(runtime, () => runtime.signalMetadata.eventFromEvent(source, {
4708
4253
  version,
4709
4254
  }), {
4710
- id: AnyMessages.pack(EventIdSchema, source.id),
4255
+ id: RepositorySignals.packValidated(EventIdSchema, source.id),
4711
4256
  typeUrl: source.message?.typeUrl ?? "",
4712
4257
  }, { repository, entityId, oldState, oldLifecycle, newState, lifecycle, version });
4713
4258
  }
@@ -4726,7 +4271,7 @@ class EntityStateChangePublishing {
4726
4271
  runtime.registerSystemEventSchema(draft.schema);
4727
4272
  const event = create(EventSchema, {
4728
4273
  id: metadata.id,
4729
- message: AnyMessages.pack(draft.schema, draft.messageAt(metadata.context.timestamp)),
4274
+ message: RepositorySignals.packValidated(draft.schema, draft.messageAt(metadata.context.timestamp)),
4730
4275
  context: RepositorySignals.eventContextWithProducer(metadata.context, change.repository, change.entityId),
4731
4276
  });
4732
4277
  this.#post(runtime, event);
@@ -4760,7 +4305,7 @@ class EntityStateChangePublishing {
4760
4305
  return {
4761
4306
  entity,
4762
4307
  signalId: [create(MessageIdSchema, { id: origin.id, typeUrl: origin.typeUrl })],
4763
- state: AnyMessages.pack(change.repository.stateSchema, change.newState),
4308
+ state: RepositorySignals.packValidated(change.repository.stateSchema, change.newState),
4764
4309
  version: create(VersionSchema, { number: change.version }),
4765
4310
  };
4766
4311
  }
@@ -4803,7 +4348,7 @@ class EntityStateChangePublishing {
4803
4348
  ...(change.oldState === undefined
4804
4349
  ? {}
4805
4350
  : {
4806
- oldState: AnyMessages.pack(change.repository.stateSchema, change.oldState),
4351
+ oldState: RepositorySignals.packValidated(change.repository.stateSchema, change.oldState),
4807
4352
  }),
4808
4353
  newState: fields.state,
4809
4354
  signalId: fields.signalId,
@@ -4968,7 +4513,7 @@ class HandlerDispatchPublishing {
4968
4513
  });
4969
4514
  return create(EventSchema, {
4970
4515
  id: runtime.signalMetadata.eventId(),
4971
- message: AnyMessages.pack(EntityLog.CommandDispatchedToHandlerSchema, this.#message(repository, command, entityId, context.timestamp)),
4516
+ message: RepositorySignals.packValidated(EntityLog.CommandDispatchedToHandlerSchema, this.#message(repository, command, entityId, context.timestamp)),
4972
4517
  context,
4973
4518
  });
4974
4519
  }
@@ -5018,7 +4563,7 @@ class HandlerDispatchPublishing {
5018
4563
  });
5019
4564
  return create(EventSchema, {
5020
4565
  id: runtime.signalMetadata.eventId(),
5021
- message: AnyMessages.pack(EntityLog.EventDispatchedToReactorSchema, this.#eventMessage(EntityLog.EventDispatchedToReactorSchema, repository, source, entityId, context.timestamp)),
4566
+ message: RepositorySignals.packValidated(EntityLog.EventDispatchedToReactorSchema, this.#eventMessage(EntityLog.EventDispatchedToReactorSchema, repository, source, entityId, context.timestamp)),
5022
4567
  context,
5023
4568
  });
5024
4569
  }
@@ -5047,7 +4592,7 @@ class HandlerDispatchPublishing {
5047
4592
  });
5048
4593
  const diagnostic = create(EventSchema, {
5049
4594
  id: runtime.signalMetadata.eventId(),
5050
- message: AnyMessages.pack(schema, this.#eventMessage(schema, repository, event, entityId, context.timestamp)),
4595
+ message: RepositorySignals.packValidated(schema, this.#eventMessage(schema, repository, event, entityId, context.timestamp)),
5051
4596
  context,
5052
4597
  });
5053
4598
  this.#post(runtime, schema, diagnostic);
@@ -5136,6 +4681,7 @@ const HandlerDispatchPublisher = Object.freeze(new HandlerDispatchPublishing());
5136
4681
  Object.freeze(RepositorySignals);
5137
4682
  /**
5138
4683
  * Internal repository stand operations.
4684
+ *
5139
4685
  * @internal
5140
4686
  */
5141
4687
  export const RepositoryStand = {
@@ -5168,60 +4714,12 @@ export const RepositoryStand = {
5168
4714
  lifecycle,
5169
4715
  });
5170
4716
  },
5171
- /**
5172
- * Maps one Event position to its Process Manager producer version.
5173
- *
5174
- * @param sequence One-based position among produced Events.
5175
- * @returns Producer sequence number for Event metadata.
5176
- */
5177
4717
  };
5178
4718
  Object.freeze(RepositoryStand);
5179
4719
  /**
5180
4720
  * Internal repository tenants operations.
5181
4721
  */
5182
4722
  const RepositoryTenants = {
5183
- /**
5184
- * Builds tenant-aware storage context for Entity inbox delivery.
5185
- *
5186
- * @param context Bounded context storage mode.
5187
- * @param tenantId Delivery tenant when multitenant.
5188
- * @returns Storage context for durable Entity inbox rows.
5189
- */
5190
- entityInboxDeliveryContext(context, tenantId) {
5191
- if (!context.multitenant) {
5192
- return Object.freeze({ name: context.name, multitenant: false });
5193
- }
5194
- const tid = tenantId;
5195
- if (tid === undefined) {
5196
- throw new Error(`Multitenant Entity Inbox handoff for "${context.name}" requires tenantId.`);
5197
- }
5198
- return Object.freeze({
5199
- name: context.name,
5200
- multitenant: true,
5201
- tenantId: RepositoryTenants.require(tid),
5202
- });
5203
- },
5204
- /**
5205
- * Builds tenant-aware storage context for Projection inbox delivery.
5206
- *
5207
- * @param context Bounded context storage mode.
5208
- * @param tenantId Delivery tenant when multitenant.
5209
- * @returns Storage context for durable Projection inbox rows.
5210
- */
5211
- projectionDeliveryContext(context, tenantId) {
5212
- if (!context.multitenant) {
5213
- return Object.freeze({ name: context.name, multitenant: false });
5214
- }
5215
- const tid = tenantId;
5216
- if (tid === undefined) {
5217
- throw new Error(`Multitenant projection inbox handoff for "${context.name}" requires tenantId.`);
5218
- }
5219
- return Object.freeze({
5220
- name: context.name,
5221
- multitenant: true,
5222
- tenantId: RepositoryTenants.require(tid),
5223
- });
5224
- },
5225
4723
  /**
5226
4724
  * Validates an Event tenant before Projection inbox handoff.
5227
4725
  *
@@ -5428,9 +4926,15 @@ const RepositoryHandlers = {
5428
4926
  requireDeclaredOutputs(handler, signals) {
5429
4927
  const schemas = HandlerMetadataValues.returnedSchemas(handler);
5430
4928
  for (const signal of signals) {
5431
- const typeName = EntityInvocation.messageTypeName(signal);
4929
+ let typeName;
4930
+ try {
4931
+ typeName = EntityInvocation.messageTypeName(signal);
4932
+ }
4933
+ catch (error) {
4934
+ throw receptionObservation.recordPostHandlerFailure(error);
4935
+ }
5432
4936
  if (!schemas.some((schema) => schema.typeName === typeName)) {
5433
- throw new Error(`Handler "${handler.methodName}" returned undeclared message "${typeName}".`);
4937
+ throw receptionObservation.recordPostHandlerFailure(new Error(`Handler "${handler.methodName}" returned undeclared message "${typeName}".`));
5434
4938
  }
5435
4939
  }
5436
4940
  },
@@ -5971,7 +5475,7 @@ const RepositoryRoutes = {
5971
5475
  * @returns Candidate target Entity ID.
5972
5476
  */
5973
5477
  async callCommandRoute(route, message, schema, context, reads) {
5974
- const unpacked = AnyMessages.unpack(message, schema);
5478
+ const unpacked = AnyPacker.tryUnpack(message, schema);
5975
5479
  if (unpacked === undefined) {
5976
5480
  throw new Error("Repository command routing requires a readable Command message.");
5977
5481
  }
@@ -6135,7 +5639,7 @@ const RepositoryRoutes = {
6135
5639
  if (message?.typeUrl !== TypeUrls.derive(EntityLog.EntityStateChangedSchema)) {
6136
5640
  throw new Error(`${operation} requires an EntityStateChanged System event.`);
6137
5641
  }
6138
- const changed = AnyMessages.unpack(message, EntityLog.EntityStateChangedSchema);
5642
+ const changed = AnyPacker.tryUnpack(message, EntityLog.EntityStateChangedSchema);
6139
5643
  const packedState = changed?.newState;
6140
5644
  if (packedState === undefined || packedState.typeUrl === "") {
6141
5645
  throw new Error(`${operation} requires EntityStateChanged.newState.`);
@@ -6143,7 +5647,7 @@ const RepositoryRoutes = {
6143
5647
  const schema = schemas.find((candidate) => TypeUrls.derive(candidate) === packedState.typeUrl);
6144
5648
  if (schema === undefined)
6145
5649
  return undefined;
6146
- const state = AnyMessages.unpack(packedState, schema);
5650
+ const state = AnyPacker.tryUnpack(packedState, schema);
6147
5651
  if (state === undefined)
6148
5652
  throw new Error(`${operation} requires a readable Entity state.`);
6149
5653
  return Object.freeze({ schema, state });
@@ -6186,7 +5690,7 @@ const RepositoryRoutes = {
6186
5690
  * @returns Validated target Entity IDs.
6187
5691
  */
6188
5692
  async callEventRoute(route, message, schema, context, targetIdField, reads) {
6189
- const unpacked = AnyMessages.unpack(message, schema);
5693
+ const unpacked = AnyPacker.tryUnpack(message, schema);
6190
5694
  if (unpacked === undefined) {
6191
5695
  throw new Error("Repository event routing requires a readable Event message.");
6192
5696
  }
@@ -6240,7 +5744,7 @@ const RepositoryRoutes = {
6240
5744
  * @returns Nonempty first-field value.
6241
5745
  */
6242
5746
  readFirstFieldId(message, schema, signalKind) {
6243
- const unpacked = AnyMessages.unpack(message, schema);
5747
+ const unpacked = AnyPacker.tryUnpack(message, schema);
6244
5748
  const firstField = schema.fields[0];
6245
5749
  if (unpacked === undefined || firstField === undefined) {
6246
5750
  throw new Error(`Repository ${signalKind} routing requires a readable first field.`);
@@ -6479,12 +5983,235 @@ const RepositoryRoutes = {
6479
5983
  return undefined;
6480
5984
  }
6481
5985
  },
6482
- };
6483
- Object.freeze(RepositoryRoutes);
6484
- /**
6485
- * Internal repository storage operations.
6486
- */
6487
- const RepositoryStorage = {
5986
+ };
5987
+ Object.freeze(RepositoryRoutes);
5988
+ /**
5989
+ * Internal repository storage operations.
5990
+ */
5991
+ const RepositoryStorage = {
5992
+ /**
5993
+ * Returns one retained EventStore for unchanged Process Manager output.
5994
+ *
5995
+ * @param repository Registered Process Manager repository.
5996
+ * @param runtime Its captured runtime services.
5997
+ * @returns Tenant-aware EventStore for produced Events.
5998
+ */
5999
+ eventStore(repository, runtime) {
6000
+ const lifetime = this.lifetime(repository);
6001
+ lifetime.eventStore ??= new EventStore(runtime.context, runtime.storageFactory);
6002
+ return lifetime.eventStore;
6003
+ },
6004
+ /**
6005
+ * Creates one repository runtime's retained storage registry.
6006
+ *
6007
+ * @returns Empty registry accepting storage work.
6008
+ */
6009
+ newLifetime() {
6010
+ return {
6011
+ entities: new Map(),
6012
+ histories: new Map(),
6013
+ ports: new Set(),
6014
+ commits: new Set(),
6015
+ active: new Set(),
6016
+ open: true,
6017
+ };
6018
+ },
6019
+ /**
6020
+ * Returns the active storage registry for one registered repository.
6021
+ *
6022
+ * @param repository Registered repository.
6023
+ * @returns Its current retained-storage registry.
6024
+ */
6025
+ lifetime(repository) {
6026
+ const lifetime = repositoryStorageLifetimes.get(repository);
6027
+ if (lifetime?.open !== true)
6028
+ throw new Error("Repository storage requires an active runtime.");
6029
+ return lifetime;
6030
+ },
6031
+ /**
6032
+ * Admits one complete physical storage operation before runtime closure.
6033
+ *
6034
+ * @typeParam T Result of the operation.
6035
+ * @param repository Registered repository.
6036
+ * @param work Work that acquires and uses retained storage.
6037
+ * @param expected Captured lifetime for a bound history reader, if any.
6038
+ * @returns The operation result or its original failure.
6039
+ */
6040
+ async track(repository, work, expected) {
6041
+ const lifetime = this.lifetime(repository);
6042
+ if (expected !== undefined && lifetime !== expected)
6043
+ throw new Error("Repository history requires its active runtime.");
6044
+ const result = work();
6045
+ lifetime.active.add(result);
6046
+ return result.finally(() => {
6047
+ lifetime.active.delete(result);
6048
+ });
6049
+ },
6050
+ /**
6051
+ * Starts repository shutdown after new admissions are fenced.
6052
+ *
6053
+ * @param repository Repository being detached.
6054
+ * @returns Completion after admitted operations and every close attempt.
6055
+ */
6056
+ closeRuntime(repository) {
6057
+ const lifetime = repositoryStorageLifetimes.get(repository);
6058
+ if (lifetime === undefined)
6059
+ return Promise.resolve();
6060
+ if (lifetime.closing !== undefined)
6061
+ return lifetime.closing;
6062
+ if (lifetime.idleOutcome !== undefined) {
6063
+ const error = lifetime.idleOutcome.error;
6064
+ return error === undefined ? Promise.resolve() : Promise.reject(error);
6065
+ }
6066
+ lifetime.open = false;
6067
+ lifetime.closing = this.drainLifetime(lifetime);
6068
+ return lifetime.closing;
6069
+ },
6070
+ /**
6071
+ * Rejects synchronous rollback while repository storage work remains active.
6072
+ *
6073
+ * @param repository Repository being rolled back.
6074
+ */
6075
+ assertIdle(repository) {
6076
+ const lifetime = repositoryStorageLifetimes.get(repository);
6077
+ if (lifetime !== undefined && (lifetime.active.size > 0 || lifetime.closing !== undefined))
6078
+ throw new Error("Repository idle cleanup requires no active storage work.");
6079
+ },
6080
+ /**
6081
+ * Closes retained resources synchronously during idle assembly rollback.
6082
+ *
6083
+ * @param repository Repository being rolled back.
6084
+ */
6085
+ closeRuntimeIdle(repository) {
6086
+ const lifetime = repositoryStorageLifetimes.get(repository);
6087
+ if (lifetime === undefined)
6088
+ return;
6089
+ if (lifetime.idleOutcome !== undefined) {
6090
+ if (lifetime.idleOutcome.error !== undefined)
6091
+ throw lifetime.idleOutcome.error;
6092
+ return;
6093
+ }
6094
+ lifetime.open = false;
6095
+ const errors = [];
6096
+ if (lifetime.eventStore !== undefined) {
6097
+ try {
6098
+ eventStoreAccess.closeIdle(lifetime.eventStore);
6099
+ }
6100
+ catch (error) {
6101
+ errors.push(error);
6102
+ }
6103
+ }
6104
+ errors.push(...this.closeLifetimePortsIdle(lifetime));
6105
+ const failure = this.closeFailure(errors);
6106
+ lifetime.idleOutcome = failure === undefined ? {} : { error: failure };
6107
+ if (failure !== undefined)
6108
+ throw failure;
6109
+ },
6110
+ /**
6111
+ * Waits for admitted storage operations, closes the retained EventStore, and releases all ports.
6112
+ *
6113
+ * @param lifetime Repository storage registry being closed.
6114
+ * @returns Completion after every close attempt.
6115
+ */
6116
+ async drainLifetime(lifetime) {
6117
+ await Promise.allSettled([...lifetime.active]);
6118
+ const errors = [];
6119
+ if (lifetime.eventStore !== undefined) {
6120
+ try {
6121
+ await lifetime.eventStore.close();
6122
+ }
6123
+ catch (error) {
6124
+ errors.push(error);
6125
+ }
6126
+ }
6127
+ errors.push(...(await this.closeLifetimePorts(lifetime)));
6128
+ const failure = this.closeFailure(errors);
6129
+ if (failure !== undefined)
6130
+ throw failure;
6131
+ },
6132
+ /**
6133
+ * Closes each retained physical port once.
6134
+ *
6135
+ * @param lifetime Repository storage registry being closed.
6136
+ * @returns Every close failure in attempt order.
6137
+ */
6138
+ async closeLifetimePorts(lifetime) {
6139
+ const commits = [...lifetime.commits];
6140
+ lifetime.commits.clear();
6141
+ const ports = [...lifetime.ports];
6142
+ lifetime.ports.clear();
6143
+ const errors = [];
6144
+ const closing = commits.map((commit) => {
6145
+ try {
6146
+ return commit.close();
6147
+ }
6148
+ catch (error) {
6149
+ errors.push(error);
6150
+ return Promise.resolve();
6151
+ }
6152
+ });
6153
+ for (const result of await Promise.allSettled(closing))
6154
+ if (result.status === "rejected")
6155
+ errors.push(result.reason);
6156
+ for (const port of ports) {
6157
+ try {
6158
+ port.close();
6159
+ }
6160
+ catch (error) {
6161
+ errors.push(error);
6162
+ }
6163
+ }
6164
+ lifetime.entities.clear();
6165
+ lifetime.histories.clear();
6166
+ delete lifetime.eventStore;
6167
+ return errors;
6168
+ },
6169
+ /**
6170
+ * Closes ports synchronously during assembly rollback.
6171
+ *
6172
+ * @param lifetime The captured repository storage lifetime.
6173
+ * @returns Synchronous cleanup failures, if any.
6174
+ */
6175
+ closeLifetimePortsIdle(lifetime) {
6176
+ const commits = [...lifetime.commits];
6177
+ lifetime.commits.clear();
6178
+ const ports = [...lifetime.ports];
6179
+ lifetime.ports.clear();
6180
+ lifetime.entities.clear();
6181
+ lifetime.histories.clear();
6182
+ delete lifetime.eventStore;
6183
+ const errors = [];
6184
+ for (const commit of commits) {
6185
+ try {
6186
+ commit.closeIdle();
6187
+ }
6188
+ catch (error) {
6189
+ errors.push(error);
6190
+ }
6191
+ }
6192
+ for (const port of ports) {
6193
+ try {
6194
+ port.close();
6195
+ }
6196
+ catch (error) {
6197
+ errors.push(error);
6198
+ }
6199
+ }
6200
+ return errors;
6201
+ },
6202
+ /**
6203
+ * Returns one close failure or aggregates multiple failures.
6204
+ *
6205
+ * @param errors Physical port cleanup failures.
6206
+ * @returns The reported cleanup failure, if any.
6207
+ */
6208
+ closeFailure(errors) {
6209
+ if (errors.length === 0)
6210
+ return undefined;
6211
+ if (errors.length === 1)
6212
+ return errors[0];
6213
+ return new AggregateError(errors, "Repository storage close failed.");
6214
+ },
6488
6215
  /**
6489
6216
  * Opens provider Entity storage for one read scope without a commit handle.
6490
6217
  *
@@ -6501,72 +6228,120 @@ const RepositoryStorage = {
6501
6228
  return candidate.createEntityStorage(input);
6502
6229
  },
6503
6230
  /**
6504
- * Opens Entity storage and an atomic commit port from a storage factory.
6231
+ * Returns the canonical tenant/model/history bucket for Entity storage.
6505
6232
  *
6506
- * @typeParam I Entity identifier type.
6507
- * @typeParam S Generated Entity state type.
6508
- * @param factory Provider storage factory.
6509
- * @param input Entity storage location and schema.
6510
- * @param agent Whether to open mandatory Agent history.
6511
- * @returns Storage ports closed together by one handle.
6233
+ * @typeParam I Typed Entity identifier.
6234
+ * @typeParam S Generated Entity state.
6235
+ * @param lifetime Active repository storage registry.
6236
+ * @param input Captured Entity storage configuration.
6237
+ * @returns Slots for one model and history configuration.
6512
6238
  */
6513
- openEntityStorage(factory, input, agent = false) {
6514
- const candidate = factory;
6515
- if (candidate.createEntityStorage === undefined)
6516
- throw new Error("StorageFactory does not provide required atomic Entity commit storage.");
6517
- const entity = candidate.createEntityStorage(input);
6518
- const commits = EntityCommitStorageFactories.create(factory, input);
6519
- let ports;
6520
- try {
6521
- if (agent)
6522
- ports = this.openAgentPorts(factory, input);
6523
- }
6524
- catch (error) {
6525
- commits.close();
6526
- entity.close();
6527
- throw error;
6239
+ entityBucket(lifetime, input) {
6240
+ const key = JSON.stringify({
6241
+ context: input.context.name,
6242
+ state: input.sourceType.typeName,
6243
+ history: input.stateHistory,
6244
+ });
6245
+ let bucket = lifetime.entities.get(key);
6246
+ if (bucket === undefined) {
6247
+ bucket = new Map();
6248
+ lifetime.entities.set(key, bucket);
6528
6249
  }
6529
- return {
6530
- ...entity,
6531
- commits,
6532
- ...ports,
6533
- close: () => {
6534
- ports?.agentExecution.close();
6535
- ports?.agentHistory.close();
6536
- commits.close();
6537
- entity.close();
6538
- },
6539
- };
6250
+ return bucket;
6540
6251
  },
6541
6252
  /**
6542
- * Opens both mandatory Agent ports and closes the first if the second fails.
6253
+ * Returns the canonical tenant/model bucket for Agent audit history.
6543
6254
  *
6544
6255
  * @typeParam I Typed Agent identifier.
6545
- * @typeParam S Generated Agent state message.
6546
- * @param factory Storage factory registered with the Bounded Context.
6547
- * @param input Original Inbox or storage input.
6548
- * @returns Indexed history and execution handles for the same tenant.
6256
+ * @typeParam S Generated Agent state.
6257
+ * @param lifetime Active repository storage registry.
6258
+ * @param input Captured Agent storage configuration.
6259
+ * @returns Audit ports independent of the state-history switch.
6260
+ */
6261
+ historyBucket(lifetime, input) {
6262
+ const key = JSON.stringify({ context: input.context.name, state: input.sourceType.typeName });
6263
+ let bucket = lifetime.histories.get(key);
6264
+ if (bucket === undefined) {
6265
+ bucket = new Map();
6266
+ lifetime.histories.set(key, bucket);
6267
+ }
6268
+ return bucket;
6269
+ },
6270
+ /**
6271
+ * Returns retained read-only Entity storage without acquiring a commit port.
6272
+ *
6273
+ * @typeParam I Typed Entity identifier.
6274
+ * @typeParam S Generated Entity state.
6275
+ * @param repository Registered repository.
6276
+ * @param factory Repository storage factory.
6277
+ * @param input Captured Entity storage configuration.
6278
+ * @returns Reusable current, state-history, and event-history ports.
6549
6279
  */
6550
- openAgentPorts(factory, input) {
6551
- const agentHistory = AgentHistoryStorageFactories.create(factory, {
6280
+ repositoryReadEntityStorage(repository, factory, input) {
6281
+ const lifetime = this.lifetime(repository);
6282
+ const bucket = this.entityBucket(lifetime, input);
6283
+ const key = TenantBoundary.of(input.context).key;
6284
+ const existing = bucket.get(key);
6285
+ if (existing !== undefined)
6286
+ return existing.entity;
6287
+ const entity = this.openReadEntityStorage(factory, input);
6288
+ bucket.set(key, { entity });
6289
+ lifetime.ports.add(entity);
6290
+ return entity;
6291
+ },
6292
+ /**
6293
+ * Returns shared Agent audit history for one tenant and model.
6294
+ *
6295
+ * @typeParam I Typed Agent identifier.
6296
+ * @typeParam S Generated Agent state.
6297
+ * @param repository Registered Agent repository.
6298
+ * @param factory Repository storage factory.
6299
+ * @param input Captured Entity storage configuration.
6300
+ * @returns Reusable Agent audit-history port.
6301
+ */
6302
+ openRepositoryAgentHistory(repository, factory, input) {
6303
+ const lifetime = this.lifetime(repository);
6304
+ const bucket = this.historyBucket(lifetime, input);
6305
+ const key = TenantBoundary.of(input.context).key;
6306
+ const existing = bucket.get(key);
6307
+ if (existing !== undefined)
6308
+ return existing;
6309
+ const history = AgentHistoryStorageFactories.create(factory, {
6552
6310
  context: input.context,
6553
6311
  stateType: input.sourceType.typeName,
6554
6312
  id: { key: input.id.key },
6555
6313
  });
6556
- try {
6557
- const agentExecution = AgentExecutionStorageFactories.create(factory, {
6558
- entity: input,
6559
- stateType: input.sourceType.typeName,
6560
- });
6561
- return { agentHistory, agentExecution };
6562
- }
6563
- catch (error) {
6564
- agentHistory.close();
6565
- throw error;
6314
+ bucket.set(key, history);
6315
+ lifetime.ports.add(history);
6316
+ return history;
6317
+ },
6318
+ /**
6319
+ * Closes every acquired port, retaining the first failure and any later failures.
6320
+ *
6321
+ * @param ports Acquired ports in close order.
6322
+ * @param acquisitionError Earlier acquisition failure, if any.
6323
+ */
6324
+ closePorts(ports, acquisitionError) {
6325
+ const errors = acquisitionError === undefined ? [] : [acquisitionError];
6326
+ for (const port of ports) {
6327
+ try {
6328
+ if ("closeIdle" in port)
6329
+ port.closeIdle();
6330
+ else
6331
+ port.close();
6332
+ }
6333
+ catch (error) {
6334
+ errors.push(error);
6335
+ }
6566
6336
  }
6337
+ if (errors.length === 1)
6338
+ throw errors[0];
6339
+ if (errors.length > 1)
6340
+ throw new AggregateError(errors, "Entity storage cleanup failed.");
6567
6341
  },
6568
6342
  /**
6569
- * Returns one shared Entity storage handle per context and state type.
6343
+ * Returns one retained Entity storage handle per context, tenant, state type,
6344
+ * and captured state-history configuration.
6570
6345
  *
6571
6346
  * @typeParam I Entity identifier type.
6572
6347
  * @typeParam S Generated Entity state type.
@@ -6576,20 +6351,72 @@ const RepositoryStorage = {
6576
6351
  * @returns Shared Entity storage handle.
6577
6352
  */
6578
6353
  openRepositoryEntityStorage(repository, factory, input) {
6579
- const key = JSON.stringify({ context: input.context, state: input.sourceType.typeName });
6580
- let handles = repositoryEntityHandles.get(repository);
6581
- const handle = RepositoryStorage.openEntityStorage(factory, input, repository.entityFamily === "agent");
6582
- if (handles === undefined) {
6583
- handles = new Map();
6584
- repositoryEntityHandles.set(repository, handles);
6585
- }
6586
- const existing = handles.get(key);
6587
- if (existing !== undefined) {
6588
- handle.close();
6589
- return existing;
6354
+ const lifetime = this.lifetime(repository);
6355
+ const bucket = this.entityBucket(lifetime, input);
6356
+ const key = TenantBoundary.of(input.context).key;
6357
+ const existing = bucket.get(key);
6358
+ if (existing?.full !== undefined)
6359
+ return existing.full;
6360
+ return this.upgradeRepositoryEntityStorage(repository, factory, input, lifetime, bucket, key, existing);
6361
+ },
6362
+ /**
6363
+ * Adds commit and Agent ports to a retained or newly acquired Entity port.
6364
+ *
6365
+ * @typeParam I Typed Entity identifier.
6366
+ * @typeParam S Generated Entity state.
6367
+ * @param repository Registered repository.
6368
+ * @param factory Repository storage factory.
6369
+ * @param input Captured Entity storage configuration.
6370
+ * @param lifetime Active repository registry.
6371
+ * @param bucket Entity slots for this model and history configuration.
6372
+ * @param key Canonical tenant key.
6373
+ * @param existing Read-only slot if one was opened earlier.
6374
+ * @returns Complete retained write storage.
6375
+ */
6376
+ upgradeRepositoryEntityStorage(repository, factory, input, lifetime, bucket, key, existing) {
6377
+ const entity = existing?.entity ?? this.openReadEntityStorage(factory, input);
6378
+ let commits;
6379
+ try {
6380
+ const commit = EntityCommitStorageFactories.create(factory, input);
6381
+ commits = commit;
6382
+ const agentHistory = repository.entityFamily === "agent"
6383
+ ? this.openRepositoryAgentHistory(repository, factory, input)
6384
+ : undefined;
6385
+ const full = {
6386
+ ...entity,
6387
+ commits: commit,
6388
+ ...(agentHistory === undefined ? {} : { agentHistory }),
6389
+ };
6390
+ this.retainUpgradedSlot(lifetime, bucket, key, existing, full, entity, commit);
6391
+ return full;
6392
+ }
6393
+ catch (error) {
6394
+ const rollback = commits === undefined ? [] : [commits];
6395
+ if (existing === undefined)
6396
+ rollback.push(entity);
6397
+ this.closePorts(rollback, error);
6398
+ throw error;
6590
6399
  }
6591
- handles.set(key, handle);
6592
- return handle;
6400
+ },
6401
+ /**
6402
+ * Records a completed Entity storage upgrade in the current lifetime.
6403
+ *
6404
+ * @typeParam I Typed Entity identifier.
6405
+ * @typeParam S Generated Entity state.
6406
+ * @param lifetime Active repository registry.
6407
+ * @param bucket Entity slots for one model and history configuration.
6408
+ * @param key Canonical tenant key.
6409
+ * @param existing Previously retained read-only slot, if any.
6410
+ * @param full Completed Entity storage.
6411
+ * @param entity Shared Entity read port.
6412
+ * @param commit Newly acquired commit port.
6413
+ */
6414
+ retainUpgradedSlot(lifetime, bucket, key, existing, full, entity, commit) {
6415
+ const slot = existing ?? { entity };
6416
+ slot.full = full;
6417
+ bucket.set(key, slot);
6418
+ lifetime.ports.add(entity);
6419
+ lifetime.commits.add(commit);
6593
6420
  },
6594
6421
  /**
6595
6422
  * Builds Entity storage input with configured state and Event history flags.
@@ -6599,12 +6426,15 @@ const RepositoryStorage = {
6599
6426
  * @returns Entity storage description for the provider.
6600
6427
  */
6601
6428
  entityStorageInput(repository, context) {
6602
- const descriptor = entityStorageDescriptor(context, repositorySpecScanner.scan(repository.entityType, repository.metadata), repository.metadata);
6429
+ const snapshot = repositorySnapshots.get(repository);
6430
+ if (snapshot === undefined)
6431
+ throw new TypeError("Entity storage requires a Repository instance.");
6432
+ const descriptor = entityStorageDescriptor(context, repositorySpecScanner.scan(snapshot.entityType, snapshot.metadata), snapshot.metadata);
6603
6433
  const history = RepositoryStorage.historyConfiguration(repository);
6604
6434
  return {
6605
6435
  ...descriptor,
6606
6436
  stateHistory: history.stateHistory,
6607
- eventHistory: repository.entityFamily === "aggregate" || history.processManagerEventHistory,
6437
+ eventHistory: snapshot.entityFamily === "aggregate" || history.processManagerEventHistory,
6608
6438
  };
6609
6439
  },
6610
6440
  /**
@@ -6653,29 +6483,242 @@ const RepositoryStorage = {
6653
6483
  };
6654
6484
  Object.freeze(RepositoryStorage);
6655
6485
  /**
6656
- * Internal repository history internals operations.
6486
+ * Provides internal repository history operations.
6657
6487
  */
6658
6488
  const RepositoryHistoryInternals = {
6659
6489
  /**
6660
6490
  * Binds state and Event history readers to one restored Entity instance.
6661
6491
  *
6492
+ * @param repository Registered repository admitting history reads.
6662
6493
  * @param entity Instance receiving the history accessors.
6663
6494
  * @param storage Entity storage used for reads and maintenance.
6664
6495
  * @param entityId Identifier used to select history records.
6665
6496
  * @param schema Schema used to unpack state records.
6497
+ * @param tenantId Captured tenant for later live membership checks.
6666
6498
  */
6667
- bindEntityHistory(entity, storage, entityId, schema) {
6668
- const stateCache = RepositoryHistoryInternals.createHistoryCache((depth, startingFromVersion) => storage.states.backward(entityId, depth, startingFromVersion), (record) => BigInt(EntityRecords.unpack(schema, record).versionMessage.number), { requireDescendingVersions: true });
6499
+ bindEntityHistory(repository, entity, storage, entityId, schema, tenantId) {
6500
+ const lifetime = RepositoryStorage.lifetime(repository);
6501
+ const runtime = repositoryRuntimes.get(repository);
6502
+ if (runtime === undefined)
6503
+ throw new Error("Repository history requires its active runtime.");
6504
+ const onAdmit = () => this.admitHistory(repository, runtime, tenantId);
6505
+ const { stateCache, eventCache } = this.boundHistoryCaches(storage, entityId, schema);
6669
6506
  entityStateHistoryCaches.set(entity, stateCache);
6670
- const eventCache = RepositoryHistoryInternals.createHistoryCache((depth, startingFromVersion) => storage.events.backward(entityId, depth, startingFromVersion), (event) => RepositorySignals.readEventVersion(event), { cacheCompleteVersionGroups: true });
6671
6507
  entityHistoryAccess.bind(entity, {
6672
- stateAt: async (time) => RepositoryHistoryInternals.cloneHistoryState(await storage.states.stateAt(entityId, time)),
6673
- states: async (depth) => RepositoryHistoryInternals.freezeHistoryStates((await stateCache.read(depth)).map((record) => EntityRecords.unpack(schema, record).state)),
6674
- events: async (depth) => RepositoryHistoryInternals.freezeHistoryEvents(await eventCache.read(depth)),
6675
- stateMaintenance: storage.states,
6676
- eventMaintenance: storage.events,
6508
+ ...this.historyReaders({
6509
+ repository,
6510
+ lifetime,
6511
+ stand: runtime.stand,
6512
+ storage,
6513
+ schema,
6514
+ entityId,
6515
+ tenantId,
6516
+ entity,
6517
+ onAdmit,
6518
+ stateCache,
6519
+ eventCache,
6520
+ }),
6521
+ stateMaintenance: this.boundStateMaintenance(repository, lifetime, storage.states, onAdmit),
6522
+ eventMaintenance: this.boundEventMaintenance(repository, lifetime, storage.events, onAdmit),
6677
6523
  });
6678
6524
  },
6525
+ /**
6526
+ * Admits retained history provider work only while repository membership remains current.
6527
+ *
6528
+ * @param repository Repository whose runtime must remain current.
6529
+ * @param runtime Captured context runtime.
6530
+ * @param tenantId Bound tenant when multitenant.
6531
+ * @returns Completion after membership admission.
6532
+ */
6533
+ async admitHistory(repository, runtime, tenantId) {
6534
+ if (repositoryRuntimes.get(repository) !== runtime)
6535
+ throw new Error("Repository history requires its active runtime.");
6536
+ await runtime.onAdmit(tenantId);
6537
+ },
6538
+ /**
6539
+ * Captures each protected history call's actor and request before admission awaits.
6540
+ *
6541
+ * @param scope Bound repository, caches, and admission work.
6542
+ * @returns State and Event read callbacks for the Entity.
6543
+ */
6544
+ historyReaders(scope) {
6545
+ return {
6546
+ stateAt: (time) => this.historyStateAt(scope, time),
6547
+ states: (depth) => this.historyStates(scope, depth),
6548
+ events: (depth) => this.historyEvents(scope, depth),
6549
+ };
6550
+ },
6551
+ /**
6552
+ * Captures an Entity's state-at request and actor before membership awaits.
6553
+ * @param scope Bound history resources.
6554
+ * @param time Caller-selected historical time.
6555
+ * @returns Historical state, if retained and visible.
6556
+ */
6557
+ async historyStateAt(scope, time) {
6558
+ const actor = entityHistoryAccess.currentActor(scope.entity);
6559
+ const captured = clone(TimestampSchema, time);
6560
+ await scope.onAdmit();
6561
+ return this.readBoundStateAt(scope.repository, scope.lifetime, scope.stand, scope.storage, scope.schema, scope.entityId, captured, scope.tenantId, actor);
6562
+ },
6563
+ /**
6564
+ * Captures an Entity state-history depth and actor before membership awaits.
6565
+ * @param scope Bound history resources.
6566
+ * @param depth Number of prior states requested.
6567
+ * @returns Visible historical states in retained order.
6568
+ */
6569
+ async historyStates(scope, depth) {
6570
+ const actor = entityHistoryAccess.currentActor(scope.entity);
6571
+ await scope.onAdmit();
6572
+ return this.readBoundStates(scope.repository, scope.lifetime, scope.stand, scope.schema, scope.stateCache, scope.entityId, depth, scope.tenantId, actor);
6573
+ },
6574
+ /**
6575
+ * Captures an Entity Event-history depth and actor before membership awaits.
6576
+ * @param scope Bound history resources.
6577
+ * @param depth Number of prior Events requested.
6578
+ * @returns Visible retained Event envelopes.
6579
+ */
6580
+ async historyEvents(scope, depth) {
6581
+ const actor = entityHistoryAccess.currentActor(scope.entity);
6582
+ await scope.onAdmit();
6583
+ return this.readBoundEvents(scope.repository, scope.lifetime, scope.stand, scope.eventCache, scope.entityId, depth, scope.tenantId, actor);
6584
+ },
6585
+ /**
6586
+ * Creates state and Event caches over retained history ports.
6587
+ *
6588
+ * @param storage Retained history ports.
6589
+ * @param entityId Captured Entity identifier.
6590
+ * @param schema State schema for record version decoding.
6591
+ * @returns Bound state and Event caches.
6592
+ */
6593
+ boundHistoryCaches(storage, entityId, schema) {
6594
+ const stateCache = this.createHistoryCache((depth, startingFromVersion) => storage.states.backward(entityId, depth, startingFromVersion), (record) => BigInt(EntityRecords.unpack(schema, record).versionMessage.number), { requireDescendingVersions: true });
6595
+ const eventCache = this.createHistoryCache((depth, startingFromVersion) => storage.events.backward(entityId, depth, startingFromVersion), (event) => RepositorySignals.readEventVersion(event), { cacheCompleteVersionGroups: true });
6596
+ return { stateCache, eventCache };
6597
+ },
6598
+ /**
6599
+ * Admits complete state-history maintenance against a captured runtime.
6600
+ *
6601
+ * @param repository Repository with the bound Entity.
6602
+ * @param lifetime Runtime that provided the retained port.
6603
+ * @param states Retained state-history port.
6604
+ * @param onAdmit Checks current membership before physical work.
6605
+ * @returns Guarded maintenance methods.
6606
+ */
6607
+ boundStateMaintenance(repository, lifetime, states, onAdmit) {
6608
+ return {
6609
+ trim: async (id, keep) => {
6610
+ await onAdmit();
6611
+ return RepositoryStorage.track(repository, () => states.trim(id, keep), lifetime);
6612
+ },
6613
+ truncate: async (time) => {
6614
+ await onAdmit();
6615
+ return RepositoryStorage.track(repository, () => states.truncate(time), lifetime);
6616
+ },
6617
+ };
6618
+ },
6619
+ /**
6620
+ * Admits complete Event-history maintenance against a captured runtime.
6621
+ *
6622
+ * @param repository Repository with the bound Entity.
6623
+ * @param lifetime Runtime that provided the retained port.
6624
+ * @param events Retained Event-history port.
6625
+ * @param onAdmit Checks current membership before physical work.
6626
+ * @returns Guarded maintenance method.
6627
+ */
6628
+ boundEventMaintenance(repository, lifetime, events, onAdmit) {
6629
+ return {
6630
+ truncate: async (time) => {
6631
+ await onAdmit();
6632
+ return RepositoryStorage.track(repository, () => events.truncate(time), lifetime);
6633
+ },
6634
+ };
6635
+ },
6636
+ /**
6637
+ * Reads one bound historical state within repository admission.
6638
+ *
6639
+ * @param repository Registered repository.
6640
+ * @param lifetime Captured repository storage lifetime.
6641
+ * @param stand Producing context's outward read policy.
6642
+ * @param storage Retained Entity read port.
6643
+ * @param schema Historical state schema.
6644
+ * @param entityId Captured Entity identifier.
6645
+ * @param time Historical instant.
6646
+ * @param tenantId Effective tenant of the bound Entity.
6647
+ * @param actorContext Actor metadata of an active handler, when present.
6648
+ * @returns Historical state, if present.
6649
+ */
6650
+ readBoundStateAt(repository, lifetime, stand, storage, schema, entityId, time, tenantId, actorContext) {
6651
+ return RepositoryStorage.track(repository, async () => {
6652
+ const state = this.cloneHistoryState(await storage.states.stateAt(entityId, time));
6653
+ if (state === undefined)
6654
+ return undefined;
6655
+ const visible = await standAccess.filterState(stand, schema, state, {
6656
+ operation: { kind: "history", history: { source: "entity-state-at", entityId, time } },
6657
+ ...(tenantId === undefined ? {} : { effectiveTenant: tenantId }),
6658
+ ...(actorContext === undefined ? {} : { actorContext }),
6659
+ });
6660
+ return visible ?? undefined;
6661
+ }, lifetime);
6662
+ },
6663
+ /**
6664
+ * Reads a complete bound state-history page within repository admission.
6665
+ *
6666
+ * @param repository Registered repository.
6667
+ * @param lifetime Captured repository storage lifetime.
6668
+ * @param stand Producing context's outward read policy.
6669
+ * @param schema State schema used to unpack rows.
6670
+ * @param cache Bound state-history cache.
6671
+ * @param entityId Captured Entity identifier.
6672
+ * @param depth Requested history depth.
6673
+ * @param tenantId Effective tenant of the bound Entity.
6674
+ * @param actorContext Actor metadata of an active handler, when present.
6675
+ * @returns Frozen historical states.
6676
+ */
6677
+ readBoundStates(repository, lifetime, stand, schema, cache, entityId, depth, tenantId, actorContext) {
6678
+ return RepositoryStorage.track(repository, async () => {
6679
+ const states = this.freezeHistoryStates((await cache.read(depth)).map((record) => EntityRecords.unpack(schema, record).state));
6680
+ const visible = [];
6681
+ for (const state of states) {
6682
+ const next = await standAccess.filterState(stand, schema, state, {
6683
+ operation: { kind: "history", history: { source: "entity-states", entityId, depth } },
6684
+ ...(tenantId === undefined ? {} : { effectiveTenant: tenantId }),
6685
+ ...(actorContext === undefined ? {} : { actorContext }),
6686
+ });
6687
+ if (next !== null)
6688
+ visible.push(Object.freeze(next));
6689
+ }
6690
+ return Object.freeze(visible);
6691
+ }, lifetime);
6692
+ },
6693
+ /**
6694
+ * Reads a complete bound Event-history page within repository admission.
6695
+ *
6696
+ * @param repository Registered repository.
6697
+ * @param lifetime Captured repository storage lifetime.
6698
+ * @param stand Producing context's outward read policy.
6699
+ * @param cache Bound Event-history cache.
6700
+ * @param entityId Captured Entity identifier.
6701
+ * @param depth Requested history depth.
6702
+ * @param tenantId Effective tenant of the bound Entity.
6703
+ * @param actorContext Actor metadata of an active handler, when present.
6704
+ * @returns Frozen historical Events.
6705
+ */
6706
+ readBoundEvents(repository, lifetime, stand, cache, entityId, depth, tenantId, actorContext) {
6707
+ return RepositoryStorage.track(repository, async () => {
6708
+ const events = this.freezeHistoryEvents(await cache.read(depth));
6709
+ const visible = [];
6710
+ for (const event of events) {
6711
+ const next = await standAccess.filterEvent(stand, event, {
6712
+ operation: { kind: "history", history: { source: "entity-events", entityId, depth } },
6713
+ ...(tenantId === undefined ? {} : { effectiveTenant: tenantId }),
6714
+ ...(actorContext === undefined ? {} : { actorContext }),
6715
+ });
6716
+ if (next !== null)
6717
+ visible.push(Object.freeze(next));
6718
+ }
6719
+ return Object.freeze(visible);
6720
+ }, lifetime);
6721
+ },
6679
6722
  /**
6680
6723
  * Creates a history cache that serializes reads and extends prior results.
6681
6724
  *
@@ -7063,14 +7106,11 @@ const DispatchGuards = {
7063
7106
  */
7064
7107
  async wasDispatched(repository, runtime, event, entityId, journalEventId, depth) {
7065
7108
  const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForEvent(runtime.context, event));
7066
- const storage = RepositoryStorage.openEntityStorage(runtime.storageFactory, input);
7067
- try {
7109
+ return RepositoryStorage.track(repository, async () => {
7110
+ const storage = RepositoryStorage.repositoryReadEntityStorage(repository, runtime.storageFactory, input);
7068
7111
  const persisted = await storage.events.backward(entityId, depth);
7069
7112
  return persisted.some((candidate) => candidate.id?.value === journalEventId);
7070
- }
7071
- finally {
7072
- storage.close();
7073
- }
7113
+ });
7074
7114
  },
7075
7115
  /**
7076
7116
  * Records a successful delivery and discards the oldest excess identifiers.
@@ -7228,7 +7268,7 @@ const InboxMessages = {
7228
7268
  if (message.label !== "HANDLE_COMMAND") {
7229
7269
  throw new Error(`Entity Inbox replay does not handle "${message.label}" messages.`);
7230
7270
  }
7231
- const command = message.signal === undefined ? undefined : AnyMessages.unpack(message.signal, CommandSchema);
7271
+ const command = message.signal === undefined ? undefined : AnyPacker.tryUnpack(message.signal, CommandSchema);
7232
7272
  if (command === undefined) {
7233
7273
  throw CommandValidationError.invalidPayload();
7234
7274
  }
@@ -7271,7 +7311,7 @@ const InboxMessages = {
7271
7311
  typeUrl: message.signal.typeUrl,
7272
7312
  value: new Uint8Array(message.signal.value),
7273
7313
  });
7274
- const event = signal === undefined ? undefined : AnyMessages.unpack(signal, EventSchema);
7314
+ const event = signal === undefined ? undefined : AnyPacker.tryUnpack(signal, EventSchema);
7275
7315
  if (event === undefined) {
7276
7316
  throw new Error(unreadableMessage);
7277
7317
  }
@@ -7368,7 +7408,7 @@ const InboxReplay = {
7368
7408
  const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
7369
7409
  if (assignee === undefined)
7370
7410
  throw new Error("Agent Command has no selected assignee.");
7371
- await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityId, { kind: "command", value: command }, command.context?.actorContext ?? create(ActorContextSchema), [assignee.registeredHandler]);
7411
+ await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityId, { case: "command", value: command }, [assignee.registeredHandler]);
7372
7412
  },
7373
7413
  /**
7374
7414
  * Admits matching Event handlers from the stored target without rerouting.
@@ -7388,7 +7428,7 @@ const InboxReplay = {
7388
7428
  const handlers = InboxReplay.agentEventHandlers(routing, route.messageFullTypeName, event);
7389
7429
  if (handlers.length === 0)
7390
7430
  return;
7391
- await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityIds[0], { kind: "event", value: event }, runtime.signalMetadata.originFromEvent(event).actorContext ?? create(ActorContextSchema), handlers);
7431
+ await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityIds[0], { case: "event", value: event }, handlers);
7392
7432
  },
7393
7433
  /**
7394
7434
  * Restores selected Event and Command reactions from the original payload.
@@ -7417,64 +7457,154 @@ const InboxReplay = {
7417
7457
  * @param tenantId Selected delivery tenant.
7418
7458
  * @param entityId Typed target from the stored route.
7419
7459
  * @param signal Original source envelope.
7420
- * @param actor Original actor metadata.
7421
7460
  * @param handlers Selected handler metadata in execution order.
7422
7461
  * @returns When the fenced repository operation completes.
7423
7462
  */
7424
- async admitAgent(repository, runtime, message, tenantId, entityId, signal, actor, handlers) {
7425
- const revisions = InboxReplay.agentRevisions(repository, runtime, handlers);
7426
- const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
7427
- const execution = InboxReplay.agentExecutionHandle(repository, runtime, input);
7428
- await execution.admit(AgentAdmission.create({
7429
- stateType: repository.stateSchema.typeName,
7430
- agentKey: input.id.key(entityId),
7431
- recipientId: message.inboxId.targetId,
7463
+ async admitAgent(repository, runtime, message, tenantId, entityId, signal, handlers) {
7464
+ const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
7465
+ const input = RepositoryStorage.entityStorageInput(repository, context);
7466
+ const deliverySignal = AgentDeliveryScope.signal();
7467
+ if (deliverySignal === undefined)
7468
+ throw new Error("Agent admission requires delivery scope.");
7469
+ const cancellation = AbortSignal.any([deliverySignal, runtime.agentSignal]);
7470
+ const key = InboxReplay.capacityKeyFor(repository, input.context, input.id.key(entityId));
7471
+ const reservation = runtime.reserveAgent(key, cancellation);
7472
+ if (reservation === undefined) {
7473
+ runtime.deferAgent(message, tenantId);
7474
+ AgentDeliveryScope.record({ kind: "deferred" });
7475
+ return;
7476
+ }
7477
+ await InboxReplay.admitReservedAgent({
7478
+ repository,
7479
+ runtime,
7480
+ context: input.context,
7481
+ message,
7482
+ tenantId,
7483
+ entityId,
7432
7484
  signal,
7433
- actor,
7434
- inbox: message,
7435
7485
  handlers,
7436
- codeRevision: revisions.codeRevision,
7437
- schemaRevision: revisions.schema,
7438
- policyRevision: revisions.policy,
7439
- }));
7440
- runtime.wakeAcceptedAgent(repository, tenantId);
7486
+ reservation,
7487
+ });
7441
7488
  },
7442
7489
  /**
7443
- * Opens the actual tenant's mandatory execution handle.
7490
+ * Serializes the complete local Agent identity for one capacity slot.
7444
7491
  *
7445
- * @typeParam I Typed Agent identifier.
7446
- * @typeParam S Generated Agent state message.
7447
- * @param repository Registered Entity repository for this operation.
7448
- * @param runtime Bounded Context storage and publication services.
7449
- * @param input Original Inbox or storage input.
7450
- * @returns Fenced execution handle for the accepted tenant.
7492
+ * @param context Captured tenant storage context.
7493
+ * @param stateType Declared Agent state type.
7494
+ * @param id Canonical typed Agent identifier.
7495
+ * @returns Stable local capacity key for this Agent and tenant.
7451
7496
  */
7452
- agentExecutionHandle(repository, runtime, input) {
7453
- const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
7454
- if (storage.agentExecution === undefined)
7455
- throw new Error("Agent Inbox replay requires durable execution storage.");
7456
- return storage.agentExecution;
7497
+ agentCapacityKey(context, stateType, id) {
7498
+ return JSON.stringify([context.name, String(TenantBoundary.of(context).key), stateType, id]);
7457
7499
  },
7458
7500
  /**
7459
- * Records the registered code, schema, and AI policy before Inbox handoff.
7501
+ * Builds a capacity key for one repository entity in the selected tenant.
7460
7502
  *
7461
- * @param repository Registered Entity repository for this operation.
7462
- * @param runtime Bounded Context storage and publication services.
7463
- * @param handlers Generated handlers in their saved order.
7464
- * @returns Pinned code, schema, and AI policy digests.
7465
- */
7466
- agentRevisions(repository, runtime, handlers) {
7467
- const ai = runtime.ai;
7468
- const configuration = repositoryAccess.agentConfiguration(repository);
7469
- if (ai === undefined ||
7470
- configuration.ai === undefined ||
7471
- configuration.codeRevision === undefined)
7472
- throw new Error("Agent Inbox replay requires registered AI capabilities and revisions.");
7473
- return {
7474
- codeRevision: configuration.codeRevision,
7475
- ...AgentRevisions.atAdmission(repository.stateSchema, handlers, ai, configuration.ai),
7503
+ * @param repository Agent repository declaring the state type.
7504
+ * @param context Captured tenant storage context.
7505
+ * @param id Canonical typed Agent identifier.
7506
+ * @returns Stable local capacity key for this repository entity.
7507
+ */
7508
+ capacityKeyFor(repository, context, id) {
7509
+ return this.agentCapacityKey(context, repository.stateSchema.typeName, id);
7510
+ },
7511
+ /**
7512
+ * Completes receipt admission or returns a nonacknowledging provider error.
7513
+ * @param input Captured signal, runtime, and local capacity reservation.
7514
+ * @returns Completion of receipt admission or recorded provider failure.
7515
+ */
7516
+ async admitReservedAgent(input) {
7517
+ const { repository, runtime, context, message, signal, reservation } = input;
7518
+ try {
7519
+ const accepted = await InboxReplay.acceptAgentReceipt(runtime, context, repository, message, signal);
7520
+ if (!accepted) {
7521
+ reservation.release();
7522
+ AgentDeliveryScope.record({ kind: "duplicate" });
7523
+ return;
7524
+ }
7525
+ InboxReplay.recordAcceptedAgent(input);
7526
+ }
7527
+ catch (error) {
7528
+ reservation.release();
7529
+ AgentDeliveryScope.record({ kind: "error", error });
7530
+ }
7531
+ },
7532
+ /**
7533
+ * Records one accepted typed signal to its post-ack serialized live turn.
7534
+ * @param input Accepted signal and its retained capacity reservation.
7535
+ */
7536
+ recordAcceptedAgent(input) {
7537
+ const { repository, runtime, message, tenantId, entityId, signal, handlers, reservation } = input;
7538
+ const invocation = {
7539
+ stateType: repository.stateSchema.typeName,
7540
+ recipientId: clone(AnySchema, message.inboxId.targetId),
7541
+ source: signal,
7542
+ };
7543
+ AgentDeliveryScope.record({
7544
+ kind: "accepted",
7545
+ discard: () => {
7546
+ reservation.release();
7547
+ },
7548
+ run: (fence, checkSession) => {
7549
+ const cancellation = AbortSignal.any([fence, runtime.agentSignal]);
7550
+ const checkAgentSession = InboxReplay.agentSessionGuard(cancellation, checkSession);
7551
+ return reservation.run(() => withDeliveryCommitFence(checkAgentSession, () => new AgentLiveRunner(repository, runtime, tenantId, entityId, invocation, handlers, cancellation, checkAgentSession).run()));
7552
+ },
7553
+ });
7554
+ },
7555
+ /**
7556
+ * Verifies context cancellation on both sides of asynchronous shard validation.
7557
+ * @param cancellation Combined context and shard cancellation signal.
7558
+ * @param checkSession Validates the current shard session.
7559
+ * @returns A guard for live Agent effects and commits.
7560
+ */
7561
+ agentSessionGuard(cancellation, checkSession) {
7562
+ return async () => {
7563
+ cancellation.throwIfAborted();
7564
+ await checkSession();
7565
+ cancellation.throwIfAborted();
7476
7566
  };
7477
7567
  },
7568
+ /**
7569
+ * Writes one typed receipt after a local Agent capacity reservation.
7570
+ * @param context Captured tenant storage context.
7571
+ * @param message Original Inbox row with the typed recipient.
7572
+ * @param repository Agent repository declaring the state type.
7573
+ * @param runtime Bound repository runtime containing the receipt port.
7574
+ * @param signal Original Command or Event signal.
7575
+ * @returns Whether this exact Agent signal was newly accepted.
7576
+ */
7577
+ acceptAgentReceipt(runtime, context, repository, message, signal) {
7578
+ const source = signal.case === "command"
7579
+ ? { kind: "command", id: InboxReplay.requireAgentCommandId(signal.value) }
7580
+ : { kind: "event", id: InboxReplay.requireAgentEventId(signal.value) };
7581
+ return runtime.agentReceipts.accept(context, {
7582
+ stateType: repository.stateSchema.typeName,
7583
+ recipientId: message.inboxId.targetId,
7584
+ source,
7585
+ action: message.label,
7586
+ });
7587
+ },
7588
+ /**
7589
+ * Validates the original typed Command ID before writing an Agent receipt.
7590
+ * @param command Original Command addressed to the Agent.
7591
+ * @returns Original nonempty typed Command ID.
7592
+ */
7593
+ requireAgentCommandId(command) {
7594
+ if (command.id === undefined || command.id.uuid.trim().length === 0)
7595
+ throw new Error("Agent acceptance requires the original Command ID.");
7596
+ return command.id;
7597
+ },
7598
+ /**
7599
+ * Validates the original typed Event ID before writing an Agent receipt.
7600
+ * @param event Original Event addressed to the Agent.
7601
+ * @returns Original nonempty typed Event ID.
7602
+ */
7603
+ requireAgentEventId(event) {
7604
+ if (event.id === undefined || event.id.value.trim().length === 0)
7605
+ throw new Error("Agent acceptance requires the original Event ID.");
7606
+ return event.id;
7607
+ },
7478
7608
  /**
7479
7609
  * Dispatches a stored Process Manager Command after tenant and payload validation.
7480
7610
  *
@@ -7583,7 +7713,7 @@ const InboxReplay = {
7583
7713
  validateReplayedCommandPayload(routing, command) {
7584
7714
  const commandMessage = EntityInvocation.requireSignalMessage(command.message, "command");
7585
7715
  const commandSchema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, commandMessage.typeUrl, "command");
7586
- const payload = AnyMessages.unpack(commandMessage, commandSchema);
7716
+ const payload = AnyPacker.tryUnpack(commandMessage, commandSchema);
7587
7717
  if (payload === undefined) {
7588
7718
  throw CommandValidationError.invalidPayload();
7589
7719
  }
@@ -7611,7 +7741,7 @@ const InboxReplay = {
7611
7741
  validateReplayedEventPayload(routing, event, invalidPayloadMessage = "Projection inbox replay requires a readable event payload.") {
7612
7742
  const eventMessage = EntityInvocation.requireSignalMessage(event.message, "event");
7613
7743
  const eventSchema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, eventMessage.typeUrl, "event");
7614
- const payload = AnyMessages.unpack(eventMessage, eventSchema);
7744
+ const payload = AnyPacker.tryUnpack(eventMessage, eventSchema);
7615
7745
  if (payload === undefined) {
7616
7746
  throw new Error(invalidPayloadMessage);
7617
7747
  }
@@ -7792,18 +7922,14 @@ const InboxHandoff = {
7792
7922
  const commandId = RepositorySignals.requireCommandId(command);
7793
7923
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7794
7924
  const deliveryTenantId = RepositoryTenants.requireCommandTenant(runtime.context, command);
7795
- const delivery = new Delivery({
7796
- context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
7797
- storageFactory: runtime.storageFactory,
7798
- strategy: runtime.entityInbox.strategy(),
7799
- });
7925
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7800
7926
  await runtime.entityInbox.receive(delivery, {
7801
7927
  inboxId: {
7802
7928
  targetId: InboxMessages.inboxTargetId(route.entityId, repository.idField),
7803
7929
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
7804
7930
  },
7805
7931
  signalId: commandId.uuid,
7806
- signal: AnyMessages.pack(CommandSchema, command, { validate: false }),
7932
+ signal: AnyPacker.pack(CommandSchema, command),
7807
7933
  label: "HANDLE_COMMAND",
7808
7934
  status: "TO_DELIVER",
7809
7935
  keepUntil,
@@ -7822,17 +7948,14 @@ const InboxHandoff = {
7822
7948
  const eventId = RepositorySignals.requireEventId(event);
7823
7949
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7824
7950
  const deliveryTenantId = RepositoryTenants.requireProjectionTenant(runtime.context, event);
7825
- const delivery = new Delivery({
7826
- context: RepositoryTenants.projectionDeliveryContext(runtime.context, deliveryTenantId),
7827
- storageFactory: runtime.storageFactory,
7828
- });
7951
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7829
7952
  await runtime.projectionInbox.receive(delivery, {
7830
7953
  inboxId: {
7831
7954
  targetId: InboxMessages.inboxTargetId(entityId, repository.idField),
7832
7955
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
7833
7956
  },
7834
7957
  signalId: eventId.value,
7835
- signal: AnyMessages.pack(EventSchema, event, { validate: false }),
7958
+ signal: AnyPacker.pack(EventSchema, event),
7836
7959
  label: "UPDATE_SUBSCRIBER",
7837
7960
  status: "TO_DELIVER",
7838
7961
  shard: ShardIndex.single(),
@@ -7852,11 +7975,7 @@ const InboxHandoff = {
7852
7975
  const eventId = RepositorySignals.requireEventId(event);
7853
7976
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7854
7977
  const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
7855
- const delivery = new Delivery({
7856
- context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
7857
- storageFactory: runtime.storageFactory,
7858
- strategy: runtime.entityInbox.strategy(),
7859
- });
7978
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7860
7979
  await runtime.entityInbox.receive(delivery, InboxHandoff.pmEventInboxInput(repository, eventId.value, event, entityId, keepUntil), deliveryTenantId);
7861
7980
  },
7862
7981
  /**
@@ -7872,11 +7991,7 @@ const InboxHandoff = {
7872
7991
  const eventId = RepositorySignals.requireEventId(event);
7873
7992
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7874
7993
  const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
7875
- const delivery = new Delivery({
7876
- context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
7877
- storageFactory: runtime.storageFactory,
7878
- strategy: runtime.entityInbox.strategy(),
7879
- });
7994
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7880
7995
  for (let start = 0; start < entityIds.length; start += 1_000) {
7881
7996
  const inputs = entityIds
7882
7997
  .slice(start, start + 1_000)
@@ -7901,7 +8016,7 @@ const InboxHandoff = {
7901
8016
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
7902
8017
  },
7903
8018
  signalId,
7904
- signal: AnyMessages.pack(EventSchema, event, { validate: false }),
8019
+ signal: AnyPacker.pack(EventSchema, event),
7905
8020
  label: "REACT_UPON_EVENT",
7906
8021
  status: "TO_DELIVER",
7907
8022
  keepUntil,
@@ -7909,182 +8024,6 @@ const InboxHandoff = {
7909
8024
  },
7910
8025
  };
7911
8026
  Object.freeze(InboxHandoff);
7912
- /**
7913
- * Rebinds generated repository recipients without repeating accepted route queries.
7914
- */
7915
- const SavedRepositoryDispatch = {
7916
- /**
7917
- * Maps a generated repository family to its serialized binding value.
7918
- *
7919
- * @param repository Registered Entity repository for this operation.
7920
- * @returns Repository family encoded in a saved dispatch target.
7921
- */
7922
- family(repository) {
7923
- switch (repository.entityFamily) {
7924
- case "aggregate":
7925
- return AgentSavedRepositoryFamily.AGENT_SAVED_AGGREGATE;
7926
- case "projection":
7927
- return AgentSavedRepositoryFamily.AGENT_SAVED_PROJECTION;
7928
- case "process-manager":
7929
- return AgentSavedRepositoryFamily.AGENT_SAVED_PROCESS_MANAGER;
7930
- case "agent":
7931
- return AgentSavedRepositoryFamily.AGENT_SAVED_AGENT;
7932
- }
7933
- },
7934
- /**
7935
- * Finds the current generated handlers that determine an Event handoff.
7936
- *
7937
- * @param routing Registered generated handler routing metadata.
7938
- * @param event Original typed Event envelope.
7939
- * @returns Generated handlers matching the outgoing Event.
7940
- */
7941
- eventHandlers(routing, event) {
7942
- const packed = EntityInvocation.requireSignalMessage(event.message, "event");
7943
- const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
7944
- const value = EntityInvocation.unpackRequired(packed, schema, "event");
7945
- const external = event.context?.external === true;
7946
- return [
7947
- ...routing.eventReactors(schema.typeName, value, external),
7948
- ...routing.commandReactions(schema.typeName, value, external),
7949
- ...routing
7950
- .eventSubscribers(schema.typeName, value, external)
7951
- .map((entry) => entry.registeredHandler),
7952
- ];
7953
- },
7954
- /**
7955
- * Calculates a digest of ordered generated metadata and message descriptors.
7956
- *
7957
- * @param repository Registered Entity repository for this operation.
7958
- * @param handlers Generated handlers in their saved order.
7959
- * @returns Digest of ordered generated handler metadata.
7960
- */
7961
- fingerprint(repository, handlers) {
7962
- const selected = handlers.map(({ entity, handler }, ordinal) => ({
7963
- ordinal,
7964
- receiver: entity.schema.typeName,
7965
- kind: handler.kind,
7966
- method: handler.methodName,
7967
- signal: handler.messageFullTypeName,
7968
- origin: handler.origin,
7969
- parameters: handler.parameterCount,
7970
- where: handler.where === undefined ? null : [handler.where.eventField, handler.where.equals],
7971
- descriptor: Buffer.from(toBinary(FileDescriptorProtoSchema, handler.schema.file.proto)).toString("base64"),
7972
- }));
7973
- const state = Buffer.from(toBinary(FileDescriptorProtoSchema, repository.stateSchema.file.proto));
7974
- return createHash("sha256").update(state).update(JSON.stringify(selected)).digest("hex");
7975
- },
7976
- /**
7977
- * Restores a typed ID only when its canonical wire representation is unchanged.
7978
- *
7979
- * @param repository Registered Entity repository for this operation.
7980
- * @param packed Original typed Protobuf payload.
7981
- * @returns Typed recipient identifier from the frozen route.
7982
- */
7983
- recipient(repository, packed) {
7984
- const id = InboxMessages.targetEntityId(packed, repository.idField);
7985
- if (!equals(AnySchema, InboxMessages.inboxTargetId(id, repository.idField), packed))
7986
- throw new Error("Saved Agent output recipient ID is not canonical for its repository.");
7987
- return id;
7988
- },
7989
- /**
7990
- * Checks the stable repository and signal identity before saved transport.
7991
- *
7992
- * @param repository Registered Entity repository for this operation.
7993
- * @param target Frozen recipient binding.
7994
- * @param kind Generation or decision model kind.
7995
- * @returns Whether the checked condition holds.
7996
- */
7997
- matches(repository, target, kind) {
7998
- return (target.kind === kind &&
7999
- target.receiverStateType === repository.stateSchema.typeName &&
8000
- target.repositoryFamily === SavedRepositoryDispatch.family(repository));
8001
- },
8002
- /**
8003
- * Prepares one typed Command recipient with its selected generated assignee.
8004
- *
8005
- * @param repository Registered Entity repository for this operation.
8006
- * @param routing Registered generated handler routing metadata.
8007
- * @param command Original typed Command envelope.
8008
- * @returns Validated frozen Command recipient target.
8009
- */
8010
- async prepareCommand(repository, routing, command) {
8011
- const route = await repository.routeCommand(command);
8012
- const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
8013
- if (assignee === undefined)
8014
- throw new Error("Saved Agent Command has no generated assignee.");
8015
- return create(SavedTargetSchema, {
8016
- kind: AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND,
8017
- receiverStateType: repository.stateSchema.typeName,
8018
- repositoryFamily: SavedRepositoryDispatch.family(repository),
8019
- signalType: route.messageFullTypeName,
8020
- bindingFingerprint: SavedRepositoryDispatch.fingerprint(repository, [
8021
- assignee.registeredHandler,
8022
- ]),
8023
- recipients: [InboxMessages.inboxTargetId(route.entityId, repository.idField)],
8024
- });
8025
- },
8026
- /**
8027
- * Resolves the original Command recipient without calling its route again.
8028
- *
8029
- * @param repository Registered Entity repository for this operation.
8030
- * @param routing Registered generated handler routing metadata.
8031
- * @param command Original typed Command envelope.
8032
- * @param target Frozen recipient binding.
8033
- * @returns When the fenced repository operation completes.
8034
- */
8035
- bindCommand(repository, routing, command, target) {
8036
- const { runtime, signalType, recipient } = SavedRepositoryDispatch.requireCommandBinding(repository, routing, command, target);
8037
- const entityId = SavedRepositoryDispatch.recipient(repository, recipient);
8038
- const route = {
8039
- entityId,
8040
- messageFullTypeName: signalType,
8041
- invocation: "deferred",
8042
- };
8043
- return Promise.resolve(() => InboxHandoff.handoffEntityCommand(repository, runtime, command, route));
8044
- },
8045
- /**
8046
- * Checks saved Command metadata, typed target presence, and runtime registration.
8047
- *
8048
- * @param repository Registered Entity repository for this operation.
8049
- * @param routing Registered generated handler routing metadata.
8050
- * @param command Original typed Command envelope.
8051
- * @param target Frozen recipient binding.
8052
- * @returns Registered Command route and original recipient.
8053
- */
8054
- requireCommandBinding(repository, routing, command, target) {
8055
- const packed = EntityInvocation.requireSignalMessage(command.message, "command");
8056
- const schema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, packed.typeUrl, "command");
8057
- const assignee = routing.commandReadiness?.findCommandAssignee(schema.typeName);
8058
- const fingerprint = assignee === undefined
8059
- ? ""
8060
- : SavedRepositoryDispatch.fingerprint(repository, [assignee.registeredHandler]);
8061
- const recipient = target.recipients[0];
8062
- const runtime = repositoryRuntimes.get(repository);
8063
- if (!SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND) ||
8064
- target.signalType !== schema.typeName ||
8065
- target.bindingFingerprint !== fingerprint ||
8066
- target.recipients.length !== 1 ||
8067
- recipient === undefined ||
8068
- runtime === undefined)
8069
- throw new Error("Saved Agent Command repository binding changed before delivery.");
8070
- return { runtime, signalType: schema.typeName, recipient };
8071
- },
8072
- /**
8073
- * Creates one internal Command binding from the repository registration.
8074
- *
8075
- * @param repository Registered Entity repository for this operation.
8076
- * @param routing Registered generated handler routing metadata.
8077
- * @returns Saved Command binding for this registration.
8078
- */
8079
- commandBinding(repository, routing) {
8080
- return {
8081
- prepare: (command) => SavedRepositoryDispatch.prepareCommand(repository, routing, command),
8082
- matches: (target) => SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND),
8083
- bind: (command, target) => SavedRepositoryDispatch.bindCommand(repository, routing, command, target),
8084
- };
8085
- },
8086
- };
8087
- Object.freeze(SavedRepositoryDispatch);
8088
8027
  /**
8089
8028
  * Connects bus dispatchers and Inbox replay to repository execution.
8090
8029
  */
@@ -8119,7 +8058,7 @@ const RepositoryDispatch = {
8119
8058
  messageSchemas: () => routing.commandSchemas,
8120
8059
  dispatch: (command) => RepositoryDispatch.dispatchRepositoryCommand(repository, routing, command),
8121
8060
  });
8122
- return SavedDispatcherBindings.command(dispatcher, SavedRepositoryDispatch.commandBinding(repository, routing));
8061
+ return dispatcher;
8123
8062
  },
8124
8063
  /**
8125
8064
  * Creates an Event dispatcher that preserves routes computed during acceptance.