@spine-event-engine/server 2.0.0-snapshot.23 → 2.0.0-snapshot.25

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 (300) hide show
  1. package/README.md +38 -11
  2. package/REFERENCE.md +278 -141
  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 +15 -10
  12. package/dist/agent/agent-history.d.ts.map +1 -1
  13. package/dist/agent/agent-history.js +26 -14
  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 +61 -0
  56. package/dist/agent/agent-live-read-runtime.d.ts.map +1 -0
  57. package/dist/agent/agent-live-read-runtime.js +121 -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/bus/command-bus.d.ts +14 -18
  64. package/dist/bus/command-bus.d.ts.map +1 -1
  65. package/dist/bus/command-bus.js +31 -48
  66. package/dist/bus/command-bus.js.map +1 -1
  67. package/dist/bus/event-bus.d.ts +82 -104
  68. package/dist/bus/event-bus.d.ts.map +1 -1
  69. package/dist/bus/event-bus.js +87 -127
  70. package/dist/bus/event-bus.js.map +1 -1
  71. package/dist/context/bounded-context.d.ts +143 -91
  72. package/dist/context/bounded-context.d.ts.map +1 -1
  73. package/dist/context/bounded-context.js +753 -359
  74. package/dist/context/bounded-context.js.map +1 -1
  75. package/dist/context/entity-inbox.d.ts +7 -2
  76. package/dist/context/entity-inbox.d.ts.map +1 -1
  77. package/dist/context/entity-inbox.js +43 -20
  78. package/dist/context/entity-inbox.js.map +1 -1
  79. package/dist/context/local-inbox-handoff.d.ts +35 -34
  80. package/dist/context/local-inbox-handoff.d.ts.map +1 -1
  81. package/dist/context/local-inbox-handoff.js +123 -135
  82. package/dist/context/local-inbox-handoff.js.map +1 -1
  83. package/dist/context/projection-handoff.d.ts +2 -2
  84. package/dist/context/projection-handoff.d.ts.map +1 -1
  85. package/dist/context/projection-handoff.js +12 -8
  86. package/dist/context/projection-handoff.js.map +1 -1
  87. package/dist/context/tenant-index.d.ts +36 -31
  88. package/dist/context/tenant-index.d.ts.map +1 -1
  89. package/dist/context/tenant-index.js +123 -125
  90. package/dist/context/tenant-index.js.map +1 -1
  91. package/dist/context/tenant-unknown-error.d.ts +33 -0
  92. package/dist/context/tenant-unknown-error.d.ts.map +1 -0
  93. package/dist/context/tenant-unknown-error.js +75 -0
  94. package/dist/context/tenant-unknown-error.js.map +1 -0
  95. package/dist/delivery/agent-delivery-scope.d.ts +66 -0
  96. package/dist/delivery/agent-delivery-scope.d.ts.map +1 -0
  97. package/dist/delivery/agent-delivery-scope.js +44 -0
  98. package/dist/delivery/agent-delivery-scope.js.map +1 -0
  99. package/dist/delivery/delivery-builder.d.ts +25 -1
  100. package/dist/delivery/delivery-builder.d.ts.map +1 -1
  101. package/dist/delivery/delivery-builder.js +89 -0
  102. package/dist/delivery/delivery-builder.js.map +1 -1
  103. package/dist/delivery/delivery-monitor.d.ts +12 -9
  104. package/dist/delivery/delivery-monitor.d.ts.map +1 -1
  105. package/dist/delivery/delivery-monitor.js +15 -9
  106. package/dist/delivery/delivery-monitor.js.map +1 -1
  107. package/dist/delivery/delivery-ports.d.ts +6 -13
  108. package/dist/delivery/delivery-ports.d.ts.map +1 -1
  109. package/dist/delivery/delivery-run-control.d.ts +21 -0
  110. package/dist/delivery/delivery-run-control.d.ts.map +1 -1
  111. package/dist/delivery/delivery-run-control.js +10 -0
  112. package/dist/delivery/delivery-run-control.js.map +1 -1
  113. package/dist/delivery/delivery-supervisor.d.ts +33 -5
  114. package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
  115. package/dist/delivery/delivery-supervisor.js +178 -26
  116. package/dist/delivery/delivery-supervisor.js.map +1 -1
  117. package/dist/delivery/delivery-tenant.d.ts +17 -0
  118. package/dist/delivery/delivery-tenant.d.ts.map +1 -0
  119. package/dist/delivery/delivery-tenant.js +50 -0
  120. package/dist/delivery/delivery-tenant.js.map +1 -0
  121. package/dist/delivery/delivery.d.ts +96 -2
  122. package/dist/delivery/delivery.d.ts.map +1 -1
  123. package/dist/delivery/delivery.js +803 -89
  124. package/dist/delivery/delivery.js.map +1 -1
  125. package/dist/delivery/inbox-records.d.ts.map +1 -1
  126. package/dist/delivery/inbox-records.js +9 -16
  127. package/dist/delivery/inbox-records.js.map +1 -1
  128. package/dist/delivery/inbox-storage.d.ts +14 -11
  129. package/dist/delivery/inbox-storage.d.ts.map +1 -1
  130. package/dist/delivery/inbox-storage.js +72 -78
  131. package/dist/delivery/inbox-storage.js.map +1 -1
  132. package/dist/delivery/inbox.d.ts +9 -12
  133. package/dist/delivery/inbox.d.ts.map +1 -1
  134. package/dist/delivery/inbox.js +13 -15
  135. package/dist/delivery/inbox.js.map +1 -1
  136. package/dist/delivery/reception-observation.d.ts +64 -0
  137. package/dist/delivery/reception-observation.d.ts.map +1 -0
  138. package/dist/delivery/reception-observation.js +78 -0
  139. package/dist/delivery/reception-observation.js.map +1 -0
  140. package/dist/delivery/sharded-work-registry.d.ts +6 -0
  141. package/dist/delivery/sharded-work-registry.d.ts.map +1 -1
  142. package/dist/delivery/sharded-work-registry.js +41 -4
  143. package/dist/delivery/sharded-work-registry.js.map +1 -1
  144. package/dist/entity/entity-storage-descriptor.d.ts.map +1 -1
  145. package/dist/entity/entity-storage-descriptor.js +7 -4
  146. package/dist/entity/entity-storage-descriptor.js.map +1 -1
  147. package/dist/index.d.ts +4 -2
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +2 -1
  150. package/dist/index.js.map +1 -1
  151. package/dist/integration/external-messages.d.ts.map +1 -1
  152. package/dist/integration/external-messages.js +9 -15
  153. package/dist/integration/external-messages.js.map +1 -1
  154. package/dist/integration/integration-broker.d.ts.map +1 -1
  155. package/dist/integration/integration-broker.js +112 -6
  156. package/dist/integration/integration-broker.js.map +1 -1
  157. package/dist/integration/third-party-context.d.ts.map +1 -1
  158. package/dist/integration/third-party-context.js +7 -10
  159. package/dist/integration/third-party-context.js.map +1 -1
  160. package/dist/repository/primitive-id.js +26 -11
  161. package/dist/repository/primitive-id.js.map +1 -1
  162. package/dist/repository/repository.d.ts +33 -62
  163. package/dist/repository/repository.d.ts.map +1 -1
  164. package/dist/repository/repository.js +1475 -1827
  165. package/dist/repository/repository.js.map +1 -1
  166. package/dist/runtime/runtime.d.ts +24 -0
  167. package/dist/runtime/runtime.d.ts.map +1 -1
  168. package/dist/runtime/runtime.js +31 -0
  169. package/dist/runtime/runtime.js.map +1 -1
  170. package/dist/runtime/signal-metadata.d.ts.map +1 -1
  171. package/dist/runtime/signal-metadata.js +8 -15
  172. package/dist/runtime/signal-metadata.js.map +1 -1
  173. package/dist/runtime/signal-publisher.d.ts +8 -36
  174. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  175. package/dist/runtime/signal-publisher.js +68 -61
  176. package/dist/runtime/signal-publisher.js.map +1 -1
  177. package/dist/runtime/standalone-handler-runtime.d.ts +0 -7
  178. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  179. package/dist/runtime/standalone-handler-runtime.js +14 -130
  180. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  181. package/dist/server/managed-delivery-gate.d.ts +34 -0
  182. package/dist/server/managed-delivery-gate.d.ts.map +1 -0
  183. package/dist/server/managed-delivery-gate.js +70 -0
  184. package/dist/server/managed-delivery-gate.js.map +1 -0
  185. package/dist/server/managed-server-application.d.ts +2 -2
  186. package/dist/server/managed-server-application.d.ts.map +1 -1
  187. package/dist/server/managed-server-application.js +471 -178
  188. package/dist/server/managed-server-application.js.map +1 -1
  189. package/dist/server/node-coordinator.d.ts +14 -0
  190. package/dist/server/node-coordinator.d.ts.map +1 -1
  191. package/dist/server/node-coordinator.js +266 -45
  192. package/dist/server/node-coordinator.js.map +1 -1
  193. package/dist/server/server-environment.d.ts +203 -25
  194. package/dist/server/server-environment.d.ts.map +1 -1
  195. package/dist/server/server-environment.js +661 -133
  196. package/dist/server/server-environment.js.map +1 -1
  197. package/dist/server/server-log.d.ts +7 -0
  198. package/dist/server/server-log.d.ts.map +1 -1
  199. package/dist/server/server-log.js +18 -1
  200. package/dist/server/server-log.js.map +1 -1
  201. package/dist/server/server.d.ts +18 -32
  202. package/dist/server/server.d.ts.map +1 -1
  203. package/dist/server/server.js +62 -203
  204. package/dist/server/server.js.map +1 -1
  205. package/dist/services/spine-services.d.ts +1 -1
  206. package/dist/services/spine-services.d.ts.map +1 -1
  207. package/dist/services/spine-services.js +399 -370
  208. package/dist/services/spine-services.js.map +1 -1
  209. package/dist/stand/event-origin-filter.d.ts +16 -0
  210. package/dist/stand/event-origin-filter.d.ts.map +1 -0
  211. package/dist/stand/event-origin-filter.js +124 -0
  212. package/dist/stand/event-origin-filter.js.map +1 -0
  213. package/dist/stand/stand.d.ts +14 -0
  214. package/dist/stand/stand.d.ts.map +1 -1
  215. package/dist/stand/stand.js +53 -9
  216. package/dist/stand/stand.js.map +1 -1
  217. package/dist/stand/subscription-observer.d.ts.map +1 -1
  218. package/dist/stand/subscription-observer.js +61 -40
  219. package/dist/stand/subscription-observer.js.map +1 -1
  220. package/dist/stand/subscription-registry.d.ts +6 -2
  221. package/dist/stand/subscription-registry.d.ts.map +1 -1
  222. package/dist/stand/subscription-registry.js +15 -11
  223. package/dist/stand/subscription-registry.js.map +1 -1
  224. package/dist/stand/subscription-runtime.d.ts +23 -4
  225. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  226. package/dist/stand/subscription-runtime.js +84 -13
  227. package/dist/stand/subscription-runtime.js.map +1 -1
  228. package/dist/testing/index.d.ts.map +1 -1
  229. package/dist/testing/index.js +3 -2
  230. package/dist/testing/index.js.map +1 -1
  231. package/dist/tsconfig.tsbuildinfo +1 -1
  232. package/package.json +15 -15
  233. package/dist/agent/agent-admission.d.ts +0 -80
  234. package/dist/agent/agent-admission.d.ts.map +0 -1
  235. package/dist/agent/agent-admission.js +0 -127
  236. package/dist/agent/agent-admission.js.map +0 -1
  237. package/dist/agent/agent-ai-runtime.d.ts +0 -76
  238. package/dist/agent/agent-ai-runtime.d.ts.map +0 -1
  239. package/dist/agent/agent-ai-runtime.js +0 -1269
  240. package/dist/agent/agent-ai-runtime.js.map +0 -1
  241. package/dist/agent/agent-execution-fault.d.ts +0 -18
  242. package/dist/agent/agent-execution-fault.d.ts.map +0 -1
  243. package/dist/agent/agent-execution-fault.js.map +0 -1
  244. package/dist/agent/agent-execution-session.d.ts +0 -103
  245. package/dist/agent/agent-execution-session.d.ts.map +0 -1
  246. package/dist/agent/agent-execution-session.js +0 -238
  247. package/dist/agent/agent-execution-session.js.map +0 -1
  248. package/dist/agent/agent-interaction-audit.d.ts +0 -58
  249. package/dist/agent/agent-interaction-audit.d.ts.map +0 -1
  250. package/dist/agent/agent-interaction-audit.js +0 -114
  251. package/dist/agent/agent-interaction-audit.js.map +0 -1
  252. package/dist/agent/agent-interaction-events.d.ts +0 -40
  253. package/dist/agent/agent-interaction-events.d.ts.map +0 -1
  254. package/dist/agent/agent-interaction-events.js +0 -129
  255. package/dist/agent/agent-interaction-events.js.map +0 -1
  256. package/dist/agent/agent-mcp-budget.d.ts +0 -125
  257. package/dist/agent/agent-mcp-budget.d.ts.map +0 -1
  258. package/dist/agent/agent-mcp-budget.js +0 -566
  259. package/dist/agent/agent-mcp-budget.js.map +0 -1
  260. package/dist/agent/agent-mcp-host.d.ts +0 -125
  261. package/dist/agent/agent-mcp-host.d.ts.map +0 -1
  262. package/dist/agent/agent-mcp-host.js.map +0 -1
  263. package/dist/agent/agent-mcp-runtime.d.ts.map +0 -1
  264. package/dist/agent/agent-mcp-runtime.js.map +0 -1
  265. package/dist/agent/agent-read-runtime.d.ts +0 -70
  266. package/dist/agent/agent-read-runtime.d.ts.map +0 -1
  267. package/dist/agent/agent-read-runtime.js +0 -320
  268. package/dist/agent/agent-read-runtime.js.map +0 -1
  269. package/dist/agent/agent-revisions.d.ts +0 -35
  270. package/dist/agent/agent-revisions.d.ts.map +0 -1
  271. package/dist/agent/agent-revisions.js +0 -133
  272. package/dist/agent/agent-revisions.js.map +0 -1
  273. package/dist/agent/agent-scheduler.d.ts +0 -99
  274. package/dist/agent/agent-scheduler.d.ts.map +0 -1
  275. package/dist/agent/agent-scheduler.js +0 -398
  276. package/dist/agent/agent-scheduler.js.map +0 -1
  277. package/dist/bus/saved-dispatcher-binding.d.ts +0 -96
  278. package/dist/bus/saved-dispatcher-binding.d.ts.map +0 -1
  279. package/dist/bus/saved-dispatcher-binding.js +0 -35
  280. package/dist/bus/saved-dispatcher-binding.js.map +0 -1
  281. package/dist/delivery/delivery-run-coordinator.d.ts +0 -218
  282. package/dist/delivery/delivery-run-coordinator.d.ts.map +0 -1
  283. package/dist/delivery/delivery-run-coordinator.js +0 -609
  284. package/dist/delivery/delivery-run-coordinator.js.map +0 -1
  285. package/dist/delivery/parked-delivery-obligations.d.ts +0 -182
  286. package/dist/delivery/parked-delivery-obligations.d.ts.map +0 -1
  287. package/dist/delivery/parked-delivery-obligations.js +0 -390
  288. package/dist/delivery/parked-delivery-obligations.js.map +0 -1
  289. package/dist/server/environment-attachment.d.ts +0 -356
  290. package/dist/server/environment-attachment.d.ts.map +0 -1
  291. package/dist/server/environment-attachment.js +0 -2062
  292. package/dist/server/environment-attachment.js.map +0 -1
  293. package/dist/server/environment-delivery-records.d.ts +0 -77
  294. package/dist/server/environment-delivery-records.d.ts.map +0 -1
  295. package/dist/server/environment-delivery-records.js +0 -282
  296. package/dist/server/environment-delivery-records.js.map +0 -1
  297. package/dist/server/environment-delivery-worker.d.ts +0 -161
  298. package/dist/server/environment-delivery-worker.d.ts.map +0 -1
  299. package/dist/server/environment-delivery-worker.js +0 -673
  300. 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, } 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
- import { StorageQueryPolicy, } from "@spine-event-engine/storage";
23
- import { TenantBoundary } from "@spine-event-engine/storage/provider";
21
+ import { EventStore, StorageQueryPolicy, } from "@spine-event-engine/storage";
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, AgentOutgoingSignalSchema, 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, ConversationIdSchema, } 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";
@@ -36,19 +33,17 @@ import { EventRoutingInternals } from "./event-routing.js";
36
33
  import { StateUpdateRoutingInternals, } from "./state-update-routing.js";
37
34
  import { RoutingDeclarations } from "./routing-declarations.js";
38
35
  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
- import { commitFenced } from "./commit-fence.js";
36
+ import { receptionObservation } from "../delivery/reception-observation.js";
37
+ import { commitFenced, withDeliveryCommitFence } from "./commit-fence.js";
42
38
  import { AgentHistoryReads } from "../agent/agent-history.js";
43
- import { AgentReadRuntime } from "../agent/agent-read-runtime.js";
44
- import { AgentAdmission } from "../agent/agent-admission.js";
45
- import { AgentRevisions } from "../agent/agent-revisions.js";
46
- import { AgentExecutionSession } from "../agent/agent-execution-session.js";
47
- import { AgentExecutionFault } from "../agent/agent-execution-fault.js";
39
+ import { AgentLiveAiRuntime } from "../agent/agent-live-ai-runtime.js";
40
+ import { AgentLiveAudit } from "../agent/agent-live-audit.js";
41
+ import { AgentLiveBudget } from "../agent/agent-live-budget.js";
42
+ import { AgentLiveReadRuntime } from "../agent/agent-live-read-runtime.js";
43
+ import { AgentLiveInteractionEvents } from "../agent/agent-live-interaction-events.js";
44
+ import { AgentLiveFault } from "../agent/agent-live-fault.js";
45
+ import { AgentDeliveryScope } from "../delivery/agent-delivery-scope.js";
48
46
  import { AgentModelSelection } from "../agent/agent-model-selection.js";
49
- import { AgentAiRuntime, AgentUncertainAttemptError } from "../agent/agent-ai-runtime.js";
50
- import { AgentInteractionEvents } from "../agent/agent-interaction-events.js";
51
- import { AgentInteractionAudit } from "../agent/agent-interaction-audit.js";
52
47
  import { AgentAiBindings } from "../agent/agent-ai-binding.js";
53
48
  import { InboxTargets } from "../delivery/inbox.js";
54
49
  import { ShardIndex } from "../delivery/shard-index.js";
@@ -149,12 +144,13 @@ export class Repository {
149
144
  return this.#metadata.schema;
150
145
  }
151
146
  /**
152
- * Sets whether future state-history rows are appended for this repository.
147
+ * Sets state-history storage for future acquisitions. Admitted stores retain
148
+ * the setting captured when they began, and prior configurations stay reusable.
153
149
  *
154
150
  * This JVM-parity switch is for controlled administration/testing, not
155
151
  * routine request-time behavior. It never deletes or reconstructs history.
156
152
  *
157
- * @param enabled Whether later successful stores append state-history rows.
153
+ * @param enabled Whether newly admitted stores append state-history rows.
158
154
  */
159
155
  setStateHistoryEnabled(enabled) {
160
156
  if (typeof enabled !== "boolean") {
@@ -164,12 +160,6 @@ export class Repository {
164
160
  if (configuration.stateHistory === enabled)
165
161
  return;
166
162
  configuration.stateHistory = enabled;
167
- const handles = repositoryEntityHandles.get(this);
168
- if (handles !== undefined) {
169
- for (const handle of handles.values())
170
- handle.close();
171
- handles.clear();
172
- }
173
163
  }
174
164
  /**
175
165
  * Returns descriptor metadata for this repository's Entity state.
@@ -215,19 +205,6 @@ export class Repository {
215
205
  agentHistory(id, scope) {
216
206
  return RepositoryAgentReads.history(this, id, scope);
217
207
  }
218
- /**
219
- * Reads the exact recorded phase for one accepted Command or Event.
220
- * The trusted application must authorize the caller before using this read.
221
- *
222
- * @param this Registered Agent repository.
223
- * @param id Typed Agent identifier.
224
- * @param source Original Command or Event ID.
225
- * @param scope Explicit tenant boundary; pass `{}` in a single-tenant Bounded Context.
226
- * @returns Recorded phase, or `undefined` when no invocation was recorded.
227
- */
228
- agentExecution(id, source, scope) {
229
- return RepositoryAgentReads.execution(this, id, source, scope);
230
- }
231
208
  /**
232
209
  * Routes a command to one entity ID without invoking a handler.
233
210
  *
@@ -338,13 +315,10 @@ const RepositoryAgentReads = {
338
315
  async historyPage(repository, captured, view, request) {
339
316
  if (repositoryRuntimes.get(repository) !== captured.runtime)
340
317
  throw new Error("Agent read requires an active repository.");
341
- const input = RepositoryStorage.entityStorageInput(repository, captured.context);
342
- const history = AgentHistoryStorageFactories.create(captured.runtime.storageFactory, {
343
- context: captured.context,
344
- stateType: repository.stateSchema.typeName,
345
- id: { key: input.id.key },
346
- });
347
- try {
318
+ await captured.runtime.onAdmit(captured.context.tenantId);
319
+ return RepositoryStorage.track(repository, async () => {
320
+ const input = RepositoryStorage.entityStorageInput(repository, captured.context);
321
+ const history = RepositoryStorage.openRepositoryAgentHistory(repository, captured.runtime.storageFactory, input);
348
322
  const page = await AgentHistoryReads.readBound({
349
323
  storage: history,
350
324
  entityId: captured.id,
@@ -354,10 +328,7 @@ const RepositoryAgentReads = {
354
328
  items: page.items,
355
329
  ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
356
330
  };
357
- }
358
- finally {
359
- history.close();
360
- }
331
+ });
361
332
  },
362
333
  /**
363
334
  * Returns one typed category from the full Proto history oneof.
@@ -378,76 +349,6 @@ const RepositoryAgentReads = {
378
349
  ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
379
350
  };
380
351
  },
381
- /**
382
- * Reads the exact saved invocation and exposes only its phase.
383
- *
384
- * @param repository Registered Agent repository.
385
- * @param id Typed Agent identifier.
386
- * @param source Original Command or Event ID.
387
- * @param scope Explicit tenant selection.
388
- * @returns Current phase, or `undefined` for no saved invocation.
389
- */
390
- async execution(repository, id, source, scope) {
391
- const captured = this.capture(repository, id, scope);
392
- const sourceSignal = this.source(source);
393
- const input = RepositoryStorage.entityStorageInput(repository, captured.context);
394
- const key = create(AgentInvocationKeySchema, {
395
- scope: { stateType: repository.stateSchema.typeName, agentKey: input.id.key(captured.id) },
396
- sourceSignal,
397
- });
398
- if (repositoryRuntimes.get(repository) !== captured.runtime)
399
- throw new Error("Agent read requires an active repository.");
400
- const storage = AgentExecutionStorageFactories.create(captured.runtime.storageFactory, {
401
- entity: input,
402
- stateType: repository.stateSchema.typeName,
403
- });
404
- try {
405
- const record = await storage.read(key);
406
- return record === undefined ? undefined : this.status(record.status);
407
- }
408
- finally {
409
- storage.close();
410
- }
411
- },
412
- /**
413
- * Copies an original signal identifier into the private execution key.
414
- *
415
- * @param source Original Command or Event ID.
416
- * @returns Typed private source key.
417
- */
418
- source(source) {
419
- if (source.$typeName === CommandIdSchema.typeName && source.uuid.length > 0)
420
- return create(AgentSignalKeySchema, {
421
- id: { case: "command", value: clone(CommandIdSchema, source) },
422
- });
423
- if (source.$typeName === EventIdSchema.typeName && source.value.length > 0)
424
- return create(AgentSignalKeySchema, {
425
- id: { case: "event", value: clone(EventIdSchema, source) },
426
- });
427
- throw new TypeError("Agent execution requires an original CommandId or EventId.");
428
- },
429
- /**
430
- * Returns the public status for a private execution phase.
431
- *
432
- * @param status Saved private phase.
433
- * @returns Public phase string.
434
- */
435
- status(status) {
436
- switch (status) {
437
- case AgentInvocationStatus.AGENT_INVOCATION_ACCEPTED:
438
- return "accepted";
439
- case AgentInvocationStatus.AGENT_INVOCATION_ACTIVE:
440
- return "active";
441
- case AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_DELIVERY:
442
- return "completed-pending-delivery";
443
- case AgentInvocationStatus.AGENT_INVOCATION_COMPLETED:
444
- return "completed";
445
- case AgentInvocationStatus.AGENT_INVOCATION_TERMINATED:
446
- return "terminated";
447
- default:
448
- throw new Error("Agent execution provider returned an unsupported phase.");
449
- }
450
- },
451
352
  };
452
353
  const repositorySnapshots = new WeakMap();
453
354
  const repositoryCreators = new WeakMap();
@@ -458,11 +359,10 @@ const repositoryEntityInboxTargets = new WeakMap();
458
359
  const repositoryProjectionInboxTargets = new WeakMap();
459
360
  const repositoryProjectionDirect = new WeakMap();
460
361
  const repositoryRuntimes = new WeakMap();
461
- const repositoryEntityHandles = new WeakMap();
362
+ const repositoryStorageLifetimes = new WeakMap();
462
363
  const entityStateHistoryCaches = new WeakMap();
463
364
  const repositoryHistoryConfigurations = new WeakMap();
464
365
  const repositoryAiConfigurations = new WeakMap();
465
- const repositoryAgentCodeRevisions = new WeakMap();
466
366
  const repositoryDispatchGuards = new WeakMap();
467
367
  /**
468
368
  * Installs repository routing, history, and callback state after identity validation.
@@ -492,7 +392,7 @@ const RepositoryRegistration = {
492
392
  RepositoryRegistration.installTargets(repository, routing);
493
393
  },
494
394
  /**
495
- * Captures Agent capability and code revisions during repository registration.
395
+ * Captures Agent capability policy during repository registration.
496
396
  *
497
397
  * @typeParam EntityType Concrete generated Entity constructor.
498
398
  * @param repository Registered Entity repository for this operation.
@@ -504,8 +404,6 @@ const RepositoryRegistration = {
504
404
  return;
505
405
  if (options.ai !== undefined)
506
406
  repositoryAiConfigurations.set(repository, options.ai);
507
- if (options.agentCodeRevision !== undefined)
508
- repositoryAgentCodeRevisions.set(repository, options.agentCodeRevision);
509
407
  },
510
408
  /**
511
409
  * Registers durable Entity and direct Projection delivery targets.
@@ -554,6 +452,24 @@ export const repositoryAccess = Object.freeze({
554
452
  }
555
453
  return RepositoryIdentity.cloneRepositorySnapshot(snapshot);
556
454
  },
455
+ /**
456
+ * Prepares actual Entity and Agent families from the captured repository definition.
457
+ * @param repository Registered repository model.
458
+ * @param context Complete selected storage scope.
459
+ * @param factory Configured provider factory.
460
+ * @param mode Creation or compatibility inspection.
461
+ * @returns Completion after all required physical families are ready.
462
+ */
463
+ async prepareStorage(repository, context, factory, mode) {
464
+ const input = RepositoryStorage.entityStorageInput(repository, context);
465
+ const preparation = StoragePreparations.for(factory);
466
+ if (repository.entityFamily !== "agent") {
467
+ await preparation.prepareEntity(input, mode);
468
+ return;
469
+ }
470
+ await preparation.prepareAgentCommit(input, mode);
471
+ await preparation.prepareAgentHistory({ context, stateType: input.sourceType.typeName, id: { key: input.id.key } }, mode);
472
+ },
557
473
  /**
558
474
  * Reads schemas for Events this repository can emit.
559
475
  *
@@ -567,32 +483,9 @@ export const repositoryAccess = Object.freeze({
567
483
  }
568
484
  return schemas;
569
485
  },
570
- producedCommandSchemas(repository) {
571
- const routing = repositoryRoutings.get(repository);
572
- if (routing === undefined)
573
- throw new TypeError("Produced Command schemas require a Repository instance.");
574
- return routing.producedCommandSchemas;
575
- },
576
486
  agentConfiguration(repository) {
577
487
  const ai = repositoryAiConfigurations.get(repository);
578
- const codeRevision = repositoryAgentCodeRevisions.get(repository);
579
- return {
580
- ...(ai === undefined ? {} : { ai }),
581
- ...(codeRevision === undefined ? {} : { codeRevision }),
582
- };
583
- },
584
- runAcceptedAgent(repository, tenantId, key, signal) {
585
- return AgentExecutionRunner.run(repository, tenantId, key, signal);
586
- },
587
- pendingAcceptedAgents(repository, tenantId, after, count) {
588
- const runtime = repositoryRuntimes.get(repository);
589
- if (runtime === undefined || repository.entityFamily !== "agent")
590
- throw new Error("Agent pending discovery requires a registered Agent repository.");
591
- const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
592
- const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
593
- if (storage.agentExecution === undefined)
594
- throw new Error("Agent pending discovery requires execution storage.");
595
- return storage.agentExecution.pending({ count, ...(after === undefined ? {} : { after }) });
488
+ return ai === undefined ? {} : { ai };
596
489
  },
597
490
  /**
598
491
  * Opens a scoped provider read without restoring an application Entity.
@@ -611,18 +504,13 @@ export const repositoryAccess = Object.freeze({
611
504
  if (runtime === undefined)
612
505
  throw new Error("Agent history requires an active repository.");
613
506
  const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
507
+ await runtime.onAdmit(context.tenantId);
614
508
  const input = RepositoryStorage.entityStorageInput(repository, context);
615
- const history = AgentHistoryStorageFactories.create(runtime.storageFactory, {
616
- context,
617
- stateType: repository.stateSchema.typeName,
618
- id: { key: input.id.key },
619
- });
620
- try {
621
- return await history.read({ entityId, view, ...read });
622
- }
623
- finally {
624
- history.close();
625
- }
509
+ return RepositoryStorage.track(repository, () => RepositoryStorage.openRepositoryAgentHistory(repository, runtime.storageFactory, input).read({
510
+ entityId,
511
+ view,
512
+ ...read,
513
+ }));
626
514
  },
627
515
  async agentHistoryPage(repository, entityId, request, tenantId) {
628
516
  if (repository.entityFamily !== "agent")
@@ -631,22 +519,16 @@ export const repositoryAccess = Object.freeze({
631
519
  if (runtime === undefined)
632
520
  throw new Error("Agent history requires an active repository.");
633
521
  const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
522
+ await runtime.onAdmit(context.tenantId);
634
523
  const input = RepositoryStorage.entityStorageInput(repository, context);
635
- const history = AgentHistoryStorageFactories.create(runtime.storageFactory, {
636
- context,
637
- stateType: repository.stateSchema.typeName,
638
- id: { key: input.id.key },
639
- });
640
- try {
641
- return await AgentHistoryReads.readBound({
524
+ return RepositoryStorage.track(repository, () => {
525
+ const history = RepositoryStorage.openRepositoryAgentHistory(repository, runtime.storageFactory, input);
526
+ return AgentHistoryReads.readBound({
642
527
  storage: history,
643
528
  entityId,
644
529
  scope: this.agentHistoryScope(context, input, entityId),
645
530
  }, { kind: "full" }, request);
646
- }
647
- finally {
648
- history.close();
649
- }
531
+ });
650
532
  },
651
533
  /**
652
534
  * Computes the opaque cursor scope for a registered Agent history read.
@@ -758,6 +640,7 @@ export const repositoryAccess = Object.freeze({
758
640
  */
759
641
  bindRuntime(repository, runtime) {
760
642
  repositoryRuntimes.set(repository, Object.freeze(runtime));
643
+ repositoryStorageLifetimes.set(repository, RepositoryStorage.newLifetime());
761
644
  },
762
645
  /**
763
646
  * Removes runtime dispatch state and closes tracked storage handles.
@@ -767,12 +650,18 @@ export const repositoryAccess = Object.freeze({
767
650
  clearRuntime(repository) {
768
651
  repositoryRuntimes.delete(repository);
769
652
  repositoryDispatchGuards.delete(repository);
770
- const handles = repositoryEntityHandles.get(repository);
771
- if (handles !== undefined) {
772
- for (const handle of handles.values())
773
- handle.close();
774
- handles.clear();
775
- }
653
+ return RepositoryStorage.closeRuntime(repository);
654
+ },
655
+ /**
656
+ * Closes idle repository storage during synchronous assembly rollback.
657
+ *
658
+ * @param repository Repository to detach.
659
+ */
660
+ clearRuntimeIdle(repository) {
661
+ RepositoryStorage.assertIdle(repository);
662
+ repositoryRuntimes.delete(repository);
663
+ repositoryDispatchGuards.delete(repository);
664
+ RepositoryStorage.closeRuntimeIdle(repository);
776
665
  },
777
666
  });
778
667
  const inboxDedupMs = 30_000;
@@ -930,14 +819,12 @@ class RepositoryReadScope {
930
819
  throw new Error("Repository read access has finished.");
931
820
  if (query.schema !== this.#repository.stateSchema)
932
821
  throw new TypeError("Repository query must select the receiving Entity state schema.");
822
+ await this.#runtime.onAdmit(this.#context.tenantId);
933
823
  const plan = RepositoryReadPredicates.plan(query.buildPlan());
934
- const storage = RepositoryStorage.openReadEntityStorage(this.#runtime.storageFactory, RepositoryStorage.entityStorageInput(this.#repository, this.#context));
935
- try {
936
- return await storage.current.query(plan);
937
- }
938
- finally {
939
- storage.close();
940
- }
824
+ return RepositoryStorage.track(this.#repository, () => {
825
+ const storage = RepositoryStorage.repositoryReadEntityStorage(this.#repository, this.#runtime.storageFactory, RepositoryStorage.entityStorageInput(this.#repository, this.#context));
826
+ return storage.current.query(plan);
827
+ });
941
828
  }
942
829
  }
943
830
  /**
@@ -1045,7 +932,20 @@ class AggregateExecutionSupport {
1045
932
  ? {}
1046
933
  : { tenantId: this.#storageContext.tenantId });
1047
934
  const entity = this.#instantiateAggregate(entityId, current);
1048
- RepositoryHistoryInternals.bindEntityHistory(entity, storage, entityId, this.#repository.stateSchema);
935
+ RepositoryHistoryInternals.bindEntityHistory(this.#repository, entity, storage, entityId, this.#repository.stateSchema, this.#storageContext.tenantId);
936
+ return this.#loadedAggregate(entityId, current, entity, storage, storageInput);
937
+ }
938
+ /**
939
+ * Returns one restored Aggregate with its captured storage configuration.
940
+ *
941
+ * @param entityId Aggregate identifier.
942
+ * @param current Stored Aggregate snapshot, if present.
943
+ * @param entity Restored Aggregate instance.
944
+ * @param storage Retained Entity storage.
945
+ * @param storageInput Captured storage configuration.
946
+ * @returns Loaded Aggregate for command execution.
947
+ */
948
+ #loadedAggregate(entityId, current, entity, storage, storageInput) {
1049
949
  return Object.freeze({
1050
950
  commits: storage.commits,
1051
951
  current: current === undefined
@@ -1134,9 +1034,7 @@ class AggregateExecutionSupport {
1134
1034
  entity: loaded.storageInput,
1135
1035
  entityId,
1136
1036
  next: record,
1137
- ...(RepositoryStorage.historyConfiguration(this.#repository).stateHistory
1138
- ? { states: [record] }
1139
- : {}),
1037
+ ...(loaded.storageInput.stateHistory ? { states: [record] } : {}),
1140
1038
  diagnostics: diagnostics.map((event) => clone(EventSchema, event)),
1141
1039
  events,
1142
1040
  });
@@ -1337,11 +1235,11 @@ class AggregateCommandExecution {
1337
1235
  const produced = await EntityInvocation.invokeEntityMethod(entity, handler.methodName, message, handler.parameterCount, context);
1338
1236
  const signals = this.#support.normalizeProducedSignals(produced);
1339
1237
  if (signals.length === 0)
1340
- throw new Error("Repository aggregate command handlers must return at least one event.");
1238
+ throw receptionObservation.recordPostHandlerFailure(new Error("Repository aggregate command handlers must return at least one event."));
1341
1239
  RepositoryHandlers.requireDeclaredOutputs(handler, signals);
1342
1240
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, true));
1343
1241
  if (commit.status === "rejected") {
1344
- throw new TransitionValidationError(commit.validation.error);
1242
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1345
1243
  }
1346
1244
  return produced;
1347
1245
  }
@@ -1393,7 +1291,7 @@ class AggregateCommandExecution {
1393
1291
  }
1394
1292
  return create(EventSchema, {
1395
1293
  id: metadata.id,
1396
- message: AnyMessages.pack(schema, message),
1294
+ message: RepositorySignals.packProduced(schema, message),
1397
1295
  context: metadata.context,
1398
1296
  });
1399
1297
  }
@@ -1563,7 +1461,7 @@ class AggregateEventExecution {
1563
1461
  async #commitEntity(entity, producedEvents) {
1564
1462
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, producedEvents));
1565
1463
  if (commit.status === "rejected")
1566
- throw new TransitionValidationError(commit.validation.error);
1464
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1567
1465
  }
1568
1466
  /**
1569
1467
  * Binds reactor results using the pre-dispatch Aggregate version.
@@ -1596,7 +1494,7 @@ class AggregateEventExecution {
1596
1494
  });
1597
1495
  return create(EventSchema, {
1598
1496
  id: metadata.id,
1599
- message: AnyMessages.pack(schema, signal),
1497
+ message: RepositorySignals.packProduced(schema, signal),
1600
1498
  context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId, version),
1601
1499
  });
1602
1500
  }
@@ -1804,9 +1702,7 @@ class ProjectionEventExecution {
1804
1702
  entity: loaded.storageInput,
1805
1703
  entityId,
1806
1704
  next: record,
1807
- ...(RepositoryStorage.historyConfiguration(this.#repository).stateHistory
1808
- ? { states: [record] }
1809
- : {}),
1705
+ ...(loaded.storageInput.stateHistory ? { states: [record] } : {}),
1810
1706
  });
1811
1707
  }
1812
1708
  /**
@@ -1845,7 +1741,7 @@ class ProjectionEventExecution {
1845
1741
  }
1846
1742
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
1847
1743
  if (commit.status === "rejected") {
1848
- throw new TransitionValidationError(commit.validation.error);
1744
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1849
1745
  }
1850
1746
  }
1851
1747
  catch (error) {
@@ -1866,7 +1762,7 @@ class ProjectionEventExecution {
1866
1762
  const context = EntityInvocation.eventHandlerContext(this.#event);
1867
1763
  const result = await EntityInvocation.invokeEntityMethod(entity, subscriber.handler.methodName, message, subscriber.handler.parameterCount, context);
1868
1764
  if (result !== undefined)
1869
- throw new Error(`Projection subscriber "${subscriber.handler.methodName}" must not return signals.`);
1765
+ throw receptionObservation.recordPostHandlerFailure(new Error(`Projection subscriber "${subscriber.handler.methodName}" must not return signals.`));
1870
1766
  }
1871
1767
  /**
1872
1768
  * Invokes state subscribers in one Entity transaction.
@@ -1883,11 +1779,11 @@ class ProjectionEventExecution {
1883
1779
  const context = EntityInvocation.eventHandlerContext(this.#event);
1884
1780
  const result = await EntityInvocation.invokeEntityMethod(entity, subscriber.handler.methodName, state, subscriber.handler.parameterCount, context);
1885
1781
  if (result !== undefined)
1886
- throw new Error(`Projection subscriber "${subscriber.handler.methodName}" must not return signals.`);
1782
+ throw receptionObservation.recordPostHandlerFailure(new Error(`Projection subscriber "${subscriber.handler.methodName}" must not return signals.`));
1887
1783
  }
1888
1784
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
1889
1785
  if (commit.status === "rejected")
1890
- throw new TransitionValidationError(commit.validation.error);
1786
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1891
1787
  }
1892
1788
  catch (error) {
1893
1789
  transactionalEntityAccess.rollback(entity);
@@ -2024,25 +1920,26 @@ class ProcessManagerExecutionSupport {
2024
1920
  *
2025
1921
  * @param loaded Process Manager and commit storage.
2026
1922
  * @param options Stand tenant selection.
2027
- * @param events Produced or diagnostic Events to retain when configured.
2028
- * @param emitted Domain Events emitted by an Agent, excluding the source Event.
1923
+ * @param diagnostics Input and produced Events to retain when configured.
1924
+ * @param producedEvents Delivery Events to save before current state.
2029
1925
  * @returns `true` after a successful commit or unchanged no-op.
2030
1926
  */
2031
- async commit(loaded, options, events, emitted = events) {
1927
+ async commit(loaded, options, diagnostics, producedEvents) {
2032
1928
  const version = RepositoryEntities.repositoryVersion(loaded.entity);
2033
1929
  const priorNumber = loaded.current?.version?.number ?? 0;
2034
1930
  if (version.number === priorNumber) {
2035
- await this.#appendUnchangedDiagnostics(loaded, events);
2036
- await this.#appendAgentEvents(loaded, emitted);
1931
+ await this.#appendUnchangedDiagnostics(loaded, diagnostics);
1932
+ await RepositoryStorage.eventStore(this.#repository, this.#runtime).appendAll(producedEvents);
1933
+ await this.#appendAgentEvents(loaded, producedEvents);
2037
1934
  return true;
2038
1935
  }
2039
1936
  const entityId = loaded.entity.id;
2040
1937
  const state = RepositoryEntities.repositoryState(loaded.entity);
2041
1938
  const lifecycle = RepositoryEntities.repositoryLifecycle(loaded.entity);
2042
1939
  const deferred = await standAccess.deferUpdate(this.#runtime.stand, this.#repository.stateSchema, state, RepositoryStand.standUpdateOptions(options.tenantId, version, lifecycle), this.#repository.metadata);
2043
- await this.#storeProcessManagerRecord(loaded, entityId, events, deferred);
2044
- await this.#appendAgentEvents(loaded, emitted);
2045
- this.#notifyProcessManager(deferred, events);
1940
+ await this.#storeProcessManagerRecord(loaded, entityId, diagnostics, producedEvents, deferred);
1941
+ this.#notifyProcessManager(deferred, diagnostics);
1942
+ await this.#appendAgentEvents(loaded, producedEvents);
2046
1943
  return true;
2047
1944
  }
2048
1945
  /**
@@ -2086,13 +1983,14 @@ class ProcessManagerExecutionSupport {
2086
1983
  *
2087
1984
  * @param loaded Process Manager and commit storage.
2088
1985
  * @param entityId Process Manager identifier.
2089
- * @param events Diagnostic Events to retain when configured.
1986
+ * @param diagnostics Diagnostic Events to retain when configured.
1987
+ * @param producedEvents Delivery Events to save before current state.
2090
1988
  * @param deferred Stand update to notify or cancel.
2091
1989
  * @returns Completion after durable persistence.
2092
1990
  */
2093
- async #storeProcessManagerRecord(loaded, entityId, events, deferred) {
1991
+ async #storeProcessManagerRecord(loaded, entityId, diagnostics, producedEvents, deferred) {
2094
1992
  try {
2095
- await this.#commitProcessManagerRecord(loaded, entityId, events, deferred);
1993
+ await this.#commitProcessManagerRecord(loaded, entityId, diagnostics, producedEvents, deferred);
2096
1994
  entityStateHistoryCaches.get(loaded.entity)?.clear();
2097
1995
  }
2098
1996
  catch (error) {
@@ -2105,11 +2003,12 @@ class ProcessManagerExecutionSupport {
2105
2003
  *
2106
2004
  * @param loaded Loaded Process Manager and commit storage.
2107
2005
  * @param entityId Process Manager identifier.
2108
- * @param events Diagnostic Events to retain when configured.
2006
+ * @param diagnostics Diagnostic Events to retain when configured.
2007
+ * @param producedEvents Delivery Events to save before current state.
2109
2008
  * @param deferred Stand update containing the prepared record.
2110
2009
  * @returns Completion after the storage commit.
2111
2010
  */
2112
- #commitProcessManagerRecord(loaded, entityId, events, deferred) {
2011
+ #commitProcessManagerRecord(loaded, entityId, diagnostics, producedEvents, deferred) {
2113
2012
  const history = RepositoryStorage.historyConfiguration(this.#repository);
2114
2013
  const record = RepositoryStand.preparedRecord(this.#repository, deferred, entityId);
2115
2014
  return loaded.commits.commit({
@@ -2117,10 +2016,11 @@ class ProcessManagerExecutionSupport {
2117
2016
  entity: loaded.storageInput,
2118
2017
  entityId,
2119
2018
  next: record,
2120
- ...(history.stateHistory ? { states: [record] } : {}),
2019
+ ...(loaded.storageInput.stateHistory ? { states: [record] } : {}),
2121
2020
  ...(history.processManagerEventHistory
2122
- ? { diagnostics: events.map((event) => clone(EventSchema, event)) }
2021
+ ? { diagnostics: diagnostics.map((event) => clone(EventSchema, event)) }
2123
2022
  : {}),
2023
+ events: producedEvents.map((event) => clone(EventSchema, event)),
2124
2024
  });
2125
2025
  }
2126
2026
  /**
@@ -2139,1234 +2039,706 @@ class ProcessManagerExecutionSupport {
2139
2039
  }
2140
2040
  }
2141
2041
  /**
2142
- * Advances admitted Agent work only under a provider claim and exact record fence.
2042
+ * Binds normal outgoing Agent signals from one live turn.
2143
2043
  */
2144
- const AgentExecutionRunner = {
2044
+ const AgentLiveOutputs = {
2145
2045
  /**
2146
- * Processes one accepted Agent transition under its provider claim.
2046
+ * Stores only committed domain Events in the Agent's emitted history.
2147
2047
  *
2148
- * @param repository Registered Agent repository.
2149
- * @param tenantId Tenant selected for this invocation.
2150
- * @param key Exact accepted invocation key.
2151
- * @param signal Cancellation signal for the current accepted execution.
2152
- * @returns When the fenced repository operation completes.
2048
+ * @param outputs Prepared outgoing domain signals.
2049
+ * @returns Committed domain Event rows for Agent history.
2153
2050
  */
2154
- async run(repository, tenantId, key, signal) {
2155
- if (signal?.aborted)
2156
- return;
2157
- const runtime = repositoryRuntimes.get(repository);
2158
- if (runtime?.ai === undefined || repository.entityFamily !== "agent")
2159
- throw new Error("Agent execution requires a bound Agent repository and AI registry.");
2160
- const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
2161
- const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
2162
- const executions = storage.agentExecution;
2163
- if (executions === undefined)
2164
- throw new Error("Agent execution storage is required.");
2165
- const token = randomUUID();
2166
- const claimed = await executions.claim(key, token, addMillis(Time.currentTime(), 30_000));
2167
- if (claimed === undefined)
2168
- return;
2169
- const session = new AgentExecutionSession(executions, claimed.record, token, claimed.preferences);
2170
- await this.runClaimed(repository, runtime, tenantId, session, signal);
2051
+ emittedHistory(outputs) {
2052
+ return outputs.flatMap((output) => {
2053
+ if (output.case !== "event" || output.value.context?.rejection !== undefined)
2054
+ return [];
2055
+ const event = output.value;
2056
+ const occurredAt = event.context?.timestamp;
2057
+ if (occurredAt === undefined)
2058
+ throw new Error("Agent emitted Event requires its original timestamp.");
2059
+ return [
2060
+ create(AgentHistoryEntrySchema, {
2061
+ occurredAt,
2062
+ item: { case: "domainEvent", value: event },
2063
+ }),
2064
+ ];
2065
+ });
2171
2066
  },
2172
2067
  /**
2173
- * Updates and releases one exact claim while its saved work advances.
2068
+ * Binds one declared result using its generated Event or Command role.
2174
2069
  *
2175
2070
  * @param repository Registered Entity repository for this operation.
2176
2071
  * @param runtime Bounded Context storage and publication services.
2177
- * @param tenantId Tenant selected for this accepted signal.
2178
- * @param session Fenced Agent execution session.
2179
- * @param signal Cancellation signal for the current accepted execution.
2180
- * @returns When the fenced repository operation completes.
2072
+ * @param routing Registered generated handler routing metadata.
2073
+ * @param source Original accepted source signal.
2074
+ * @param entityId Typed recipient Entity identifier.
2075
+ * @param version Entity version associated with the signal.
2076
+ * @param signal Original domain signal or execution cancellation signal.
2077
+ * @returns Typed outgoing signal with its original envelope.
2181
2078
  */
2182
- async runClaimed(repository, runtime, tenantId, session, signal) {
2183
- const abort = () => {
2184
- session.stop();
2185
- };
2186
- signal?.addEventListener("abort", abort, { once: true });
2187
- if (signal?.aborted)
2188
- abort();
2189
- const renewal = setInterval(() => {
2190
- // spine-log-boundary: server.agent_lease_renewal
2191
- void session.renew(addMillis(Time.currentTime(), 30_000)).catch(() => undefined);
2192
- }, 10_000);
2193
- try {
2194
- if (session.signal.aborted)
2195
- return;
2196
- await this.advanceClaimed(repository, runtime, tenantId, session);
2197
- }
2198
- catch (error) {
2199
- if (signal?.aborted)
2200
- return;
2201
- await this.handleRunFailure(repository, runtime, tenantId, session, error);
2202
- }
2203
- finally {
2204
- clearInterval(renewal);
2205
- signal?.removeEventListener("abort", abort);
2206
- session.stop();
2207
- }
2079
+ bindOutput(repository, runtime, routing, source, entityId, version, signal) {
2080
+ const typeName = EntityInvocation.messageTypeName(signal);
2081
+ const eventSchema = routing.producedEventSchemas.find((entry) => entry.typeName === typeName);
2082
+ if (eventSchema !== undefined)
2083
+ return this.bindEventOutput(repository, runtime, source, entityId, version, signal, eventSchema);
2084
+ const schema = routing.producedCommandSchemas.find((entry) => entry.typeName === typeName);
2085
+ if (schema === undefined)
2086
+ throw new Error(`Agent cannot emit undeclared signal "${typeName}".`);
2087
+ return this.bindCommandOutput(runtime, source, signal, schema);
2208
2088
  },
2209
2089
  /**
2210
- * Handles transient failures while giving expired work a final audit.
2090
+ * Creates the original domain Event envelope and producer context.
2211
2091
  *
2212
2092
  * @param repository Registered Entity repository for this operation.
2213
2093
  * @param runtime Bounded Context storage and publication services.
2214
- * @param tenantId Tenant selected for this accepted signal.
2215
- * @param session Fenced Agent execution session.
2216
- * @param error Failure raised while processing this accepted signal.
2217
- * @returns When the fenced repository operation completes.
2094
+ * @param source Original accepted source signal.
2095
+ * @param entityId Typed recipient Entity identifier.
2096
+ * @param version Entity version associated with the signal.
2097
+ * @param signal Original domain signal or execution cancellation signal.
2098
+ * @param schema Generated message descriptor for the signal.
2099
+ * @returns Typed original Event envelope for one normal EventBus handoff.
2218
2100
  */
2219
- async handleRunFailure(repository, runtime, tenantId, session, error) {
2220
- if (session.record().status === AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_DELIVERY)
2221
- throw error;
2222
- if (error instanceof AgentUncertainAttemptError) {
2223
- const reason = this.hasUnresolvedToolWrite(session.record())
2224
- ? "TOOL_OUTCOME_UNKNOWN"
2225
- : "MODEL_OUTCOME_UNKNOWN";
2226
- return this.terminate(repository, runtime, tenantId, session, reason);
2227
- }
2228
- if (error instanceof AgentExecutionFault) {
2229
- if (session.signal.aborted)
2230
- throw error;
2231
- return this.terminate(repository, runtime, tenantId, session, error.reason);
2232
- }
2233
- if (this.deadlineExpired(session.record()))
2234
- return this.terminate(repository, runtime, tenantId, session, "DEADLINE_EXCEEDED");
2235
- throw error;
2101
+ bindEventOutput(repository, runtime, source, entityId, version, signal, schema) {
2102
+ const metadata = source.case === "command"
2103
+ ? runtime.signalMetadata.eventFromCommand(source.value, { version: version.number })
2104
+ : runtime.signalMetadata.eventFromEvent(source.value, { version: version.number });
2105
+ const event = create(EventSchema, {
2106
+ id: metadata.id,
2107
+ message: RepositorySignals.packValidated(schema, signal),
2108
+ context: RepositorySignals.eventContextWithProducer(metadata.context, repository, entityId, version),
2109
+ });
2110
+ return { case: "event", value: event };
2236
2111
  },
2237
2112
  /**
2238
- * Processes one claimed record according to its saved completion or request state.
2113
+ * Creates the original Command envelope from its accepted origin.
2239
2114
  *
2240
- * @param repository Registered Entity repository for this operation.
2241
2115
  * @param runtime Bounded Context storage and publication services.
2242
- * @param tenantId Tenant selected for this accepted signal.
2243
- * @param session Fenced Agent execution session.
2244
- * @returns When the fenced repository operation completes.
2116
+ * @param source Original accepted source signal.
2117
+ * @param signal Original domain signal or execution cancellation signal.
2118
+ * @param schema Generated message descriptor for the signal.
2119
+ * @returns Typed original Command envelope for durable delivery.
2245
2120
  */
2246
- async advanceClaimed(repository, runtime, tenantId, session) {
2247
- if (this.hasUnresolvedPhysicalRequest(session.record()))
2248
- return this.terminate(repository, runtime, tenantId, session, this.hasUnresolvedToolWrite(session.record())
2249
- ? "TOOL_OUTCOME_UNKNOWN"
2250
- : "MODEL_OUTCOME_UNKNOWN");
2251
- if (session.record().status === AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_DELIVERY)
2252
- return this.deliver(runtime, session);
2253
- if (this.deadlineExpired(session.record()))
2254
- return this.terminate(repository, runtime, tenantId, session, "DEADLINE_EXCEEDED");
2255
- return this.execute(repository, runtime, tenantId, session);
2121
+ bindCommandOutput(runtime, source, signal, schema) {
2122
+ const metadata = source.case === "command"
2123
+ ? runtime.signalMetadata.commandFromCommand(source.value)
2124
+ : runtime.signalMetadata.commandFromEvent(source.value);
2125
+ const command = create(CommandSchema, {
2126
+ id: metadata.id,
2127
+ message: RepositorySignals.packValidated(schema, signal),
2128
+ context: metadata.context,
2129
+ });
2130
+ return { case: "command", value: command };
2256
2131
  },
2132
+ };
2133
+ /**
2134
+ * Waits only for a current in-process handler invocation.
2135
+ */
2136
+ const AgentLiveHandler = {
2257
2137
  /**
2258
- * Tests the original saved deadline without granting recovery fresh time.
2138
+ * Awaits an application handler within its live deadline and current session.
2259
2139
  *
2260
- * @param record Durable Agent execution record.
2261
- * @returns Whether the checked condition holds.
2140
+ * @typeParam Value Handler result.
2141
+ * @param work Already-started generated handler invocation.
2142
+ * @param signal Cancellation when live authority ends.
2143
+ * @param deadlineEpochMs Live invocation deadline.
2144
+ * @returns Handler result only while its session and deadline remain valid.
2262
2145
  */
2263
- deadlineExpired(record) {
2264
- const deadline = record.started?.deadline;
2265
- return (deadline !== undefined &&
2266
- Time.currentTimeMillis() >=
2267
- Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000));
2146
+ async awaitHandler(work, signal, deadlineEpochMs) {
2147
+ let timer;
2148
+ let abort = () => undefined;
2149
+ const cutoff = new Promise((_resolve, reject) => {
2150
+ abort = () => {
2151
+ reject(new Error("Agent handler session ended."));
2152
+ };
2153
+ const tick = () => {
2154
+ if (deadlineEpochMs === undefined)
2155
+ return;
2156
+ const remaining = deadlineEpochMs - Time.currentTimeMillis();
2157
+ if (remaining <= 0)
2158
+ reject(new Error("Agent handler deadline expired."));
2159
+ else
2160
+ timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
2161
+ };
2162
+ signal.addEventListener("abort", abort, { once: true });
2163
+ if (signal.aborted)
2164
+ abort();
2165
+ else
2166
+ tick();
2167
+ });
2168
+ try {
2169
+ const result = await Promise.race([work, cutoff]);
2170
+ if (signal.aborted ||
2171
+ (deadlineEpochMs !== undefined && Time.currentTimeMillis() >= deadlineEpochMs))
2172
+ throw new Error("Agent handler deadline or session ended.");
2173
+ return result;
2174
+ }
2175
+ finally {
2176
+ clearTimeout(timer);
2177
+ signal.removeEventListener("abort", abort);
2178
+ }
2268
2179
  },
2180
+ };
2181
+ /**
2182
+ * Runs one receipt-admitted Agent signal without an execution record.
2183
+ */
2184
+ class AgentLiveRunner {
2185
+ repository;
2186
+ runtime;
2187
+ tenantId;
2188
+ entityId;
2189
+ invocation;
2190
+ handlers;
2191
+ signal;
2192
+ checkSession;
2193
+ #deadlineEpochMs;
2194
+ #deadlineSampled = false;
2195
+ /**
2196
+ * Binds one original signal, resolved handlers, and the current repository runtime.
2197
+ *
2198
+ * @param checkSession Callback that validates the current execution session.
2199
+ * @param entityId Typed Entity identifier.
2200
+ * @param handlers Registered generated handlers.
2201
+ * @param invocation Captured live Agent invocation.
2202
+ * @param repository Repository configured for this Entity.
2203
+ * @param runtime Current bounded-context runtime.
2204
+ * @param signal Signal or cancellation signal for this operation.
2205
+ * @param tenantId Captured tenant identifier.
2206
+ */
2207
+ constructor(repository, runtime, tenantId, entityId, invocation, handlers, signal, checkSession) {
2208
+ this.repository = repository;
2209
+ this.runtime = runtime;
2210
+ this.tenantId = tenantId;
2211
+ this.entityId = entityId;
2212
+ this.invocation = invocation;
2213
+ this.handlers = handlers;
2214
+ this.signal = signal;
2215
+ this.checkSession = checkSession;
2216
+ }
2217
+ /**
2218
+ * Starts the serialized turn by loading current state and committed model preferences.
2219
+ *
2220
+ * @returns Completion after admitted Agent work finishes.
2221
+ */
2222
+ async run() {
2223
+ if (this.signal.aborted)
2224
+ return;
2225
+ await this.checkSession();
2226
+ await this.runtime.onAdmit(this.tenantId);
2227
+ await RepositoryStorage.track(this.repository, async () => {
2228
+ const loaded = await new ProcessManagerExecutionSupport(this.repository, this.runtime).load(this.entityId, this.tenantId === undefined ? {} : { tenantId: this.tenantId });
2229
+ const expected = await loaded.commits.readAgentPreferences(this.entityId);
2230
+ if (this.signal.aborted)
2231
+ return;
2232
+ const ai = this.runtime.ai;
2233
+ if (ai === undefined)
2234
+ throw new Error("Live Agent execution requires an AI registry.");
2235
+ const preferences = expected?.map((entry) => clone(ModelPreferenceSchema, entry)) ?? [];
2236
+ const audit = this.audit(loaded);
2237
+ let selected;
2238
+ try {
2239
+ selected = await this.select(preferences);
2240
+ }
2241
+ catch (error) {
2242
+ if (!(error instanceof AgentLiveFault) || error.reason !== "MODEL_USE_DENIED")
2243
+ throw error;
2244
+ await this.recordModelDenial(audit, error);
2245
+ throw error;
2246
+ }
2247
+ const budget = new AgentLiveBudget(this.limits(ai));
2248
+ await this.dispatchStart(loaded);
2249
+ const produced = await this.produce(loaded, selected, preferences, audit, budget);
2250
+ this.signal.throwIfAborted();
2251
+ await this.checkSession();
2252
+ await this.complete(loaded, expected, preferences, produced);
2253
+ });
2254
+ }
2269
2255
  /**
2270
- * Checks for a dispatched model reservation whose response was never saved.
2256
+ * Writes only definite pre-handler model-use denial with its original source.
2271
2257
  *
2272
- * @param record Durable Agent execution record.
2273
- * @returns Whether the checked condition holds.
2258
+ * @param audit Retained history audit for this invocation.
2259
+ * @param denial Typed model-use denial to record.
2260
+ * @returns Completion after the exact pre-handler denial is saved to Agent history.
2274
2261
  */
2275
- hasUnresolvedPhysicalRequest(record) {
2276
- const model = record.journal.some((entry) => {
2277
- if (entry.evidence.case !== "attempt")
2278
- return false;
2279
- const attempt = entry.evidence.value;
2280
- return (attempt.transportReserved &&
2281
- attempt.response.case === undefined &&
2282
- !this.hasSavedOperationResult(record, attempt.operation?.value));
2262
+ async recordModelDenial(audit, denial) {
2263
+ const agent = create(MessageIdSchema, {
2264
+ id: this.invocation.recipientId,
2265
+ typeUrl: TypeUrls.derive(this.repository.stateSchema),
2283
2266
  });
2284
- return model || this.hasUnresolvedToolWrite(record);
2285
- },
2267
+ const fact = create(AgentInvocationTerminatedSchema, {
2268
+ agent,
2269
+ sourceSignal: AgentLiveInteractionEvents.sourceRef(this.invocation),
2270
+ reason: "MODEL_USE_DENIED",
2271
+ terminatedAt: Time.currentTime(),
2272
+ });
2273
+ await audit.saveDenial(fact, denial);
2274
+ }
2286
2275
  /**
2287
- * Checks for a dispatched write with no recorded tool result.
2276
+ * Converts only a declared Command rejection into a normal outgoing Event.
2288
2277
  *
2289
- * @param record Durable Agent execution record.
2290
- * @returns Whether the checked condition holds.
2278
+ * @param audit Retained history audit for this invocation.
2279
+ * @param budget Invocation-wide resource budget.
2280
+ * @param loaded Current Entity and retained storage for this turn.
2281
+ * @param preferences Current or staged model preferences.
2282
+ * @param selected Models or selection retained for this invocation.
2283
+ * @returns The declared rejection Event, when a Command rejects.
2291
2284
  */
2292
- hasUnresolvedToolWrite(record) {
2293
- return record.journal.some((entry) => {
2294
- if (entry.evidence.case !== "tool")
2295
- return false;
2296
- const tool = entry.evidence.value;
2297
- return (tool.request?.effect === ToolEffect.WRITE &&
2298
- tool.response === undefined &&
2299
- (tool.dispatched || tool.outcomeUnknown) &&
2300
- !this.hasSavedOperationResult(record, tool.operation?.value));
2301
- });
2302
- },
2285
+ async produce(loaded, selected, preferences, audit, budget) {
2286
+ try {
2287
+ return {
2288
+ case: "produced",
2289
+ values: await this.invoke(loaded, selected, preferences, audit, budget),
2290
+ };
2291
+ }
2292
+ catch (error) {
2293
+ if (!RejectionThrowable.is(error) || this.invocation.source.case !== "command")
2294
+ throw error;
2295
+ const handler = this.handlers[0];
2296
+ if (handler === undefined)
2297
+ throw new Error("Agent rejection requires a Command assignee.");
2298
+ RepositorySignals.requireDeclaredRejection(handler.handler, error);
2299
+ preferences.length = 0;
2300
+ const output = RepositorySignals.rejectionEvent(this.runtime, this.repository, this.invocation.source.value, this.entityId, error);
2301
+ return { case: "rejected", output: { case: "event", value: output } };
2302
+ }
2303
+ }
2303
2304
  /**
2304
- * Checks for a settled named result even when physical evidence is incomplete.
2305
+ * Finds each enabled model from the preference image loaded at turn start.
2305
2306
  *
2306
- * @param record Durable Agent execution record.
2307
- * @param id Saved signal, operation, or Entity identifier.
2308
- * @returns Whether the checked condition holds.
2307
+ * @param preferences Current or staged model preferences.
2308
+ * @returns The selected model image for the current Agent turn.
2309
2309
  */
2310
- hasSavedOperationResult(record, id) {
2311
- return record.journal.some((entry) => entry.evidence.case === "operation" &&
2312
- entry.evidence.value.operation?.value === id &&
2313
- entry.evidence.value.result.case !== undefined);
2314
- },
2310
+ async select(preferences) {
2311
+ const ai = this.runtime.ai;
2312
+ const configuration = repositoryAccess.agentConfiguration(this.repository).ai;
2313
+ if (ai === undefined || configuration === undefined)
2314
+ throw new Error("Live Agent execution requires registered model policy.");
2315
+ const scope = AgentModelSelection.scopeLive(this.invocation, this.repository.stateSchema, this.runtime.signalMetadata, this.tenantId);
2316
+ const selected = new Map();
2317
+ for (const kind of ["generation", "decision"]) {
2318
+ if (!configuration.models.some((model) => model.definition.kind === kind))
2319
+ continue;
2320
+ await this.checkSession();
2321
+ const value = await AgentModelSelection.selectLiveKind(ai, configuration, scope, preferences, this.signal, this.deadline(ai), kind, this.invocation.source.value.message);
2322
+ selected.set(value.kind, value);
2323
+ }
2324
+ return selected;
2325
+ }
2315
2326
  /**
2316
- * Records termination of an unreplayable physical request under the provider fence.
2327
+ * Captures the configured live invocation deadline.
2317
2328
  *
2318
- * @param repository Registered Entity repository for this operation.
2319
- * @param runtime Bounded Context storage and publication services.
2320
- * @param tenantId Tenant selected for this accepted signal.
2321
- * @param session Fenced Agent execution session.
2322
- * @param reason Safe terminal reason retained in Agent audit.
2323
- * @returns When the fenced repository operation completes.
2329
+ * @param ai Configured AI capability.
2330
+ * @returns The sampled live invocation deadline, when configured.
2324
2331
  */
2325
- async terminate(repository, runtime, tenantId, session, reason) {
2326
- const record = session.record();
2327
- const event = this.terminationEvent(repository, runtime, tenantId, record, reason);
2328
- const occurredAt = event.context?.timestamp;
2329
- if (occurredAt === undefined)
2330
- throw new Error("Agent termination Event requires Time.");
2331
- runtime.registerSystemEventSchema(AgentInvocationTerminatedSchema);
2332
- await session.update((next) => {
2333
- next.status = AgentInvocationStatus.AGENT_INVOCATION_TERMINATED;
2334
- next.terminalFailure = create(AiDiagnosticIdSchema, { value: randomUUID() });
2335
- return next;
2336
- }, [
2337
- create(AgentHistoryEntrySchema, {
2338
- occurredAt,
2339
- item: { case: "systemEvent", value: event },
2340
- }),
2341
- ]);
2342
- await runtime.publishAgentSystemEvent(event);
2343
- },
2344
- /**
2345
- * Builds one original termination envelope for a saved terminal reason.
2346
- *
2347
- * @param repository Registered Entity repository for this operation.
2348
- * @param runtime Bounded Context storage and publication services.
2349
- * @param tenantId Tenant selected for this accepted signal.
2350
- * @param record Durable Agent execution record.
2351
- * @param reason Safe terminal reason retained in Agent audit.
2352
- * @returns Original AgentInvocationTerminated System Event envelope.
2353
- */
2354
- terminationEvent(repository, runtime, tenantId, record, reason) {
2355
- const accepted = record.accepted;
2356
- if (accepted?.recipientId === undefined)
2357
- throw new Error("Agent termination requires the original typed recipient.");
2358
- const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2359
- const unresolvedToolCalls = this.unresolvedToolCalls(record);
2360
- const payload = create(AgentInvocationTerminatedSchema, {
2361
- agent: scope.agent,
2362
- sourceSignal: scope.source,
2363
- reason,
2364
- unresolvedAttempts: this.unresolvedAttempts(record),
2365
- unresolvedToolCalls,
2366
- terminatedAt: Time.currentTime(),
2367
- });
2368
- return AgentInteractionEvents.envelope(accepted, runtime.signalMetadata, accepted.recipientId, AgentInvocationTerminatedSchema, payload);
2369
- },
2332
+ deadline(ai) {
2333
+ if (this.#deadlineSampled)
2334
+ return this.#deadlineEpochMs;
2335
+ this.#deadlineSampled = true;
2336
+ const duration = this.limits(ai).deadlineMs;
2337
+ this.#deadlineEpochMs =
2338
+ duration === undefined ? undefined : Time.currentTimeMillis() + duration;
2339
+ return this.#deadlineEpochMs;
2340
+ }
2370
2341
  /**
2371
- * Returns IDs only for physically reserved model requests without saved outcomes.
2342
+ * Applies only the narrower per-repository bounds to the shared registry policy.
2372
2343
  *
2373
- * @param record Durable execution journal.
2374
- * @returns Original attempt IDs for unresolved physical sends.
2344
+ * @param ai Configured AI capability.
2345
+ * @returns The current configured limits for the start audit.
2375
2346
  */
2376
- unresolvedAttempts(record) {
2377
- return record.journal.flatMap((entry) => {
2378
- if (entry.evidence.case !== "attempt")
2379
- return [];
2380
- const attempt = entry.evidence.value;
2381
- return attempt.transportReserved &&
2382
- attempt.response.case === undefined &&
2383
- !this.hasSavedOperationResult(record, attempt.operation?.value) &&
2384
- attempt.attempt !== undefined
2385
- ? [attempt.attempt]
2386
- : [];
2387
- });
2388
- },
2347
+ limits(ai) {
2348
+ const global = registryOptions(ai).invocationLimits ?? {};
2349
+ const local = repositoryAccess.agentConfiguration(this.repository).ai?.invocationLimits ?? {};
2350
+ const limits = {};
2351
+ const keys = [
2352
+ "operations",
2353
+ "modelRequests",
2354
+ "toolCalls",
2355
+ "recordedReads",
2356
+ "deadlineMs",
2357
+ "totalInputBytes",
2358
+ "totalOutputBytes",
2359
+ ];
2360
+ for (const key of keys) {
2361
+ const first = global[key];
2362
+ const second = local[key];
2363
+ const value = first === undefined ? second : second === undefined ? first : Math.min(first, second);
2364
+ if (value !== undefined)
2365
+ limits[key] = value;
2366
+ }
2367
+ return limits;
2368
+ }
2389
2369
  /**
2390
- * Returns exact IDs for dispatched write effects without saved results.
2370
+ * Binds one original System audit writer to retained Agent history.
2391
2371
  *
2392
- * @param record Durable Agent execution record.
2393
- * @returns Original ToolCall IDs for unresolved dispatched writes.
2372
+ * @param loaded Current Entity and retained storage for this turn.
2373
+ * @returns The history writer bound to this Agent and source.
2394
2374
  */
2395
- unresolvedToolCalls(record) {
2396
- return record.journal.flatMap((entry) => {
2397
- if (entry.evidence.case !== "tool")
2398
- return [];
2399
- const tool = entry.evidence.value;
2400
- return tool.request?.effect === ToolEffect.WRITE &&
2401
- tool.response === undefined &&
2402
- (tool.dispatched || tool.outcomeUnknown) &&
2403
- tool.request.call !== undefined
2404
- ? [tool.request.call]
2405
- : [];
2375
+ audit(loaded) {
2376
+ const history = loaded.agentHistory;
2377
+ if (history === undefined)
2378
+ throw new Error("Live Agent execution requires history storage.");
2379
+ 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 () => {
2380
+ this.signal.throwIfAborted();
2381
+ await this.checkSession();
2382
+ this.signal.throwIfAborted();
2406
2383
  });
2407
- },
2408
- /**
2409
- * Persists start facts, runs selected handlers, and conditionally completes a no-op.
2410
- *
2411
- * @param repository Agent registration.
2412
- * @param runtime Bound services.
2413
- * @param tenantId Complete tenant, when applicable.
2414
- * @param session Fenced Agent execution session.
2415
- * @returns When the fenced repository operation completes.
2416
- */
2417
- async execute(repository, runtime, tenantId, session) {
2418
- const accepted = session.record().accepted;
2419
- if (accepted?.key === undefined || accepted.recipientId === undefined)
2420
- throw new Error("Claimed Agent invocation lacks accepted source and recipient.");
2421
- const entityId = InboxMessages.targetEntityId(accepted.recipientId, repository.idField);
2422
- const support = new ProcessManagerExecutionSupport(repository, runtime);
2423
- const loaded = await support.load(entityId, tenantId === undefined ? {} : { tenantId });
2424
- const handlers = this.handlers(repository, accepted);
2425
- const configuration = repositoryAccess.agentConfiguration(repository);
2426
- if (runtime.ai === undefined)
2427
- throw new Error("Agent execution requires a bound AI registry.");
2428
- this.assertRevisions(repository, accepted, handlers, runtime.ai, configuration);
2429
- const record = session.record().started === undefined
2430
- ? await this.start(repository, runtime, session, loaded, entityId, tenantId)
2431
- : await this.selectMissingModels(repository, runtime, session, accepted, tenantId);
2432
- if (session.signal.aborted)
2433
- return;
2434
- const completed = await this.completeExecution(repository, runtime, tenantId, session, loaded, accepted, handlers, entityId, record.started?.initialVersion ?? create(VersionSchema));
2435
- if (completed.status === AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_DELIVERY)
2436
- await this.deliver(runtime, session);
2437
- },
2384
+ }
2438
2385
  /**
2439
- * Creates or rejects the draft before one conditional Entity completion.
2386
+ * Writes the genuine dispatch System row before publishing its original Event.
2440
2387
  *
2441
- * @param repository Registered Entity repository for this operation.
2442
- * @param runtime Bounded Context storage and publication services.
2443
- * @param tenantId Tenant selected for this accepted signal.
2444
- * @param session Fenced Agent execution session.
2445
- * @param loaded Loaded Entity and its repository storage state.
2446
- * @param accepted Durable original signal and saved handler bindings.
2447
- * @param handlers Generated handlers in their saved order.
2448
- * @param entityId Typed recipient Entity identifier.
2449
- * @param initialVersion Entity version before selected handlers run.
2450
- * @returns Record after conditional Entity and execution completion.
2388
+ * @param loaded Current Entity and retained storage for this turn.
2389
+ * @returns Completion after the original dispatch System Event is saved.
2451
2390
  */
2452
- async completeExecution(repository, runtime, tenantId, session, loaded, accepted, handlers, entityId, initialVersion) {
2453
- const outputs = await this.outputsOrRejection(repository, runtime, tenantId, session, loaded.entity, accepted, handlers, entityId, initialVersion);
2454
- return this.complete(repository, runtime, tenantId, session, loaded, entityId, outputs);
2455
- },
2391
+ async dispatchStart(loaded) {
2392
+ await this.checkSession();
2393
+ const source = this.invocation.source;
2394
+ const event = source.case === "command"
2395
+ ? HandlerDispatchPublisher.commandEvent(this.runtime, this.repository, source.value, this.entityId)
2396
+ : HandlerDispatchPublisher.reactorEvent(this.runtime, this.repository, source.value, this.entityId);
2397
+ const occurredAt = event.context?.timestamp;
2398
+ if (occurredAt === undefined || loaded.agentHistory === undefined)
2399
+ throw new Error("Agent dispatch audit requires timestamp and history.");
2400
+ await loaded.agentHistory.appendAll(this.entityId, [
2401
+ create(AgentHistoryEntrySchema, { occurredAt, item: { case: "systemEvent", value: event } }),
2402
+ ]);
2403
+ await this.checkSession();
2404
+ if (source.case === "command")
2405
+ HandlerDispatchPublisher.publishCommand(this.runtime, event);
2406
+ else
2407
+ HandlerDispatchPublisher.publishReactor(this.runtime, event);
2408
+ }
2456
2409
  /**
2457
- * Rejects a Command draft and persists only its declared rejection.
2410
+ * Executes current generated handlers inside the Entity's local transaction.
2458
2411
  *
2459
- * @param repository Registered Entity repository for this operation.
2460
- * @param runtime Bounded Context storage and publication services.
2461
- * @param tenantId Tenant selected for this accepted signal.
2462
- * @param session Fenced Agent execution session.
2463
- * @param entity Loaded recipient Entity instance.
2464
- * @param accepted Durable original signal and saved handler bindings.
2465
- * @param handlers Generated handlers in their saved order.
2466
- * @param entityId Typed recipient Entity identifier.
2467
- * @param initialVersion Entity version before selected handlers run.
2468
- * @returns Prepared outgoing signals, or only the declared rejection.
2412
+ * @param audit Retained history audit for this invocation.
2413
+ * @param budget Invocation-wide resource budget.
2414
+ * @param loaded Current Entity and retained storage for this turn.
2415
+ * @param preferences Current or staged model preferences.
2416
+ * @param selected Models or selection retained for this invocation.
2417
+ * @returns The typed model result with its operation identifier.
2469
2418
  */
2470
- async outputsOrRejection(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion) {
2419
+ async invoke(loaded, selected, preferences, audit, budget) {
2420
+ transactionalEntityAccess.start(loaded.entity);
2471
2421
  try {
2472
- return await this.produceOutputs(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion);
2422
+ const produced = [];
2423
+ for (const handler of this.handlers)
2424
+ produced.push(...(await this.invokeHandler(loaded, handler, selected, preferences, audit, budget)));
2425
+ const commit = await commitFenced(loaded.entity, (current) => transactionalEntityAccess.commit(current));
2426
+ if (commit.status === "rejected")
2427
+ throw new TransitionValidationError(commit.validation.error);
2428
+ return produced;
2473
2429
  }
2474
2430
  catch (error) {
2475
- if (!RejectionThrowable.is(error) || accepted.signal.case !== "command")
2476
- throw error;
2477
- const handler = handlers[0];
2478
- if (handler === undefined)
2479
- throw new Error("Agent rejection requires a Command assignee.");
2480
- RepositorySignals.requireDeclaredRejection(handler.handler, error);
2481
- session.discardPreferenceChanges();
2482
- return [this.rejectionOutput(runtime, repository, accepted.signal.value, entityId, error)];
2431
+ transactionalEntityAccess.rollback(loaded.entity);
2432
+ throw error;
2483
2433
  }
2484
- },
2485
- /**
2486
- * Wraps the original rejection Event as one pending saved output.
2487
- *
2488
- * @param runtime Bounded Context storage and publication services.
2489
- * @param repository Registered Entity repository for this operation.
2490
- * @param command Original typed Command envelope.
2491
- * @param entityId Typed recipient Entity identifier.
2492
- * @param error Failure raised while processing this accepted signal.
2493
- * @returns Prepared original rejection Event for durable delivery.
2494
- */
2495
- rejectionOutput(runtime, repository, command, entityId, error) {
2496
- return create(AgentOutgoingSignalSchema, {
2497
- signal: {
2498
- case: "event",
2499
- value: RepositorySignals.rejectionEvent(runtime, repository, command, entityId, error),
2500
- },
2501
- });
2502
- },
2503
- /**
2504
- * Restores handler results and binds their declared original output envelopes.
2505
- *
2506
- * @param repository Registered Entity repository for this operation.
2507
- * @param runtime Bounded Context storage and publication services.
2508
- * @param tenantId Tenant selected for this accepted signal.
2509
- * @param session Fenced Agent execution session.
2510
- * @param entity Loaded recipient Entity instance.
2511
- * @param accepted Durable original signal and saved handler bindings.
2512
- * @param handlers Generated handlers in their saved order.
2513
- * @param entityId Typed recipient Entity identifier.
2514
- * @param initialVersion Entity version before selected handlers run.
2515
- * @returns Prepared outgoing signals from accepted handler results.
2516
- */
2517
- async produceOutputs(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion) {
2518
- const produced = await this.invoke(repository, runtime, tenantId, session, entity, accepted, handlers);
2519
- return this.bindOutputs(repository, runtime, accepted, entityId, initialVersion, produced);
2520
- },
2521
- /**
2522
- * Commits changed state and the fenced execution record as one provider mutation.
2523
- *
2524
- * @param repository Agent registration.
2525
- * @param runtime Bounded Context Stand and provider services.
2526
- * @param tenantId Complete delivery tenant.
2527
- * @param session Serialized provider claim and current record.
2528
- * @param loaded Entity after handler transaction commit.
2529
- * @param entityId Original typed recipient.
2530
- * @param outputs Original emitted Event or Command envelopes.
2531
- * @returns Complete record after the atomic provider mutation.
2532
- */
2533
- async complete(repository, runtime, tenantId, session, loaded, entityId, outputs) {
2534
- await this.authorizePreferenceChanges(repository, runtime, tenantId, session);
2535
- const version = RepositoryEntities.repositoryVersion(loaded.entity);
2536
- const preferences = session.preferences();
2537
- const emitted = this.emittedHistory(outputs);
2538
- const preferenceEvents = this.preferenceEvents(repository, runtime, tenantId, session);
2539
- const historyEntries = [...emitted, ...this.preferenceHistory(preferenceEvents)];
2540
- const makeNext = (record) => this.completionFacts(record, version, outputs, preferences);
2541
- const completed = await this.commitCompletion(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries);
2542
- for (const event of preferenceEvents)
2543
- await runtime.publishAgentSystemEvent(event);
2544
- return completed;
2545
- },
2434
+ }
2546
2435
  /**
2547
- * Checks effective staged model changes before any completion mutation.
2436
+ * Executes one handler with live AI and read facades scoped to its returned work.
2548
2437
  *
2549
- * @param repository Agent registration and model policy.
2550
- * @param runtime Bounded Context and AI registry.
2551
- * @param tenantId Accepted tenant when applicable.
2552
- * @param session Fenced Agent execution and staged preferences.
2553
- * @returns When every effective change is authorized.
2438
+ * @param audit Retained history audit for this invocation.
2439
+ * @param budget Invocation-wide resource budget.
2440
+ * @param handler Selected generated handler.
2441
+ * @param loaded Current Entity and retained storage for this turn.
2442
+ * @param preferences Current or staged model preferences.
2443
+ * @param selected Models or selection retained for this invocation.
2444
+ * @returns The declared handler result scoped to live capabilities.
2554
2445
  */
2555
- async authorizePreferenceChanges(repository, runtime, tenantId, session) {
2556
- const policy = repositoryAccess.agentConfiguration(repository).ai;
2557
- if (policy?.authorizeSelection === undefined)
2558
- return;
2559
- const accepted = session.record().accepted;
2560
- if (runtime.ai === undefined || accepted === undefined)
2561
- throw new Error("Agent model preference requires accepted start facts.");
2562
- const deadlineMs = this.savedModelDeadline(session, "preference");
2563
- const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2564
- for (const kind of [AiModelKind.GENERATION, AiModelKind.DECISION]) {
2565
- const previous = this.preferenceModel(session.initialPreferences(), kind);
2566
- const selected = this.preferenceModel(session.preferences(), kind);
2567
- if (previous === undefined && selected === undefined)
2568
- continue;
2569
- if (previous !== undefined &&
2570
- selected !== undefined &&
2571
- equals(ModelRefSchema, previous, selected))
2572
- continue;
2573
- await AgentModelSelection.authorizeSelection(runtime.ai, policy, scope, selected, session.signal, deadlineMs);
2446
+ async invokeHandler(loaded, handler, selected, preferences, audit, budget) {
2447
+ const { facade, actor, deadline } = this.handlerFacade(selected, preferences, audit, budget);
2448
+ const historyScope = repositoryAccess.agentHistoryScope(loaded.storageInput.context, loaded.storageInput, this.entityId);
2449
+ const reads = new AgentLiveReadRuntime(historyScope, budget, this.checkSession);
2450
+ const release = this.bind(loaded.entity, facade, reads, actor);
2451
+ try {
2452
+ const output = await AgentLiveHandler.awaitHandler(this.callHandler(loaded.entity, handler), this.signal, deadline);
2453
+ reads.finish();
2454
+ facade.finish();
2455
+ return output;
2574
2456
  }
2575
- },
2576
- /**
2577
- * Reads an optional saved model deadline and detects incomplete persisted bounds.
2578
- *
2579
- * @param session Fenced Agent execution session.
2580
- * @param context Selection or preference authorization context.
2581
- * @returns Saved absolute deadline, when configured.
2582
- */
2583
- savedModelDeadline(session, context) {
2584
- const record = session.record();
2585
- const deadline = record.started?.deadline;
2586
- if (deadline === undefined && record.started?.bounds?.deadlineMillis !== undefined)
2587
- throw new Error(`Agent model ${context} deadline was not saved.`);
2588
- return deadline === undefined
2589
- ? undefined
2590
- : Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000);
2591
- },
2592
- /**
2593
- * Applies the same provider fence to preference events and Entity completion.
2594
- *
2595
- * @param repository Registered Entity repository for this operation.
2596
- * @param runtime Bounded Context storage and publication services.
2597
- * @param tenantId Tenant selected for this accepted signal.
2598
- * @param session Fenced Agent execution session.
2599
- * @param loaded Loaded Entity and its repository storage state.
2600
- * @param entityId Typed recipient Entity identifier.
2601
- * @param makeNext Callback constructing the next durable execution record.
2602
- * @param historyEntries Agent history rows included in the fenced mutation.
2603
- * @returns Saved execution record after conditional completion.
2604
- */
2605
- commitCompletion(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries) {
2606
- return RepositoryEntities.repositoryChanged(loaded.entity)
2607
- ? this.commitChanged(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries)
2608
- : session.complete(makeNext, historyEntries);
2609
- },
2610
- /**
2611
- * Wraps original preference Events as indexed Agent history entries.
2612
- *
2613
- * @param events Original Event envelopes retained for diagnostics.
2614
- * @returns Typed Agent history rows for saved preference changes.
2615
- */
2616
- preferenceHistory(events) {
2617
- return events.map((event) => {
2618
- const occurredAt = event.context?.timestamp;
2619
- if (occurredAt === undefined)
2620
- throw new Error("Agent preference Event requires Time.");
2621
- return create(AgentHistoryEntrySchema, {
2622
- occurredAt,
2623
- item: { case: "systemEvent", value: event },
2624
- });
2625
- });
2626
- },
2627
- /**
2628
- * Captures only effective preference changes committed by this signal.
2629
- *
2630
- * @param repository Registered Entity repository for this operation.
2631
- * @param runtime Bounded Context storage and publication services.
2632
- * @param tenantId Tenant selected for this accepted signal.
2633
- * @param session Fenced Agent execution session.
2634
- * @returns Original model-selection System Events.
2635
- */
2636
- preferenceEvents(repository, runtime, tenantId, session) {
2637
- const accepted = session.record().accepted;
2638
- if (accepted?.recipientId === undefined)
2639
- throw new Error("Agent preference requires accepted work.");
2640
- const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2641
- return [AiModelKind.GENERATION, AiModelKind.DECISION].flatMap((kind) => {
2642
- const previous = this.preferenceModel(session.initialPreferences(), kind);
2643
- const selected = this.preferenceModel(session.preferences(), kind);
2644
- if (previous === undefined && selected === undefined)
2645
- return [];
2646
- if (previous !== undefined &&
2647
- selected !== undefined &&
2648
- equals(ModelRefSchema, previous, selected))
2649
- return [];
2650
- return [this.preferenceEvent(accepted, scope, kind, previous, selected, runtime)];
2651
- });
2652
- },
2653
- /**
2654
- * Builds one effective selection change with its original causal context.
2655
- *
2656
- * @param accepted Durable original signal and saved handler bindings.
2657
- * @param scope Accepted actor, source, Bounded Context, and tenant.
2658
- * @param kind Generation or decision model kind.
2659
- * @param previous Previously selected model deployment.
2660
- * @param selected Newly selected model deployment.
2661
- * @param runtime Bounded Context storage and publication services.
2662
- * @returns Original model-selection System Event.
2663
- */
2664
- preferenceEvent(accepted, scope, kind, previous, selected, runtime) {
2665
- const recipientId = accepted.recipientId;
2666
- if (recipientId === undefined)
2667
- throw new Error("Agent preference requires a recipient.");
2668
- const payload = create(SelectionChangedSchema, {
2669
- agent: scope.agent,
2670
- sourceSignal: scope.source,
2671
- kind,
2672
- ...(previous === undefined ? {} : { previousModel: previous }),
2673
- ...(selected === undefined ? {} : { selectedModel: selected }),
2674
- changedAt: Time.currentTime(),
2675
- });
2676
- runtime.registerSystemEventSchema(SelectionChangedSchema);
2677
- return AgentInteractionEvents.envelope(accepted, runtime.signalMetadata, recipientId, SelectionChangedSchema, payload);
2678
- },
2679
- /**
2680
- * Reads an explicit model selection, including inherited defaults as absence.
2681
- *
2682
- * @param preferences Current Agent instance model preferences.
2683
- * @param kind Generation or decision model kind.
2684
- * @returns Selected deployment reference, or inherited default.
2685
- */
2686
- preferenceModel(preferences, kind) {
2687
- const selected = preferences.find((entry) => entry.kind === kind);
2688
- return selected?.selection.case === "model" ? selected.selection.value : undefined;
2689
- },
2690
- /**
2691
- * Builds the immutable completion image and emitted domain history.
2692
- *
2693
- * @param record Durable Agent execution record.
2694
- * @param version Entity version associated with the signal.
2695
- * @param outputs Prepared outgoing domain signals.
2696
- * @param preferences Current Agent instance model preferences.
2697
- * @returns Record completed with original outputs and resulting version.
2698
- */
2699
- completionFacts(record, version, outputs, preferences) {
2700
- const initialVersion = record.started?.initialVersion;
2701
- if (initialVersion === undefined)
2702
- throw new Error("Agent completion requires its started Version.");
2703
- record.status =
2704
- outputs.length === 0
2705
- ? AgentInvocationStatus.AGENT_INVOCATION_COMPLETED
2706
- : AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_DELIVERY;
2707
- record.completion = create(AgentExecutionCompletionSchema, {
2708
- initialVersion,
2709
- resultingVersion: version,
2710
- preferences: [...preferences],
2711
- outgoing: [...outputs],
2712
- });
2713
- return record;
2714
- },
2457
+ finally {
2458
+ facade.close();
2459
+ release();
2460
+ }
2461
+ }
2715
2462
  /**
2716
- * Stores only committed domain Events in the Agent's emitted history.
2463
+ * Creates live AI and actor capabilities to one generated handler.
2717
2464
  *
2718
- * @param outputs Prepared outgoing domain signals.
2719
- * @returns Committed domain Event rows for Agent history.
2465
+ * @param audit Retained history audit for this invocation.
2466
+ * @param budget Invocation-wide resource budget.
2467
+ * @param preferences Current or staged model preferences.
2468
+ * @param selected Models or selection retained for this invocation.
2469
+ * @returns The handler facade with live AI and actor capabilities.
2720
2470
  */
2721
- emittedHistory(outputs) {
2722
- return outputs.flatMap((output) => {
2723
- if (output.signal.case !== "event" || output.signal.value.context?.rejection !== undefined)
2724
- return [];
2725
- const event = output.signal.value;
2726
- const occurredAt = event.context?.timestamp;
2727
- if (occurredAt === undefined)
2728
- throw new Error("Agent emitted Event requires its original timestamp.");
2729
- return [
2730
- create(AgentHistoryEntrySchema, {
2731
- occurredAt,
2732
- item: { case: "domainEvent", value: event },
2733
- }),
2734
- ];
2735
- });
2736
- },
2471
+ handlerFacade(selected, preferences, audit, budget) {
2472
+ const ai = this.runtime.ai;
2473
+ const policy = repositoryAccess.agentConfiguration(this.repository).ai;
2474
+ if (ai === undefined || policy === undefined)
2475
+ throw new Error("Agent AI policy is missing.");
2476
+ const scope = AgentModelSelection.scopeLive(this.invocation, this.repository.stateSchema, this.runtime.signalMetadata, this.tenantId);
2477
+ const facade = new AgentLiveAiRuntime(ai, policy, scope, selected, budget, this.signal, this.deadline(ai), (kind, model) => {
2478
+ this.stage(preferences, kind, model);
2479
+ }, audit, this.checkSession);
2480
+ return { facade, actor: scope.actor, deadline: this.deadline(ai) };
2481
+ }
2737
2482
  /**
2738
- * Commits changed Entity state with the already prepared execution image.
2483
+ * Prepares a per-kind selection without changing the observed preference image.
2739
2484
  *
2740
- * @param repository Registered Entity repository for this operation.
2741
- * @param runtime Bounded Context storage and publication services.
2742
- * @param tenantId Tenant selected for this accepted signal.
2743
- * @param session Fenced Agent execution session.
2744
- * @param loaded Loaded Entity and its repository storage state.
2745
- * @param entityId Typed recipient Entity identifier.
2746
- * @param makeNext Callback constructing the next durable execution record.
2747
- * @param historyEntries Agent history rows included in the fenced mutation.
2748
- * @returns Saved execution record after the changed Entity commit.
2485
+ * @param kind Declared model or operation kind.
2486
+ * @param model Selected AI model for this invocation.
2487
+ * @param preferences Current or staged model preferences.
2749
2488
  */
2750
- async commitChanged(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries) {
2751
- const version = RepositoryEntities.repositoryVersion(loaded.entity);
2752
- const state = RepositoryEntities.repositoryState(loaded.entity);
2753
- const lifecycle = RepositoryEntities.repositoryLifecycle(loaded.entity);
2754
- const deferred = await standAccess.deferUpdate(runtime.stand, repository.stateSchema, state, RepositoryStand.standUpdateOptions(tenantId, version, lifecycle), repository.metadata);
2755
- const prepared = RepositoryStand.preparedRecord(repository, deferred, entityId);
2756
- return this.commitDeferred(repository, session, loaded, entityId, makeNext, historyEntries, deferred, prepared);
2757
- },
2489
+ stage(preferences, kind, model) {
2490
+ const index = preferences.findIndex((entry) => entry.kind === kind);
2491
+ if (index >= 0)
2492
+ preferences.splice(index, 1);
2493
+ if (model !== undefined)
2494
+ preferences.push(create(ModelPreferenceSchema, { kind, selection: { case: "model", value: model } }));
2495
+ }
2758
2496
  /**
2759
- * Applies the deferred Stand image only after fenced provider completion.
2497
+ * Binds current protected history, query, and AI capabilities to one handler.
2760
2498
  *
2761
- * @param repository Registered Entity repository for this operation.
2762
- * @param session Fenced Agent execution session.
2763
- * @param loaded Loaded Entity and its repository storage state.
2764
- * @param entityId Typed recipient Entity identifier.
2765
- * @param makeNext Callback constructing the next durable execution record.
2766
- * @param historyEntries Agent history rows included in the fenced mutation.
2767
- * @param deferred Prepared Stand update awaiting conditional commit.
2768
- * @param prepared Prepared conditional Entity commit.
2769
- * @returns Saved execution record after the deferred Entity commit.
2499
+ * @param actor Captured signal actor context.
2500
+ * @param entity Current Entity instance.
2501
+ * @param facade Handler-facing operation facade.
2502
+ * @param reads Retained Agent history reader.
2503
+ * @returns The handler bound to current protected history and AI capabilities.
2770
2504
  */
2771
- async commitDeferred(repository, session, loaded, entityId, makeNext, historyEntries, deferred, prepared) {
2505
+ bind(entity, facade, reads, actor) {
2506
+ const history = AgentHistoryReads.withReadInterceptor(entity, (scope, request, live, maxBytes) => reads.history(scope, request, live, maxBytes));
2507
+ let query;
2772
2508
  try {
2773
- const completed = await session.complete(makeNext, historyEntries, {
2774
- context: loaded.storageInput.context,
2775
- entity: loaded.storageInput,
2776
- entityId,
2777
- next: prepared,
2778
- ...(RepositoryStorage.historyConfiguration(repository).stateHistory
2779
- ? { states: [prepared] }
2780
- : {}),
2781
- });
2782
- deferred.notify();
2783
- return completed;
2509
+ query = ProcessManagerQueries.bind(entity, this.runtime, actor, this.tenantId, (schema, request, live) => reads.query(schema, request, live));
2510
+ const ai = AgentAiBindings.bind(entity, facade);
2511
+ return () => {
2512
+ ai();
2513
+ query?.();
2514
+ history();
2515
+ };
2784
2516
  }
2785
2517
  catch (error) {
2786
- deferred.cancel();
2518
+ query?.();
2519
+ history();
2787
2520
  throw error;
2788
2521
  }
2789
- },
2790
- /**
2791
- * Binds declared generated Event and Command results in handler order.
2792
- *
2793
- * @param repository Agent declaration.
2794
- * @param runtime Signal metadata factory.
2795
- * @param accepted Original accepted envelope.
2796
- * @param entityId Typed recipient.
2797
- * @param version Entity Version before this transition.
2798
- * @param produced Handler results in declaration order.
2799
- * @returns Original outgoing envelopes ready for one conditional completion.
2800
- */
2801
- bindOutputs(repository, runtime, accepted, entityId, version, produced) {
2802
- const source = accepted.signal;
2803
- if (source.case !== "command" && source.case !== "event")
2804
- throw new Error("Agent output requires its accepted source signal.");
2805
- const routing = repositoryRoutings.get(repository);
2806
- if (routing === undefined)
2807
- throw new Error("Agent output requires registered schemas.");
2808
- return produced.map((signal) => this.bindOutput(repository, runtime, routing, source, entityId, version, signal));
2809
- },
2810
- /**
2811
- * Binds one declared result using its generated Event or Command role.
2812
- *
2813
- * @param repository Registered Entity repository for this operation.
2814
- * @param runtime Bounded Context storage and publication services.
2815
- * @param routing Registered generated handler routing metadata.
2816
- * @param source Original accepted source signal.
2817
- * @param entityId Typed recipient Entity identifier.
2818
- * @param version Entity version associated with the signal.
2819
- * @param signal Original domain signal or execution cancellation signal.
2820
- * @returns Typed outgoing signal with its original envelope.
2821
- */
2822
- bindOutput(repository, runtime, routing, source, entityId, version, signal) {
2823
- const typeName = EntityInvocation.messageTypeName(signal);
2824
- const eventSchema = routing.producedEventSchemas.find((entry) => entry.typeName === typeName);
2825
- if (eventSchema !== undefined)
2826
- return this.bindEventOutput(repository, runtime, source, entityId, version, signal, eventSchema);
2827
- const schema = routing.producedCommandSchemas.find((entry) => entry.typeName === typeName);
2828
- if (schema === undefined)
2829
- throw new Error(`Agent cannot emit undeclared signal "${typeName}".`);
2830
- return this.bindCommandOutput(runtime, source, signal, schema);
2831
- },
2832
- /**
2833
- * Creates the original domain Event envelope and producer context.
2834
- *
2835
- * @param repository Registered Entity repository for this operation.
2836
- * @param runtime Bounded Context storage and publication services.
2837
- * @param source Original accepted source signal.
2838
- * @param entityId Typed recipient Entity identifier.
2839
- * @param version Entity version associated with the signal.
2840
- * @param signal Original domain signal or execution cancellation signal.
2841
- * @param schema Generated message descriptor for the signal.
2842
- * @returns Typed original Event envelope for durable delivery.
2843
- */
2844
- bindEventOutput(repository, runtime, source, entityId, version, signal, schema) {
2845
- if (source.case !== "command" && source.case !== "event")
2846
- throw new Error("Missing source.");
2847
- const metadata = source.case === "command"
2848
- ? runtime.signalMetadata.eventFromCommand(source.value, { version: version.number })
2849
- : runtime.signalMetadata.eventFromEvent(source.value, { version: version.number });
2850
- const event = create(EventSchema, {
2851
- id: metadata.id,
2852
- message: AnyMessages.pack(schema, signal),
2853
- context: RepositorySignals.eventContextWithProducer(metadata.context, repository, entityId, version),
2854
- });
2855
- return create(AgentOutgoingSignalSchema, { signal: { case: "event", value: event } });
2856
- },
2857
- /**
2858
- * Creates the original Command envelope from its accepted origin.
2859
- *
2860
- * @param runtime Bounded Context storage and publication services.
2861
- * @param source Original accepted source signal.
2862
- * @param signal Original domain signal or execution cancellation signal.
2863
- * @param schema Generated message descriptor for the signal.
2864
- * @returns Typed original Command envelope for durable delivery.
2865
- */
2866
- bindCommandOutput(runtime, source, signal, schema) {
2867
- if (source.case !== "command" && source.case !== "event")
2868
- throw new Error("Missing source.");
2869
- const metadata = source.case === "command"
2870
- ? runtime.signalMetadata.commandFromCommand(source.value)
2871
- : runtime.signalMetadata.commandFromEvent(source.value);
2872
- const command = create(CommandSchema, {
2873
- id: metadata.id,
2874
- message: AnyMessages.pack(schema, signal),
2875
- context: metadata.context,
2876
- });
2877
- return create(AgentOutgoingSignalSchema, { signal: { case: "command", value: command } });
2878
- },
2879
- /**
2880
- * Publishes saved envelopes through normal bus acceptance before marking original IDs.
2881
- *
2882
- * @param runtime Bound normal bus acceptance callbacks.
2883
- * @param session Fenced record containing pending saved envelopes.
2884
- * @returns When the fenced repository operation completes.
2885
- */
2886
- async deliver(runtime, session) {
2887
- const completed = session.record();
2888
- const key = completed.accepted?.key;
2889
- if (key === undefined)
2890
- throw new Error("Agent output delivery requires its accepted key.");
2891
- for (const [index, output] of (completed.completion?.outgoing ?? []).entries()) {
2892
- if (session.signal.aborted)
2893
- return;
2894
- if (output.delivered)
2895
- continue;
2896
- const signal = output.signal;
2897
- if (signal.case !== "event" && signal.case !== "command")
2898
- throw new Error("Agent outgoing signal has no original envelope.");
2899
- const key = this.outputKey(output);
2900
- const plan = output.plan ??
2901
- (await (signal.case === "event"
2902
- ? runtime.prepareSavedEvent(signal.value)
2903
- : runtime.prepareSavedCommand(signal.value)));
2904
- if (output.plan === undefined) {
2905
- await session.update((current) => {
2906
- const pending = current.completion?.outgoing[index];
2907
- if (pending?.plan !== undefined || pending?.signal.case !== signal.case)
2908
- throw new Error("Saved Agent output changed before plan installation.");
2909
- pending.plan = plan;
2910
- return current;
2911
- });
2912
- }
2913
- if (signal.case === "event")
2914
- await runtime.publishSavedEvent(signal.value, plan);
2915
- else
2916
- await runtime.publishSavedCommand(signal.value, plan);
2917
- await session.markDelivered([key]);
2918
- runtime.recordAcceptedSaved(signal.value);
2919
- }
2920
- },
2522
+ }
2921
2523
  /**
2922
- * Returns the typed original ID of one completed outgoing signal.
2524
+ * Invokes and validates one declared generated Command or Event handler.
2923
2525
  *
2924
- * @param output Prepared outgoing domain signal.
2925
- * @returns Original durable key for the outgoing signal.
2526
+ * @param entity Current Entity instance.
2527
+ * @param handler Selected generated handler.
2528
+ * @returns The validated generated handler result.
2926
2529
  */
2927
- outputKey(output) {
2928
- if (output.signal.case === "event" && output.signal.value.id !== undefined)
2929
- return create(AgentSignalKeySchema, {
2930
- id: { case: "event", value: output.signal.value.id },
2931
- });
2932
- if (output.signal.case === "command" && output.signal.value.id !== undefined)
2933
- return create(AgentSignalKeySchema, {
2934
- id: { case: "command", value: output.signal.value.id },
2935
- });
2936
- throw new Error("Agent outgoing signal requires its original typed ID.");
2937
- },
2530
+ async callHandler(entity, handler) {
2531
+ const source = this.invocation.source;
2532
+ const kind = source.case;
2533
+ const message = EntityInvocation.unpackRequired(EntityInvocation.requireSignalMessage(source.value.message, kind), handler.handler.schema, kind);
2534
+ const context = kind === "command"
2535
+ ? EntityInvocation.commandHandlerContext(source.value)
2536
+ : EntityInvocation.eventHandlerContext(source.value);
2537
+ const produced = await EntityInvocation.invokeEntityMethod(entity, handler.handler.methodName, message, handler.handler.parameterCount, context);
2538
+ const outputs = produced === undefined
2539
+ ? []
2540
+ : Array.isArray(produced)
2541
+ ? Array.from(produced)
2542
+ : [produced];
2543
+ RepositoryHandlers.requireDeclaredOutputs(handler.handler, outputs);
2544
+ return outputs;
2545
+ }
2938
2546
  /**
2939
- * Validates every stored binding against the current generated selectors.
2547
+ * Commits the observed state/preferences image and its final audit atomically.
2940
2548
  *
2941
- * @param repository Agent registration.
2942
- * @param accepted Immutable source and ordered bindings.
2943
- * @returns Current matching handler metadata.
2549
+ * @param expected Expected persisted value or version.
2550
+ * @param loaded Current Entity and retained storage for this turn.
2551
+ * @param preferences Current or staged model preferences.
2552
+ * @param produced Declared outgoing signals.
2553
+ * @returns Completion after the conditional state, preferences, and final audit commit.
2944
2554
  */
2945
- handlers(repository, accepted) {
2946
- const routing = repositoryRoutings.get(repository);
2555
+ async complete(loaded, expected, preferences, produced) {
2556
+ const version = RepositoryEntities.repositoryVersion(loaded.entity);
2557
+ const routing = repositoryRoutings.get(this.repository);
2947
2558
  if (routing === undefined)
2948
- throw new Error("Agent execution requires registered handlers.");
2949
- const current = this.selectedHandlers(routing, accepted);
2950
- if (accepted.handlers.length !== current.length || current.length === 0)
2951
- throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent handler binding changed before replay.");
2952
- for (const [ordinal, handler] of current.entries()) {
2953
- const binding = accepted.handlers[ordinal];
2954
- if (binding === undefined || !AgentAdmission.matchesHandler(binding, handler, ordinal))
2955
- throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent handler binding changed before replay.");
2956
- }
2957
- return current;
2958
- },
2959
- /**
2960
- * Finds current Command or Event handlers from the immutable source envelope.
2961
- *
2962
- * @param routing Registered generated handler routing metadata.
2963
- * @param accepted Durable original signal and saved handler bindings.
2964
- * @returns Generated handlers selected for the accepted signal.
2965
- */
2966
- selectedHandlers(routing, accepted) {
2967
- if (accepted.signal.case === "command") {
2968
- const binding = accepted.handlers[0];
2969
- const current = routing.commandReadiness?.findCommandAssignee(binding?.signalType ?? "");
2970
- return current === undefined ? [] : [current.registeredHandler];
2971
- }
2972
- if (accepted.signal.case !== "event")
2973
- throw new Error("Agent source envelope is missing.");
2974
- const event = accepted.signal.value;
2975
- const packed = EntityInvocation.requireSignalMessage(event.message, "event");
2976
- const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
2977
- const value = EntityInvocation.unpackRequired(packed, schema, "event");
2978
- const external = event.context?.external === true;
2979
- return [
2980
- ...routing.eventReactors(schema.typeName, value, external),
2981
- ...routing.commandReactions(schema.typeName, value, external),
2559
+ throw new Error("Live Agent output requires registered routing.");
2560
+ const outputs = produced.case === "rejected"
2561
+ ? [produced.output]
2562
+ : produced.values.map((item) => AgentLiveOutputs.bindOutput(this.repository, this.runtime, routing, this.invocation.source, this.entityId, version, item));
2563
+ await this.authorizePreferences(expected ?? [], preferences);
2564
+ await this.checkSession();
2565
+ const preferenceEvents = this.preferenceEvents(expected ?? [], preferences);
2566
+ const history = [
2567
+ ...AgentLiveOutputs.emittedHistory(outputs),
2568
+ ...preferenceEvents.map((event) => this.systemEntry(event)),
2982
2569
  ];
2983
- },
2984
- /**
2985
- * Records start facts and the original dispatch System Event before handler execution.
2986
- *
2987
- * @param repository Agent registration.
2988
- * @param runtime Bound services.
2989
- * @param session Fenced provider claim and current record.
2990
- * @param loaded Restored Entity and initial Version.
2991
- * @param entityId Original typed recipient.
2992
- * @param tenantId Accepted delivery tenant when applicable.
2993
- * @returns Updated record image for exact subsequent CAS.
2994
- */
2995
- async start(repository, runtime, session, loaded, entityId, tenantId) {
2996
- const accepted = session.record().accepted;
2997
- if (accepted === undefined)
2998
- throw new Error("Agent start requires accepted work.");
2999
- const started = this.startFacts(repository, runtime, accepted, loaded);
3000
- const audit = this.startAudit(runtime, repository, accepted, entityId);
3001
- await session.update((current) => {
3002
- current.started = started;
3003
- return current;
3004
- }, [this.startHistory(audit)]);
3005
- if (accepted.signal.case === "command")
3006
- HandlerDispatchPublisher.publishCommand(runtime, audit);
3007
- else
3008
- HandlerDispatchPublisher.publishReactor(runtime, audit);
3009
- return this.selectMissingModels(repository, runtime, session, accepted, tenantId);
3010
- },
3011
- /**
3012
- * Stores the original dispatch audit envelope in mandatory Agent history.
3013
- *
3014
- * @param audit Original Agent System event envelope.
3015
- * @returns Typed Agent history row for the original dispatch Event.
3016
- */
3017
- startHistory(audit) {
3018
- const occurredAt = audit.context?.timestamp;
3019
- if (occurredAt === undefined)
3020
- throw new Error("Agent dispatch audit requires its timestamp.");
3021
- return create(AgentHistoryEntrySchema, {
3022
- occurredAt,
3023
- item: { case: "systemEvent", value: audit },
3024
- });
3025
- },
3026
- /**
3027
- * Verifies admission revisions and fixes whole-invocation limits before execution.
3028
- *
3029
- * @param repository Registered Entity repository for this operation.
3030
- * @param runtime Bounded Context storage and publication services.
3031
- * @param accepted Durable original signal and saved handler bindings.
3032
- * @param loaded Loaded Entity and its repository storage state.
3033
- * @returns Durable deadline, initial version, and whole-invocation bounds.
3034
- */
3035
- startFacts(repository, runtime, accepted, loaded) {
3036
- const configuration = repositoryAccess.agentConfiguration(repository);
3037
- const ai = runtime.ai;
3038
- if (accepted.key === undefined || ai === undefined || configuration.ai === undefined)
3039
- throw new Error("Agent execution start requires accepted work and AI policy.");
3040
- const limits = registryOptions(ai).invocationLimits ?? {};
3041
- return create(AgentExecutionStartSchema, {
3042
- ...(limits.deadlineMs === undefined
3043
- ? {}
3044
- : { deadline: addMillis(Time.currentTime(), limits.deadlineMs) }),
3045
- initialVersion: RepositoryEntities.priorVersion(loaded.current),
3046
- bounds: this.invocationBounds(limits),
3047
- });
3048
- },
3049
- /**
3050
- * Persists each model kind without a saved connection identity.
3051
- *
3052
- * @param repository Agent registration and permitted capabilities.
3053
- * @param runtime Bound AI registry.
3054
- * @param session Current fenced execution record.
3055
- * @param accepted Original accepted signal and actor.
3056
- * @param tenantId Accepted tenant when applicable.
3057
- * @returns Record after each missing identity has been saved.
3058
- */
3059
- async selectMissingModels(repository, runtime, session, accepted, tenantId) {
3060
- const ai = runtime.ai;
3061
- const configuration = repositoryAccess.agentConfiguration(repository).ai;
3062
- if (ai === undefined || configuration === undefined)
3063
- throw new Error("Agent model selection requires saved start facts and AI policy.");
3064
- const deadlineMs = this.savedModelDeadline(session, "selection");
3065
- const source = accepted.signal.value?.message;
3066
- for (const kind of ["generation", "decision"]) {
3067
- if (!configuration.models.some((model) => model.definition.kind === kind))
3068
- continue;
3069
- const modelKind = AgentModelSelection.kind(kind);
3070
- if (session.record().started?.models.some((model) => model.kind === modelKind))
3071
- continue;
3072
- const selected = await AgentModelSelection.selectKind(ai, configuration, AgentModelSelection.scope(accepted, repository.stateSchema, tenantId), session.initialPreferences(), session.signal, deadlineMs, kind, source);
3073
- await this.saveSelectedModel(session, selected);
3074
- }
3075
- return session.record();
3076
- },
3077
- /**
3078
- * Writes one selected model under the current Agent claim.
3079
- *
3080
- * @param session Fenced Agent execution session.
3081
- * @param selected Authorized deployment and connection identity.
3082
- * @returns When the selection is durable.
3083
- */
3084
- async saveSelectedModel(session, selected) {
3085
- await session.update((record) => {
3086
- if (record.started === undefined)
3087
- throw new Error("Agent start facts disappeared.");
3088
- record.started.models.push(selected);
3089
- return record;
3090
- });
3091
- },
3092
- /**
3093
- * Rejects accepted work when its exact handler and AI policy changed.
3094
- *
3095
- * @param repository Registered Entity repository for this operation.
3096
- * @param accepted Durable original signal and saved handler bindings.
3097
- * @param handlers Generated handlers in their saved order.
3098
- * @param ai Configured AI registry for this Bounded Context.
3099
- * @param configuration Repository and AI policy used for revision checks.
3100
- */
3101
- assertRevisions(repository, accepted, handlers, ai, configuration) {
3102
- if (configuration.ai === undefined)
3103
- throw new Error("Agent AI policy is required.");
3104
- const revisions = AgentRevisions.atAdmission(repository.stateSchema, handlers, ai, configuration.ai);
3105
- if (accepted.codeRevision?.value !== configuration.codeRevision ||
3106
- accepted.schemaRevision?.value !== revisions.schema ||
3107
- accepted.policyRevision?.value !== revisions.policy)
3108
- throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent code, schema, or policy changed before execution.");
3109
- },
3110
- /**
3111
- * Copies the whole-invocation budget into its durable integer fields.
3112
- *
3113
- * @param limits Configured whole-invocation limits.
3114
- * @returns Whole-transition limits persisted at invocation start.
3115
- */
3116
- invocationBounds(limits) {
3117
- return create(AgentInvocationBoundsSchema, {
3118
- ...(limits.operations === undefined ? {} : { operations: BigInt(limits.operations) }),
3119
- ...(limits.modelRequests === undefined
3120
- ? {}
3121
- : { modelRequests: BigInt(limits.modelRequests) }),
3122
- ...(limits.toolCalls === undefined ? {} : { toolCalls: BigInt(limits.toolCalls) }),
3123
- ...(limits.recordedReads === undefined
3124
- ? {}
3125
- : { recordedReads: BigInt(limits.recordedReads) }),
3126
- ...(limits.deadlineMs === undefined ? {} : { deadlineMillis: BigInt(limits.deadlineMs) }),
3127
- ...(limits.totalInputBytes === undefined
3128
- ? {}
3129
- : { totalInputBytes: BigInt(limits.totalInputBytes) }),
3130
- ...(limits.totalOutputBytes === undefined
3131
- ? {}
3132
- : { totalOutputBytes: BigInt(limits.totalOutputBytes) }),
3133
- ...(limits.maxRecoveryBytes === undefined
3134
- ? {}
3135
- : { maxRecoveryBytes: BigInt(limits.maxRecoveryBytes) }),
3136
- });
3137
- },
3138
- /**
3139
- * Creates a genuine source-specific dispatch System Event before the handler.
3140
- *
3141
- * @param runtime Bounded Context storage and publication services.
3142
- * @param repository Registered Entity repository for this operation.
3143
- * @param accepted Durable original signal and saved handler bindings.
3144
- * @param entityId Typed recipient Entity identifier.
3145
- * @returns Original dispatch System Event envelope.
3146
- */
3147
- startAudit(runtime, repository, accepted, entityId) {
3148
- const source = accepted.signal;
3149
- if (source.case === "command")
3150
- return HandlerDispatchPublisher.commandEvent(runtime, repository, source.value, entityId);
3151
- if (source.case === "event")
3152
- return HandlerDispatchPublisher.reactorEvent(runtime, repository, source.value, entityId);
3153
- throw new Error("Agent start requires its accepted envelope.");
3154
- },
2570
+ await this.commit(loaded, expected, preferences, history);
2571
+ await this.checkSession();
2572
+ for (const event of preferenceEvents)
2573
+ await this.runtime.publishAgentSystemEvent(event);
2574
+ this.publish(outputs);
2575
+ }
3155
2576
  /**
3156
- * Invokes saved Command or Event bindings inside one Entity transaction.
2577
+ * Applies final conditional state and preference mutation under the observed Version.
3157
2578
  *
3158
- * @param entity Restored Agent instance.
3159
- * @param accepted Original accepted signal.
3160
- * @param handlers Validated current handler bindings.
3161
- * @param repository Registered Entity repository for this operation.
3162
- * @param runtime Bounded Context storage and publication services.
3163
- * @param tenantId Tenant selected for this accepted signal.
3164
- * @param session Fenced Agent execution session.
3165
- * @returns Outputs returned by the selected generated handlers.
2579
+ * @param expected Expected persisted value or version.
2580
+ * @param history Retained history entries or history port.
2581
+ * @param loaded Current Entity and retained storage for this turn.
2582
+ * @param preferences Current or staged model preferences.
2583
+ * @returns The atomic conditional commit result for the observed version.
3166
2584
  */
3167
- async invoke(repository, runtime, tenantId, session, entity, accepted, handlers) {
3168
- transactionalEntityAccess.start(entity);
2585
+ async commit(loaded, expected, preferences, history) {
2586
+ const deferred = await this.deferredUpdate(loaded);
3169
2587
  try {
3170
- const signals = [];
3171
- for (const [ordinal, handler] of handlers.entries()) {
3172
- signals.push(...(await this.invokeBoundHandler(repository, runtime, tenantId, session, entity, accepted, handler, ordinal)));
3173
- }
3174
- await this.commitInvokedEntity(entity);
3175
- return signals;
2588
+ const prepared = deferred === undefined
2589
+ ? undefined
2590
+ : RepositoryStand.preparedRecord(this.repository, deferred, this.entityId);
2591
+ await this.checkSession();
2592
+ await loaded.commits.commitAgent({
2593
+ entityId: this.entityId,
2594
+ expectedVersion: loaded.current?.version?.number ?? 0,
2595
+ expectedPreferences: expected,
2596
+ preferences,
2597
+ historyEntries: history,
2598
+ ...(prepared === undefined ? {} : { entityCommit: this.entityCommit(loaded, prepared) }),
2599
+ });
2600
+ deferred?.notify();
3176
2601
  }
3177
2602
  catch (error) {
3178
- transactionalEntityAccess.rollback(entity);
2603
+ deferred?.cancel();
3179
2604
  throw error;
3180
2605
  }
3181
- },
2606
+ }
3182
2607
  /**
3183
- * Validates the transaction result before Agent output completion.
2608
+ * Creates current state and its enabled history only for a changed Entity.
3184
2609
  *
3185
- * @param entity Loaded recipient Entity instance.
3186
- * @returns When the fenced repository operation completes.
2610
+ * @param loaded Current Entity and retained storage for this turn.
2611
+ * @param prepared Prepared Entity mutation and outputs.
2612
+ * @returns The changed Entity image and enabled history for the commit.
3187
2613
  */
3188
- async commitInvokedEntity(entity) {
3189
- const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
3190
- if (commit.status === "rejected")
3191
- throw new TransitionValidationError(commit.validation.error);
3192
- },
2614
+ entityCommit(loaded, prepared) {
2615
+ return {
2616
+ context: loaded.storageInput.context,
2617
+ entity: loaded.storageInput,
2618
+ entityId: this.entityId,
2619
+ next: prepared,
2620
+ ...(loaded.storageInput.stateHistory ? { states: [prepared] } : {}),
2621
+ };
2622
+ }
3193
2623
  /**
3194
- * Binds named AI and history reads around one generated handler call.
2624
+ * Prepares the changed Entity state for publication only after atomic commit.
3195
2625
  *
3196
- * @param repository Registered Entity repository for this operation.
3197
- * @param runtime Bounded Context storage and publication services.
3198
- * @param tenantId Tenant selected for this accepted signal.
3199
- * @param session Fenced Agent execution session.
3200
- * @param entity Loaded recipient Entity instance.
3201
- * @param accepted Durable original signal and saved handler bindings.
3202
- * @param handler Generated handler selected for this signal.
3203
- * @param ordinal Saved position of the selected handler.
3204
- * @returns Outputs returned by one generated handler.
3205
- */
3206
- async invokeBoundHandler(repository, runtime, tenantId, session, entity, accepted, handler, ordinal) {
3207
- const facade = this.handlerFacade(repository, runtime, tenantId, session, accepted, ordinal);
3208
- const reads = new AgentReadRuntime(session, ordinal);
3209
- const release = this.bindHandlerCapabilities(entity, runtime, tenantId, accepted, reads, facade);
3210
- try {
3211
- const deadlineEpochMs = this.handlerDeadline(session);
3212
- const signals = await this.awaitHandler(this.invokeHandler(entity, accepted, handler), session.signal, deadlineEpochMs);
3213
- reads.finish();
3214
- facade.finish();
3215
- return signals;
3216
- }
3217
- finally {
3218
- facade.close();
3219
- release();
3220
- }
3221
- },
2626
+ * @param loaded Current Entity and retained storage for this turn.
2627
+ * @returns The Stand update deferred until the atomic commit succeeds.
2628
+ */
2629
+ deferredUpdate(loaded) {
2630
+ if (!RepositoryEntities.repositoryChanged(loaded.entity))
2631
+ return Promise.resolve(undefined);
2632
+ 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);
2633
+ }
3222
2634
  /**
3223
- * Binds Agent AI, indexed history and routed projection queries for one handler.
2635
+ * Checks authorization of effective changed selections before final persistence.
3224
2636
  *
3225
- * @param entity Loaded recipient Entity instance.
3226
- * @param runtime Bounded Context storage and publication services.
3227
- * @param tenantId Tenant selected for this accepted signal.
3228
- * @param accepted Durable original signal and saved handler bindings.
3229
- * @param reads Handler-scoped durable read facade.
3230
- * @param facade Handler-scoped AI facade.
3231
- * @returns Release callback for handler-scoped AI and read facades.
3232
- */
3233
- bindHandlerCapabilities(entity, runtime, tenantId, accepted, reads, facade) {
3234
- const releaseHistory = AgentHistoryReads.withJournal(entity, (scope, view, request, live, maxBytes) => reads.history(scope, view, request, live, maxBytes));
3235
- let releaseQuery;
3236
- try {
3237
- releaseQuery = ProcessManagerQueries.bind(entity, runtime, accepted.actor, tenantId, (schema, query, live) => reads.query(schema, query, live));
3238
- const releaseAi = AgentAiBindings.bind(entity, facade);
3239
- return () => {
3240
- releaseQuery?.();
3241
- releaseHistory();
3242
- releaseAi();
3243
- };
3244
- }
3245
- catch (error) {
3246
- releaseQuery?.();
3247
- releaseHistory();
3248
- throw error;
2637
+ * @param after Proposed value after the transition.
2638
+ * @param before Persisted value before the transition.
2639
+ * @returns Completion after changed model selections pass authorization.
2640
+ */
2641
+ async authorizePreferences(before, after) {
2642
+ const ai = this.runtime.ai;
2643
+ const policy = repositoryAccess.agentConfiguration(this.repository).ai;
2644
+ if (ai === undefined || policy?.authorizeSelection === undefined)
2645
+ return;
2646
+ const scope = AgentModelSelection.scopeLive(this.invocation, this.repository.stateSchema, this.runtime.signalMetadata, this.tenantId);
2647
+ for (const kind of [AiModelKind.GENERATION, AiModelKind.DECISION]) {
2648
+ const previous = this.preference(before, kind);
2649
+ const selected = this.preference(after, kind);
2650
+ if (previous === undefined && selected === undefined)
2651
+ continue;
2652
+ if (previous !== undefined &&
2653
+ selected !== undefined &&
2654
+ equals(ModelRefSchema, previous, selected))
2655
+ continue;
2656
+ await AgentModelSelection.authorizeSelection(ai, policy, scope, selected, this.signal, this.deadline(ai));
3249
2657
  }
3250
- },
2658
+ }
3251
2659
  /**
3252
- * Creates one handler-scoped facade from its accepted model policy.
2660
+ * Returns the explicit model reference for one kind, or inherited absence.
3253
2661
  *
3254
- * @param repository Registered Entity repository for this operation.
3255
- * @param runtime Bounded Context storage and publication services.
3256
- * @param tenantId Tenant selected for this accepted signal.
3257
- * @param session Fenced Agent execution session.
3258
- * @param accepted Durable original signal and saved handler bindings.
3259
- * @param ordinal Saved position of the selected handler.
3260
- * @returns AI facade bound to one selected handler.
3261
- */
3262
- handlerFacade(repository, runtime, tenantId, session, accepted, ordinal) {
3263
- const ai = runtime.ai;
3264
- const configuration = repositoryAccess.agentConfiguration(repository).ai;
3265
- if (ai === undefined || configuration === undefined)
3266
- throw new Error("Agent handler requires its bound AI registry.");
3267
- 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));
3268
- },
2662
+ * @param kind Declared model or operation kind.
2663
+ * @param values Typed values being committed.
2664
+ * @returns The explicit model reference for the kind, or inherited absence.
2665
+ */
2666
+ preference(values, kind) {
2667
+ const selected = values.find((entry) => entry.kind === kind);
2668
+ return selected?.selection.case === "model" ? selected.selection.value : undefined;
2669
+ }
3269
2670
  /**
3270
- * Invokes one selected generated handler and validates its declared results.
2671
+ * Creates original selection System Events for changed committed preferences.
3271
2672
  *
3272
- * @param entity Loaded recipient Entity instance.
3273
- * @param accepted Durable original signal and saved handler bindings.
3274
- * @param handler Generated handler selected for this signal.
3275
- * @returns Declared outputs returned by one generated handler.
2673
+ * @param after Proposed value after the transition.
2674
+ * @param before Persisted value before the transition.
2675
+ * @returns The original selection System Events for changed preferences.
3276
2676
  */
3277
- async invokeHandler(entity, accepted, handler) {
3278
- const source = accepted.signal;
3279
- if (source.case !== "command" && source.case !== "event")
3280
- throw new Error("Agent source envelope is missing.");
3281
- const kind = source.case;
3282
- const message = EntityInvocation.unpackRequired(EntityInvocation.requireSignalMessage(source.value.message, kind), handler.handler.schema, kind);
3283
- const context = source.case === "command"
3284
- ? EntityInvocation.commandHandlerContext(source.value)
3285
- : EntityInvocation.eventHandlerContext(source.value);
3286
- const produced = await EntityInvocation.invokeEntityMethod(entity, handler.handler.methodName, message, handler.handler.parameterCount, context);
3287
- const signals = produced === undefined
3288
- ? []
3289
- : Array.isArray(produced)
3290
- ? Array.from(produced)
3291
- : [produced];
3292
- RepositoryHandlers.requireDeclaredOutputs(handler.handler, signals);
3293
- return signals;
3294
- },
2677
+ preferenceEvents(before, after) {
2678
+ const scope = AgentModelSelection.scopeLive(this.invocation, this.repository.stateSchema, this.runtime.signalMetadata, this.tenantId);
2679
+ return [AiModelKind.GENERATION, AiModelKind.DECISION].flatMap((kind) => {
2680
+ const previous = this.preference(before, kind);
2681
+ const selected = this.preference(after, kind);
2682
+ if (previous === undefined && selected === undefined)
2683
+ return [];
2684
+ if (previous !== undefined &&
2685
+ selected !== undefined &&
2686
+ equals(ModelRefSchema, previous, selected))
2687
+ return [];
2688
+ return [this.selectionEvent(scope, kind, previous, selected)];
2689
+ });
2690
+ }
3295
2691
  /**
3296
- * Reads the original invocation deadline before starting an application callback.
2692
+ * Creates one original committed model-selection System Event.
3297
2693
  *
3298
- * @param session Current fenced execution session.
3299
- * @returns Absolute saved deadline in milliseconds.
2694
+ * @param kind Declared model or operation kind.
2695
+ * @param previous Persisted value before the update.
2696
+ * @param scope Captured invocation or tenant scope.
2697
+ * @param selected Models or selection retained for this invocation.
2698
+ * @returns The original committed model-selection System Event.
3300
2699
  */
3301
- handlerDeadline(session) {
3302
- const record = session.record();
3303
- const deadline = record.started?.deadline;
3304
- if (deadline === undefined && record.started?.bounds?.deadlineMillis !== undefined)
3305
- throw new Error("Agent handler requires its saved deadline.");
3306
- if (session.signal.aborted || this.deadlineExpired(record))
3307
- throw new Error("Agent handler deadline or claim ended.");
3308
- return deadline === undefined
3309
- ? undefined
3310
- : Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000);
3311
- },
2700
+ selectionEvent(scope, kind, previous, selected) {
2701
+ const payload = create(SelectionChangedSchema, {
2702
+ agent: scope.agent,
2703
+ sourceSignal: scope.source,
2704
+ kind,
2705
+ ...(previous === undefined ? {} : { previousModel: previous }),
2706
+ ...(selected === undefined ? {} : { selectedModel: selected }),
2707
+ changedAt: Time.currentTime(),
2708
+ });
2709
+ this.runtime.registerSystemEventSchema(SelectionChangedSchema);
2710
+ return AgentLiveInteractionEvents.envelope(this.invocation, this.runtime.signalMetadata, this.invocation.recipientId, SelectionChangedSchema, payload);
2711
+ }
3312
2712
  /**
3313
- * Awaits an application handler within its saved deadline and current claim.
2713
+ * Wraps one committed original System Event in indexed history.
3314
2714
  *
3315
- * @typeParam Value Handler result.
3316
- * @param work Already-started generated handler invocation.
3317
- * @param signal Session cancellation when claim authority ends.
3318
- * @param deadlineEpochMs Original saved invocation deadline.
3319
- * @returns Handler result only while its claim and deadline remain valid.
2715
+ * @param event Original typed Event envelope.
2716
+ * @returns The indexed history entry for the original System Event.
3320
2717
  */
3321
- async awaitHandler(work, signal, deadlineEpochMs) {
3322
- let timer;
3323
- let abort = () => undefined;
3324
- const cutoff = new Promise((_resolve, reject) => {
3325
- abort = () => {
3326
- reject(new Error("Agent handler claim ended."));
3327
- };
3328
- const tick = () => {
3329
- if (deadlineEpochMs === undefined)
3330
- return;
3331
- const remaining = deadlineEpochMs - Time.currentTimeMillis();
3332
- if (remaining <= 0)
3333
- reject(new Error("Agent handler deadline expired."));
3334
- else
3335
- timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
3336
- };
3337
- signal.addEventListener("abort", abort, { once: true });
3338
- if (signal.aborted)
3339
- abort();
3340
- else
3341
- tick();
2718
+ systemEntry(event) {
2719
+ const occurredAt = event.context?.timestamp;
2720
+ if (occurredAt === undefined)
2721
+ throw new Error("Agent System Event requires Time.");
2722
+ return create(AgentHistoryEntrySchema, {
2723
+ occurredAt,
2724
+ item: { case: "systemEvent", value: event },
3342
2725
  });
3343
- try {
3344
- const result = await Promise.race([work, cutoff]);
3345
- if (signal.aborted ||
3346
- (deadlineEpochMs !== undefined && Time.currentTimeMillis() >= deadlineEpochMs))
3347
- throw new Error("Agent handler deadline or claim ended.");
3348
- return result;
3349
- }
3350
- finally {
3351
- clearTimeout(timer);
3352
- signal.removeEventListener("abort", abort);
2726
+ }
2727
+ /**
2728
+ * Publishes produced signals through ordinary tracked publication after commit.
2729
+ *
2730
+ * @param outputs Declared outgoing Command or Event envelopes.
2731
+ */
2732
+ publish(outputs) {
2733
+ for (const output of outputs) {
2734
+ if (output.case === "command")
2735
+ void this.runtime.publisher.publishCommand(output.value);
2736
+ else if (output.value.context?.rejection !== undefined)
2737
+ void this.runtime.publisher.publishRejectionEvent(output.value);
2738
+ else
2739
+ void this.runtime.publisher.publishReactorEvent(output.value);
3353
2740
  }
3354
- },
3355
- };
3356
- /**
3357
- * Adds a positive timeout to a Time-sampled Protobuf timestamp.
3358
- *
3359
- * @param at Provider-independent current time.
3360
- * @param millis Bounded additional milliseconds.
3361
- * @returns Absolute timestamp retaining nanosecond precision.
3362
- */
3363
- function addMillis(at, millis) {
3364
- const nanos = at.nanos + (millis % 1000) * 1_000_000;
3365
- return {
3366
- ...at,
3367
- seconds: at.seconds + BigInt(Math.floor(millis / 1000)) + BigInt(Math.floor(nanos / 1_000_000_000)),
3368
- nanos: nanos % 1_000_000_000,
3369
- };
2741
+ }
3370
2742
  }
3371
2743
  /**
3372
2744
  * Executes one Process Manager Command against its loaded Entity state.
@@ -3468,7 +2840,7 @@ class ProcessManagerCommandExecution {
3468
2840
  async #commitAndPublish(loaded, tenantOptions, intake, producedSignals) {
3469
2841
  const commands = this.#commandOutputs(intake.assignee, producedSignals);
3470
2842
  const events = this.#eventOutputs(intake.assignee, producedSignals, intake.route.entityId, RepositoryEntities.priorVersion(loaded.current));
3471
- const committed = await this.#support.commit(loaded, tenantOptions, events);
2843
+ const committed = await this.#support.commit(loaded, tenantOptions, events, events);
3472
2844
  if (!committed)
3473
2845
  return undefined;
3474
2846
  this.#publishChangedState(loaded, intake.route.entityId);
@@ -3559,12 +2931,11 @@ class ProcessManagerCommandExecution {
3559
2931
  const produced = await EntityInvocation.invokeEntityMethod(entity, assignee.handler.methodName, message, assignee.handler.parameterCount, EntityInvocation.commandHandlerContext(this.#command));
3560
2932
  const signals = this.#support.normalizeProducedSignals(produced);
3561
2933
  if (signals.length === 0)
3562
- throw new Error("Repository command handlers must return a signal.");
2934
+ throw receptionObservation.recordPostHandlerFailure(new Error("Repository command handlers must return a signal."));
3563
2935
  RepositoryHandlers.requireDeclaredOutputs(assignee.handler, signals);
3564
2936
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, assignee.handler.kind === "command-assignment" && signals.length > 0));
3565
- if (commit.status === "rejected") {
3566
- throw new TransitionValidationError(commit.validation.error);
3567
- }
2937
+ if (commit.status === "rejected")
2938
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
3568
2939
  return signals;
3569
2940
  }
3570
2941
  catch (error) {
@@ -3602,7 +2973,7 @@ class ProcessManagerCommandExecution {
3602
2973
  });
3603
2974
  return create(EventSchema, {
3604
2975
  id: metadata.id,
3605
- message: AnyMessages.pack(schema, signal),
2976
+ message: RepositorySignals.packProduced(schema, signal),
3606
2977
  context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId, version),
3607
2978
  });
3608
2979
  }
@@ -3622,7 +2993,7 @@ class ProcessManagerCommandExecution {
3622
2993
  const metadata = this.#runtime.signalMetadata.commandFromCommand(this.#command);
3623
2994
  return create(CommandSchema, {
3624
2995
  id: metadata.id,
3625
- message: AnyMessages.pack(schema, signal),
2996
+ message: RepositorySignals.packProduced(schema, signal),
3626
2997
  context: metadata.context,
3627
2998
  });
3628
2999
  }));
@@ -3648,7 +3019,7 @@ class ProcessManagerCommandExecution {
3648
3019
  #postEvents(events) {
3649
3020
  for (const event of events) {
3650
3021
  // spine-log-boundary: server.repository_event_follow_up
3651
- void this.#runtime.publisher.publishEvent(event);
3022
+ void this.#runtime.publisher.publishReactorEvent(event);
3652
3023
  }
3653
3024
  }
3654
3025
  }
@@ -3736,7 +3107,7 @@ class ProcessManagerEventExecution {
3736
3107
  *
3737
3108
  * @param entityId Target Entity identifier.
3738
3109
  * @param intake Decoded Event and matching handlers.
3739
- * @returns Completion after accepted results are published.
3110
+ * @returns Completion after accepted results are registered for publication.
3740
3111
  */
3741
3112
  async #executeEntity(entityId, intake) {
3742
3113
  const tenantOptions = RepositoryTenants.standTenantOptions(this.#runtime.context, this.#event);
@@ -3744,15 +3115,16 @@ class ProcessManagerEventExecution {
3744
3115
  const produced = await this.#invokeHandlers(entityId, loaded.entity, intake, tenantOptions.tenantId);
3745
3116
  const events = this.#bindProducedEvents(produced.events, entityId, RepositoryEntities.priorVersion(loaded.current));
3746
3117
  const commands = this.#bindProducedCommands(produced.commands);
3747
- const diagnostics = this.#repository.entityFamily === "agent"
3748
- ? events
3749
- : [DispatchGuards.guardedJournalEvent(this.#repository, this.#event, entityId), ...events];
3118
+ const diagnostics = [
3119
+ DispatchGuards.guardedJournalEvent(this.#repository, this.#event, entityId),
3120
+ ...events,
3121
+ ];
3750
3122
  const committed = await this.#support.commit(loaded, tenantOptions, diagnostics, events);
3751
3123
  if (!committed)
3752
3124
  return;
3753
3125
  this.#publishChangedState(loaded, entityId);
3754
3126
  this.#postEvents(events);
3755
- await this.#postCommands(commands);
3127
+ this.#postCommands(commands);
3756
3128
  }
3757
3129
  /**
3758
3130
  * Publishes a state-change System Event when the Entity changed.
@@ -3868,7 +3240,7 @@ class ProcessManagerEventExecution {
3868
3240
  async #commitEntity(entity, producedEvents) {
3869
3241
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, producedEvents));
3870
3242
  if (commit.status === "rejected")
3871
- throw new TransitionValidationError(commit.validation.error);
3243
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
3872
3244
  }
3873
3245
  /**
3874
3246
  * Binds reactor results as Entity Events.
@@ -3900,7 +3272,7 @@ class ProcessManagerEventExecution {
3900
3272
  });
3901
3273
  return create(EventSchema, {
3902
3274
  id: metadata.id,
3903
- message: AnyMessages.pack(schema, signal),
3275
+ message: RepositorySignals.packProduced(schema, signal),
3904
3276
  context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId, version),
3905
3277
  });
3906
3278
  }
@@ -3920,7 +3292,7 @@ class ProcessManagerEventExecution {
3920
3292
  const metadata = this.#runtime.signalMetadata.commandFromEvent(this.#event);
3921
3293
  return create(CommandSchema, {
3922
3294
  id: metadata.id,
3923
- message: AnyMessages.pack(schema, signal),
3295
+ message: RepositorySignals.packProduced(schema, signal),
3924
3296
  context: metadata.context,
3925
3297
  });
3926
3298
  }));
@@ -3929,11 +3301,11 @@ class ProcessManagerEventExecution {
3929
3301
  * Publishes each produced Command in handler order.
3930
3302
  *
3931
3303
  * @param commands Bound Commands to publish.
3932
- * @returns Completion after all Commands are published.
3933
3304
  */
3934
- async #postCommands(commands) {
3305
+ #postCommands(commands) {
3935
3306
  for (const command of commands) {
3936
- await this.#runtime.publisher.publishCommand(command);
3307
+ // spine-log-boundary: server.process_manager_command_follow_up
3308
+ void this.#runtime.publisher.publishCommand(command);
3937
3309
  }
3938
3310
  }
3939
3311
  /**
@@ -3944,7 +3316,7 @@ class ProcessManagerEventExecution {
3944
3316
  #postEvents(events) {
3945
3317
  for (const event of events) {
3946
3318
  // spine-log-boundary: server.process_manager_event_follow_up
3947
- void this.#runtime.publisher.publishEvent(event);
3319
+ void this.#runtime.publisher.publishReactorEvent(event);
3948
3320
  }
3949
3321
  }
3950
3322
  }
@@ -4238,6 +3610,15 @@ Object.freeze(RepositoryIdentity);
4238
3610
  * Internal entity invocation operations.
4239
3611
  */
4240
3612
  const EntityInvocation = {
3613
+ /**
3614
+ * Returns a rejected Entity transition as an exact reception failure.
3615
+ *
3616
+ * @param validationError The rejected transition detail.
3617
+ * @returns The original transition error for propagation.
3618
+ */
3619
+ rejectedTransition(validationError) {
3620
+ return receptionObservation.recordPostHandlerFailure(new TransitionValidationError(validationError));
3621
+ },
4241
3622
  /**
4242
3623
  * Reads a generated message's type name or rejects malformed output.
4243
3624
  *
@@ -4266,7 +3647,23 @@ const EntityInvocation = {
4266
3647
  if (typeof method !== "function") {
4267
3648
  throw new TypeError(`Repository entity execution requires method "${methodName}".`);
4268
3649
  }
4269
- return Reflect.apply(method, entity, parameterCount === 2 ? [message, context] : [message]);
3650
+ let result;
3651
+ try {
3652
+ result = Reflect.apply(method, entity, parameterCount === 2 ? [message, context] : [message]);
3653
+ }
3654
+ catch (error) {
3655
+ receptionObservation.recordHandlerFailure(error);
3656
+ throw error;
3657
+ }
3658
+ if (result !== null &&
3659
+ (typeof result === "object" || typeof result === "function") &&
3660
+ typeof result.then === "function") {
3661
+ return Promise.resolve(result).catch((error) => {
3662
+ receptionObservation.recordHandlerFailure(error);
3663
+ throw error;
3664
+ });
3665
+ }
3666
+ return result;
4270
3667
  },
4271
3668
  /**
4272
3669
  * Copies a Command context or creates an empty context when absent.
@@ -4299,7 +3696,7 @@ const EntityInvocation = {
4299
3696
  * @returns Decoded generated message.
4300
3697
  */
4301
3698
  unpackRequired(message, schema, signalKind) {
4302
- const unpacked = AnyMessages.unpack(message, schema);
3699
+ const unpacked = AnyPacker.tryUnpack(message, schema);
4303
3700
  if (unpacked === undefined) {
4304
3701
  throw new Error(`Repository ${signalKind} execution requires a readable message.`);
4305
3702
  }
@@ -4337,9 +3734,9 @@ const RepositoryEntities = {
4337
3734
  bindStorage(repository, runtime, tenantId, entityId, entity) {
4338
3735
  const storageInput = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
4339
3736
  const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, storageInput);
4340
- RepositoryHistoryInternals.bindEntityHistory(entity, storage, entityId, repository.stateSchema);
3737
+ RepositoryHistoryInternals.bindEntityHistory(repository, entity, storage, entityId, repository.stateSchema, storageInput.context.tenantId);
4341
3738
  if (repository.entityFamily === "agent")
4342
- this.bindAgentHistory(entity, entityId, storageInput, storage.agentHistory);
3739
+ this.bindAgentHistory(repository, entity, entityId, storageInput, storage.agentHistory);
4343
3740
  return {
4344
3741
  commits: storage.commits,
4345
3742
  events: storage.events,
@@ -4350,17 +3747,26 @@ const RepositoryEntities = {
4350
3747
  /**
4351
3748
  * Attaches the indexed history port to one restored Agent.
4352
3749
  *
3750
+ * @param repository Repository tracking the captured runtime.
4353
3751
  * @param entity Restored Agent instance.
4354
3752
  * @param entityId Typed Agent identifier.
4355
3753
  * @param input Entity storage scope.
4356
3754
  * @param history Mandatory Agent history port.
4357
3755
  */
4358
- bindAgentHistory(entity, entityId, input, history) {
3756
+ bindAgentHistory(repository, entity, entityId, input, history) {
4359
3757
  if (history === undefined)
4360
3758
  throw new Error("Agent history storage is required.");
3759
+ const lifetime = RepositoryStorage.lifetime(repository);
3760
+ const runtime = repositoryRuntimes.get(repository);
4361
3761
  AgentHistoryReads.bind(entity, {
4362
3762
  storage: history,
4363
3763
  entityId,
3764
+ onAdmit: async (onRead) => {
3765
+ if (runtime === undefined || repositoryRuntimes.get(repository) !== runtime)
3766
+ throw new Error("Agent history requires its active runtime.");
3767
+ await runtime.onAdmit(input.context.tenantId);
3768
+ return RepositoryStorage.track(repository, onRead, lifetime);
3769
+ },
4364
3770
  scope: {
4365
3771
  context: input.context.name,
4366
3772
  tenant: JSON.stringify(input.context.tenantId ?? null),
@@ -4473,6 +3879,34 @@ Object.freeze(RepositoryEntities);
4473
3879
  * Internal repository signals operations.
4474
3880
  */
4475
3881
  const RepositorySignals = {
3882
+ /**
3883
+ * Packs one handler-produced signal and records only its validation failure.
3884
+ *
3885
+ * @typeParam Schema Generated schema for the signal.
3886
+ * @param schema Generated schema for the signal.
3887
+ * @param message Handler-produced message.
3888
+ * @returns Validated packed signal.
3889
+ */
3890
+ packProduced(schema, message) {
3891
+ try {
3892
+ return RepositorySignals.packValidated(schema, message);
3893
+ }
3894
+ catch (error) {
3895
+ throw receptionObservation.recordPostHandlerFailure(error);
3896
+ }
3897
+ },
3898
+ /**
3899
+ * Packs a repository-produced value after checking its domain constraints.
3900
+ *
3901
+ * @typeParam Schema Generated schema for the value.
3902
+ * @param schema Generated schema for the value.
3903
+ * @param message Repository-produced message to pack.
3904
+ * @returns Validated packed message.
3905
+ */
3906
+ packValidated(schema, message) {
3907
+ Validate.check(schema, message);
3908
+ return AnyPacker.pack(schema, message);
3909
+ },
4476
3910
  /**
4477
3911
  * Validates that a rejection type was declared by the invoked handler.
4478
3912
  *
@@ -4560,7 +3994,7 @@ const RepositorySignals = {
4560
3994
  const metadata = runtime.signalMetadata.eventFromCommand(command, {});
4561
3995
  return create(EventSchema, {
4562
3996
  id: metadata.id,
4563
- message: AnyMessages.pack(rejection.schema, rejection.messageThrown()),
3997
+ message: RepositorySignals.packValidated(rejection.schema, rejection.messageThrown()),
4564
3998
  context: create(EventContextSchema, {
4565
3999
  ...RepositorySignals.eventContextWithProducer(metadata.context, repository, entityId),
4566
4000
  rejection: create(RejectionEventContextSchema, {
@@ -4624,7 +4058,7 @@ class EntityStateChangePublishing {
4624
4058
  this.#publish(runtime, () => runtime.signalMetadata.eventFromCommand(command, {
4625
4059
  version,
4626
4060
  }), {
4627
- id: AnyMessages.pack(CommandIdSchema, command.id),
4061
+ id: RepositorySignals.packValidated(CommandIdSchema, command.id),
4628
4062
  typeUrl: command.message?.typeUrl ?? "",
4629
4063
  }, { repository, entityId, oldState, oldLifecycle, newState, lifecycle, version });
4630
4064
  }
@@ -4645,7 +4079,7 @@ class EntityStateChangePublishing {
4645
4079
  this.#publish(runtime, () => runtime.signalMetadata.eventFromEvent(source, {
4646
4080
  version,
4647
4081
  }), {
4648
- id: AnyMessages.pack(EventIdSchema, source.id),
4082
+ id: RepositorySignals.packValidated(EventIdSchema, source.id),
4649
4083
  typeUrl: source.message?.typeUrl ?? "",
4650
4084
  }, { repository, entityId, oldState, oldLifecycle, newState, lifecycle, version });
4651
4085
  }
@@ -4664,7 +4098,7 @@ class EntityStateChangePublishing {
4664
4098
  runtime.registerSystemEventSchema(draft.schema);
4665
4099
  const event = create(EventSchema, {
4666
4100
  id: metadata.id,
4667
- message: AnyMessages.pack(draft.schema, draft.messageAt(metadata.context.timestamp)),
4101
+ message: RepositorySignals.packValidated(draft.schema, draft.messageAt(metadata.context.timestamp)),
4668
4102
  context: RepositorySignals.eventContextWithProducer(metadata.context, change.repository, change.entityId),
4669
4103
  });
4670
4104
  this.#post(runtime, event);
@@ -4698,7 +4132,7 @@ class EntityStateChangePublishing {
4698
4132
  return {
4699
4133
  entity,
4700
4134
  signalId: [create(MessageIdSchema, { id: origin.id, typeUrl: origin.typeUrl })],
4701
- state: AnyMessages.pack(change.repository.stateSchema, change.newState),
4135
+ state: RepositorySignals.packValidated(change.repository.stateSchema, change.newState),
4702
4136
  version: create(VersionSchema, { number: change.version }),
4703
4137
  };
4704
4138
  }
@@ -4741,7 +4175,7 @@ class EntityStateChangePublishing {
4741
4175
  ...(change.oldState === undefined
4742
4176
  ? {}
4743
4177
  : {
4744
- oldState: AnyMessages.pack(change.repository.stateSchema, change.oldState),
4178
+ oldState: RepositorySignals.packValidated(change.repository.stateSchema, change.oldState),
4745
4179
  }),
4746
4180
  newState: fields.state,
4747
4181
  signalId: fields.signalId,
@@ -4906,7 +4340,7 @@ class HandlerDispatchPublishing {
4906
4340
  });
4907
4341
  return create(EventSchema, {
4908
4342
  id: runtime.signalMetadata.eventId(),
4909
- message: AnyMessages.pack(EntityLog.CommandDispatchedToHandlerSchema, this.#message(repository, command, entityId, context.timestamp)),
4343
+ message: RepositorySignals.packValidated(EntityLog.CommandDispatchedToHandlerSchema, this.#message(repository, command, entityId, context.timestamp)),
4910
4344
  context,
4911
4345
  });
4912
4346
  }
@@ -4956,7 +4390,7 @@ class HandlerDispatchPublishing {
4956
4390
  });
4957
4391
  return create(EventSchema, {
4958
4392
  id: runtime.signalMetadata.eventId(),
4959
- message: AnyMessages.pack(EntityLog.EventDispatchedToReactorSchema, this.#eventMessage(EntityLog.EventDispatchedToReactorSchema, repository, source, entityId, context.timestamp)),
4393
+ message: RepositorySignals.packValidated(EntityLog.EventDispatchedToReactorSchema, this.#eventMessage(EntityLog.EventDispatchedToReactorSchema, repository, source, entityId, context.timestamp)),
4960
4394
  context,
4961
4395
  });
4962
4396
  }
@@ -4985,7 +4419,7 @@ class HandlerDispatchPublishing {
4985
4419
  });
4986
4420
  const diagnostic = create(EventSchema, {
4987
4421
  id: runtime.signalMetadata.eventId(),
4988
- message: AnyMessages.pack(schema, this.#eventMessage(schema, repository, event, entityId, context.timestamp)),
4422
+ message: RepositorySignals.packValidated(schema, this.#eventMessage(schema, repository, event, entityId, context.timestamp)),
4989
4423
  context,
4990
4424
  });
4991
4425
  this.#post(runtime, schema, diagnostic);
@@ -5074,6 +4508,7 @@ const HandlerDispatchPublisher = Object.freeze(new HandlerDispatchPublishing());
5074
4508
  Object.freeze(RepositorySignals);
5075
4509
  /**
5076
4510
  * Internal repository stand operations.
4511
+ *
5077
4512
  * @internal
5078
4513
  */
5079
4514
  export const RepositoryStand = {
@@ -5106,60 +4541,12 @@ export const RepositoryStand = {
5106
4541
  lifecycle,
5107
4542
  });
5108
4543
  },
5109
- /**
5110
- * Maps one Event position to its Process Manager producer version.
5111
- *
5112
- * @param sequence One-based position among produced Events.
5113
- * @returns Producer sequence number for Event metadata.
5114
- */
5115
4544
  };
5116
4545
  Object.freeze(RepositoryStand);
5117
4546
  /**
5118
4547
  * Internal repository tenants operations.
5119
4548
  */
5120
4549
  const RepositoryTenants = {
5121
- /**
5122
- * Builds tenant-aware storage context for Entity inbox delivery.
5123
- *
5124
- * @param context Bounded context storage mode.
5125
- * @param tenantId Delivery tenant when multitenant.
5126
- * @returns Storage context for durable Entity inbox rows.
5127
- */
5128
- entityInboxDeliveryContext(context, tenantId) {
5129
- if (!context.multitenant) {
5130
- return Object.freeze({ name: context.name, multitenant: false });
5131
- }
5132
- const tid = tenantId;
5133
- if (tid === undefined) {
5134
- throw new Error(`Multitenant Entity Inbox handoff for "${context.name}" requires tenantId.`);
5135
- }
5136
- return Object.freeze({
5137
- name: context.name,
5138
- multitenant: true,
5139
- tenantId: RepositoryTenants.require(tid),
5140
- });
5141
- },
5142
- /**
5143
- * Builds tenant-aware storage context for Projection inbox delivery.
5144
- *
5145
- * @param context Bounded context storage mode.
5146
- * @param tenantId Delivery tenant when multitenant.
5147
- * @returns Storage context for durable Projection inbox rows.
5148
- */
5149
- projectionDeliveryContext(context, tenantId) {
5150
- if (!context.multitenant) {
5151
- return Object.freeze({ name: context.name, multitenant: false });
5152
- }
5153
- const tid = tenantId;
5154
- if (tid === undefined) {
5155
- throw new Error(`Multitenant projection inbox handoff for "${context.name}" requires tenantId.`);
5156
- }
5157
- return Object.freeze({
5158
- name: context.name,
5159
- multitenant: true,
5160
- tenantId: RepositoryTenants.require(tid),
5161
- });
5162
- },
5163
4550
  /**
5164
4551
  * Validates an Event tenant before Projection inbox handoff.
5165
4552
  *
@@ -5366,9 +4753,15 @@ const RepositoryHandlers = {
5366
4753
  requireDeclaredOutputs(handler, signals) {
5367
4754
  const schemas = HandlerMetadataValues.returnedSchemas(handler);
5368
4755
  for (const signal of signals) {
5369
- const typeName = EntityInvocation.messageTypeName(signal);
4756
+ let typeName;
4757
+ try {
4758
+ typeName = EntityInvocation.messageTypeName(signal);
4759
+ }
4760
+ catch (error) {
4761
+ throw receptionObservation.recordPostHandlerFailure(error);
4762
+ }
5370
4763
  if (!schemas.some((schema) => schema.typeName === typeName)) {
5371
- throw new Error(`Handler "${handler.methodName}" returned undeclared message "${typeName}".`);
4764
+ throw receptionObservation.recordPostHandlerFailure(new Error(`Handler "${handler.methodName}" returned undeclared message "${typeName}".`));
5372
4765
  }
5373
4766
  }
5374
4767
  },
@@ -5909,7 +5302,7 @@ const RepositoryRoutes = {
5909
5302
  * @returns Candidate target Entity ID.
5910
5303
  */
5911
5304
  async callCommandRoute(route, message, schema, context, reads) {
5912
- const unpacked = AnyMessages.unpack(message, schema);
5305
+ const unpacked = AnyPacker.tryUnpack(message, schema);
5913
5306
  if (unpacked === undefined) {
5914
5307
  throw new Error("Repository command routing requires a readable Command message.");
5915
5308
  }
@@ -6073,7 +5466,7 @@ const RepositoryRoutes = {
6073
5466
  if (message?.typeUrl !== TypeUrls.derive(EntityLog.EntityStateChangedSchema)) {
6074
5467
  throw new Error(`${operation} requires an EntityStateChanged System event.`);
6075
5468
  }
6076
- const changed = AnyMessages.unpack(message, EntityLog.EntityStateChangedSchema);
5469
+ const changed = AnyPacker.tryUnpack(message, EntityLog.EntityStateChangedSchema);
6077
5470
  const packedState = changed?.newState;
6078
5471
  if (packedState === undefined || packedState.typeUrl === "") {
6079
5472
  throw new Error(`${operation} requires EntityStateChanged.newState.`);
@@ -6081,7 +5474,7 @@ const RepositoryRoutes = {
6081
5474
  const schema = schemas.find((candidate) => TypeUrls.derive(candidate) === packedState.typeUrl);
6082
5475
  if (schema === undefined)
6083
5476
  return undefined;
6084
- const state = AnyMessages.unpack(packedState, schema);
5477
+ const state = AnyPacker.tryUnpack(packedState, schema);
6085
5478
  if (state === undefined)
6086
5479
  throw new Error(`${operation} requires a readable Entity state.`);
6087
5480
  return Object.freeze({ schema, state });
@@ -6124,7 +5517,7 @@ const RepositoryRoutes = {
6124
5517
  * @returns Validated target Entity IDs.
6125
5518
  */
6126
5519
  async callEventRoute(route, message, schema, context, targetIdField, reads) {
6127
- const unpacked = AnyMessages.unpack(message, schema);
5520
+ const unpacked = AnyPacker.tryUnpack(message, schema);
6128
5521
  if (unpacked === undefined) {
6129
5522
  throw new Error("Repository event routing requires a readable Event message.");
6130
5523
  }
@@ -6178,7 +5571,7 @@ const RepositoryRoutes = {
6178
5571
  * @returns Nonempty first-field value.
6179
5572
  */
6180
5573
  readFirstFieldId(message, schema, signalKind) {
6181
- const unpacked = AnyMessages.unpack(message, schema);
5574
+ const unpacked = AnyPacker.tryUnpack(message, schema);
6182
5575
  const firstField = schema.fields[0];
6183
5576
  if (unpacked === undefined || firstField === undefined) {
6184
5577
  throw new Error(`Repository ${signalKind} routing requires a readable first field.`);
@@ -6423,6 +5816,229 @@ Object.freeze(RepositoryRoutes);
6423
5816
  * Internal repository storage operations.
6424
5817
  */
6425
5818
  const RepositoryStorage = {
5819
+ /**
5820
+ * Returns one retained EventStore for unchanged Process Manager output.
5821
+ *
5822
+ * @param repository Registered Process Manager repository.
5823
+ * @param runtime Its captured runtime services.
5824
+ * @returns Tenant-aware EventStore for produced Events.
5825
+ */
5826
+ eventStore(repository, runtime) {
5827
+ const lifetime = this.lifetime(repository);
5828
+ lifetime.eventStore ??= new EventStore(runtime.context, runtime.storageFactory);
5829
+ return lifetime.eventStore;
5830
+ },
5831
+ /**
5832
+ * Creates one repository runtime's retained storage registry.
5833
+ *
5834
+ * @returns Empty registry accepting storage work.
5835
+ */
5836
+ newLifetime() {
5837
+ return {
5838
+ entities: new Map(),
5839
+ histories: new Map(),
5840
+ ports: new Set(),
5841
+ commits: new Set(),
5842
+ active: new Set(),
5843
+ open: true,
5844
+ };
5845
+ },
5846
+ /**
5847
+ * Returns the active storage registry for one registered repository.
5848
+ *
5849
+ * @param repository Registered repository.
5850
+ * @returns Its current retained-storage registry.
5851
+ */
5852
+ lifetime(repository) {
5853
+ const lifetime = repositoryStorageLifetimes.get(repository);
5854
+ if (lifetime?.open !== true)
5855
+ throw new Error("Repository storage requires an active runtime.");
5856
+ return lifetime;
5857
+ },
5858
+ /**
5859
+ * Admits one complete physical storage operation before runtime closure.
5860
+ *
5861
+ * @typeParam T Result of the operation.
5862
+ * @param repository Registered repository.
5863
+ * @param work Work that acquires and uses retained storage.
5864
+ * @param expected Captured lifetime for a bound history reader, if any.
5865
+ * @returns The operation result or its original failure.
5866
+ */
5867
+ async track(repository, work, expected) {
5868
+ const lifetime = this.lifetime(repository);
5869
+ if (expected !== undefined && lifetime !== expected)
5870
+ throw new Error("Repository history requires its active runtime.");
5871
+ const result = work();
5872
+ lifetime.active.add(result);
5873
+ return result.finally(() => {
5874
+ lifetime.active.delete(result);
5875
+ });
5876
+ },
5877
+ /**
5878
+ * Starts repository shutdown after new admissions are fenced.
5879
+ *
5880
+ * @param repository Repository being detached.
5881
+ * @returns Completion after admitted operations and every close attempt.
5882
+ */
5883
+ closeRuntime(repository) {
5884
+ const lifetime = repositoryStorageLifetimes.get(repository);
5885
+ if (lifetime === undefined)
5886
+ return Promise.resolve();
5887
+ if (lifetime.closing !== undefined)
5888
+ return lifetime.closing;
5889
+ if (lifetime.idleOutcome !== undefined) {
5890
+ const error = lifetime.idleOutcome.error;
5891
+ return error === undefined ? Promise.resolve() : Promise.reject(error);
5892
+ }
5893
+ lifetime.open = false;
5894
+ lifetime.closing = this.drainLifetime(lifetime);
5895
+ return lifetime.closing;
5896
+ },
5897
+ /**
5898
+ * Rejects synchronous rollback while repository storage work remains active.
5899
+ *
5900
+ * @param repository Repository being rolled back.
5901
+ */
5902
+ assertIdle(repository) {
5903
+ const lifetime = repositoryStorageLifetimes.get(repository);
5904
+ if (lifetime !== undefined && (lifetime.active.size > 0 || lifetime.closing !== undefined))
5905
+ throw new Error("Repository idle cleanup requires no active storage work.");
5906
+ },
5907
+ /**
5908
+ * Closes retained resources synchronously during idle assembly rollback.
5909
+ *
5910
+ * @param repository Repository being rolled back.
5911
+ */
5912
+ closeRuntimeIdle(repository) {
5913
+ const lifetime = repositoryStorageLifetimes.get(repository);
5914
+ if (lifetime === undefined)
5915
+ return;
5916
+ if (lifetime.idleOutcome !== undefined) {
5917
+ if (lifetime.idleOutcome.error !== undefined)
5918
+ throw lifetime.idleOutcome.error;
5919
+ return;
5920
+ }
5921
+ lifetime.open = false;
5922
+ const errors = [];
5923
+ if (lifetime.eventStore !== undefined) {
5924
+ try {
5925
+ eventStoreAccess.closeIdle(lifetime.eventStore);
5926
+ }
5927
+ catch (error) {
5928
+ errors.push(error);
5929
+ }
5930
+ }
5931
+ errors.push(...this.closeLifetimePortsIdle(lifetime));
5932
+ const failure = this.closeFailure(errors);
5933
+ lifetime.idleOutcome = failure === undefined ? {} : { error: failure };
5934
+ if (failure !== undefined)
5935
+ throw failure;
5936
+ },
5937
+ /**
5938
+ * Waits for admitted storage operations, closes the retained EventStore, and releases all ports.
5939
+ *
5940
+ * @param lifetime Repository storage registry being closed.
5941
+ * @returns Completion after every close attempt.
5942
+ */
5943
+ async drainLifetime(lifetime) {
5944
+ await Promise.allSettled([...lifetime.active]);
5945
+ const errors = [];
5946
+ if (lifetime.eventStore !== undefined) {
5947
+ try {
5948
+ await lifetime.eventStore.close();
5949
+ }
5950
+ catch (error) {
5951
+ errors.push(error);
5952
+ }
5953
+ }
5954
+ errors.push(...(await this.closeLifetimePorts(lifetime)));
5955
+ const failure = this.closeFailure(errors);
5956
+ if (failure !== undefined)
5957
+ throw failure;
5958
+ },
5959
+ /**
5960
+ * Closes each retained physical port once.
5961
+ *
5962
+ * @param lifetime Repository storage registry being closed.
5963
+ * @returns Every close failure in attempt order.
5964
+ */
5965
+ async closeLifetimePorts(lifetime) {
5966
+ const commits = [...lifetime.commits];
5967
+ lifetime.commits.clear();
5968
+ const ports = [...lifetime.ports];
5969
+ lifetime.ports.clear();
5970
+ const errors = [];
5971
+ const closing = commits.map((commit) => {
5972
+ try {
5973
+ return commit.close();
5974
+ }
5975
+ catch (error) {
5976
+ errors.push(error);
5977
+ return Promise.resolve();
5978
+ }
5979
+ });
5980
+ for (const result of await Promise.allSettled(closing))
5981
+ if (result.status === "rejected")
5982
+ errors.push(result.reason);
5983
+ for (const port of ports) {
5984
+ try {
5985
+ port.close();
5986
+ }
5987
+ catch (error) {
5988
+ errors.push(error);
5989
+ }
5990
+ }
5991
+ lifetime.entities.clear();
5992
+ lifetime.histories.clear();
5993
+ delete lifetime.eventStore;
5994
+ return errors;
5995
+ },
5996
+ /**
5997
+ * Closes ports synchronously during assembly rollback.
5998
+ *
5999
+ * @param lifetime The captured repository storage lifetime.
6000
+ * @returns Synchronous cleanup failures, if any.
6001
+ */
6002
+ closeLifetimePortsIdle(lifetime) {
6003
+ const commits = [...lifetime.commits];
6004
+ lifetime.commits.clear();
6005
+ const ports = [...lifetime.ports];
6006
+ lifetime.ports.clear();
6007
+ lifetime.entities.clear();
6008
+ lifetime.histories.clear();
6009
+ delete lifetime.eventStore;
6010
+ const errors = [];
6011
+ for (const commit of commits) {
6012
+ try {
6013
+ commit.closeIdle();
6014
+ }
6015
+ catch (error) {
6016
+ errors.push(error);
6017
+ }
6018
+ }
6019
+ for (const port of ports) {
6020
+ try {
6021
+ port.close();
6022
+ }
6023
+ catch (error) {
6024
+ errors.push(error);
6025
+ }
6026
+ }
6027
+ return errors;
6028
+ },
6029
+ /**
6030
+ * Returns one close failure or aggregates multiple failures.
6031
+ *
6032
+ * @param errors Physical port cleanup failures.
6033
+ * @returns The reported cleanup failure, if any.
6034
+ */
6035
+ closeFailure(errors) {
6036
+ if (errors.length === 0)
6037
+ return undefined;
6038
+ if (errors.length === 1)
6039
+ return errors[0];
6040
+ return new AggregateError(errors, "Repository storage close failed.");
6041
+ },
6426
6042
  /**
6427
6043
  * Opens provider Entity storage for one read scope without a commit handle.
6428
6044
  *
@@ -6439,72 +6055,120 @@ const RepositoryStorage = {
6439
6055
  return candidate.createEntityStorage(input);
6440
6056
  },
6441
6057
  /**
6442
- * Opens Entity storage and an atomic commit port from a storage factory.
6058
+ * Returns the canonical tenant/model/history bucket for Entity storage.
6059
+ *
6060
+ * @typeParam I Typed Entity identifier.
6061
+ * @typeParam S Generated Entity state.
6062
+ * @param lifetime Active repository storage registry.
6063
+ * @param input Captured Entity storage configuration.
6064
+ * @returns Slots for one model and history configuration.
6065
+ */
6066
+ entityBucket(lifetime, input) {
6067
+ const key = JSON.stringify({
6068
+ context: input.context.name,
6069
+ state: input.sourceType.typeName,
6070
+ history: input.stateHistory,
6071
+ });
6072
+ let bucket = lifetime.entities.get(key);
6073
+ if (bucket === undefined) {
6074
+ bucket = new Map();
6075
+ lifetime.entities.set(key, bucket);
6076
+ }
6077
+ return bucket;
6078
+ },
6079
+ /**
6080
+ * Returns the canonical tenant/model bucket for Agent audit history.
6081
+ *
6082
+ * @typeParam I Typed Agent identifier.
6083
+ * @typeParam S Generated Agent state.
6084
+ * @param lifetime Active repository storage registry.
6085
+ * @param input Captured Agent storage configuration.
6086
+ * @returns Audit ports independent of the state-history switch.
6087
+ */
6088
+ historyBucket(lifetime, input) {
6089
+ const key = JSON.stringify({ context: input.context.name, state: input.sourceType.typeName });
6090
+ let bucket = lifetime.histories.get(key);
6091
+ if (bucket === undefined) {
6092
+ bucket = new Map();
6093
+ lifetime.histories.set(key, bucket);
6094
+ }
6095
+ return bucket;
6096
+ },
6097
+ /**
6098
+ * Returns retained read-only Entity storage without acquiring a commit port.
6443
6099
  *
6444
- * @typeParam I Entity identifier type.
6445
- * @typeParam S Generated Entity state type.
6446
- * @param factory Provider storage factory.
6447
- * @param input Entity storage location and schema.
6448
- * @param agent Whether to open mandatory Agent history.
6449
- * @returns Storage ports closed together by one handle.
6100
+ * @typeParam I Typed Entity identifier.
6101
+ * @typeParam S Generated Entity state.
6102
+ * @param repository Registered repository.
6103
+ * @param factory Repository storage factory.
6104
+ * @param input Captured Entity storage configuration.
6105
+ * @returns Reusable current, state-history, and event-history ports.
6450
6106
  */
6451
- openEntityStorage(factory, input, agent = false) {
6452
- const candidate = factory;
6453
- if (candidate.createEntityStorage === undefined)
6454
- throw new Error("StorageFactory does not provide required atomic Entity commit storage.");
6455
- const entity = candidate.createEntityStorage(input);
6456
- const commits = EntityCommitStorageFactories.create(factory, input);
6457
- let ports;
6458
- try {
6459
- if (agent)
6460
- ports = this.openAgentPorts(factory, input);
6461
- }
6462
- catch (error) {
6463
- commits.close();
6464
- entity.close();
6465
- throw error;
6466
- }
6467
- return {
6468
- ...entity,
6469
- commits,
6470
- ...ports,
6471
- close: () => {
6472
- ports?.agentExecution.close();
6473
- ports?.agentHistory.close();
6474
- commits.close();
6475
- entity.close();
6476
- },
6477
- };
6107
+ repositoryReadEntityStorage(repository, factory, input) {
6108
+ const lifetime = this.lifetime(repository);
6109
+ const bucket = this.entityBucket(lifetime, input);
6110
+ const key = TenantBoundary.of(input.context).key;
6111
+ const existing = bucket.get(key);
6112
+ if (existing !== undefined)
6113
+ return existing.entity;
6114
+ const entity = this.openReadEntityStorage(factory, input);
6115
+ bucket.set(key, { entity });
6116
+ lifetime.ports.add(entity);
6117
+ return entity;
6478
6118
  },
6479
6119
  /**
6480
- * Opens both mandatory Agent ports and closes the first if the second fails.
6120
+ * Returns shared Agent audit history for one tenant and model.
6481
6121
  *
6482
6122
  * @typeParam I Typed Agent identifier.
6483
- * @typeParam S Generated Agent state message.
6484
- * @param factory Storage factory registered with the Bounded Context.
6485
- * @param input Original Inbox or storage input.
6486
- * @returns Indexed history and execution handles for the same tenant.
6487
- */
6488
- openAgentPorts(factory, input) {
6489
- const agentHistory = AgentHistoryStorageFactories.create(factory, {
6123
+ * @typeParam S Generated Agent state.
6124
+ * @param repository Registered Agent repository.
6125
+ * @param factory Repository storage factory.
6126
+ * @param input Captured Entity storage configuration.
6127
+ * @returns Reusable Agent audit-history port.
6128
+ */
6129
+ openRepositoryAgentHistory(repository, factory, input) {
6130
+ const lifetime = this.lifetime(repository);
6131
+ const bucket = this.historyBucket(lifetime, input);
6132
+ const key = TenantBoundary.of(input.context).key;
6133
+ const existing = bucket.get(key);
6134
+ if (existing !== undefined)
6135
+ return existing;
6136
+ const history = AgentHistoryStorageFactories.create(factory, {
6490
6137
  context: input.context,
6491
6138
  stateType: input.sourceType.typeName,
6492
6139
  id: { key: input.id.key },
6493
6140
  });
6494
- try {
6495
- const agentExecution = AgentExecutionStorageFactories.create(factory, {
6496
- entity: input,
6497
- stateType: input.sourceType.typeName,
6498
- });
6499
- return { agentHistory, agentExecution };
6500
- }
6501
- catch (error) {
6502
- agentHistory.close();
6503
- throw error;
6141
+ bucket.set(key, history);
6142
+ lifetime.ports.add(history);
6143
+ return history;
6144
+ },
6145
+ /**
6146
+ * Closes every acquired port, retaining the first failure and any later failures.
6147
+ *
6148
+ * @param ports Acquired ports in close order.
6149
+ * @param acquisitionError Earlier acquisition failure, if any.
6150
+ */
6151
+ closePorts(ports, acquisitionError) {
6152
+ const errors = acquisitionError === undefined ? [] : [acquisitionError];
6153
+ for (const port of ports) {
6154
+ try {
6155
+ if ("closeIdle" in port)
6156
+ port.closeIdle();
6157
+ else
6158
+ port.close();
6159
+ }
6160
+ catch (error) {
6161
+ errors.push(error);
6162
+ }
6504
6163
  }
6164
+ if (errors.length === 1)
6165
+ throw errors[0];
6166
+ if (errors.length > 1)
6167
+ throw new AggregateError(errors, "Entity storage cleanup failed.");
6505
6168
  },
6506
6169
  /**
6507
- * Returns one shared Entity storage handle per context and state type.
6170
+ * Returns one retained Entity storage handle per context, tenant, state type,
6171
+ * and captured state-history configuration.
6508
6172
  *
6509
6173
  * @typeParam I Entity identifier type.
6510
6174
  * @typeParam S Generated Entity state type.
@@ -6514,20 +6178,72 @@ const RepositoryStorage = {
6514
6178
  * @returns Shared Entity storage handle.
6515
6179
  */
6516
6180
  openRepositoryEntityStorage(repository, factory, input) {
6517
- const key = JSON.stringify({ context: input.context, state: input.sourceType.typeName });
6518
- let handles = repositoryEntityHandles.get(repository);
6519
- const handle = RepositoryStorage.openEntityStorage(factory, input, repository.entityFamily === "agent");
6520
- if (handles === undefined) {
6521
- handles = new Map();
6522
- repositoryEntityHandles.set(repository, handles);
6523
- }
6524
- const existing = handles.get(key);
6525
- if (existing !== undefined) {
6526
- handle.close();
6527
- return existing;
6181
+ const lifetime = this.lifetime(repository);
6182
+ const bucket = this.entityBucket(lifetime, input);
6183
+ const key = TenantBoundary.of(input.context).key;
6184
+ const existing = bucket.get(key);
6185
+ if (existing?.full !== undefined)
6186
+ return existing.full;
6187
+ return this.upgradeRepositoryEntityStorage(repository, factory, input, lifetime, bucket, key, existing);
6188
+ },
6189
+ /**
6190
+ * Adds commit and Agent ports to a retained or newly acquired Entity port.
6191
+ *
6192
+ * @typeParam I Typed Entity identifier.
6193
+ * @typeParam S Generated Entity state.
6194
+ * @param repository Registered repository.
6195
+ * @param factory Repository storage factory.
6196
+ * @param input Captured Entity storage configuration.
6197
+ * @param lifetime Active repository registry.
6198
+ * @param bucket Entity slots for this model and history configuration.
6199
+ * @param key Canonical tenant key.
6200
+ * @param existing Read-only slot if one was opened earlier.
6201
+ * @returns Complete retained write storage.
6202
+ */
6203
+ upgradeRepositoryEntityStorage(repository, factory, input, lifetime, bucket, key, existing) {
6204
+ const entity = existing?.entity ?? this.openReadEntityStorage(factory, input);
6205
+ let commits;
6206
+ try {
6207
+ const commit = EntityCommitStorageFactories.create(factory, input);
6208
+ commits = commit;
6209
+ const agentHistory = repository.entityFamily === "agent"
6210
+ ? this.openRepositoryAgentHistory(repository, factory, input)
6211
+ : undefined;
6212
+ const full = {
6213
+ ...entity,
6214
+ commits: commit,
6215
+ ...(agentHistory === undefined ? {} : { agentHistory }),
6216
+ };
6217
+ this.retainUpgradedSlot(lifetime, bucket, key, existing, full, entity, commit);
6218
+ return full;
6219
+ }
6220
+ catch (error) {
6221
+ const rollback = commits === undefined ? [] : [commits];
6222
+ if (existing === undefined)
6223
+ rollback.push(entity);
6224
+ this.closePorts(rollback, error);
6225
+ throw error;
6528
6226
  }
6529
- handles.set(key, handle);
6530
- return handle;
6227
+ },
6228
+ /**
6229
+ * Records a completed Entity storage upgrade in the current lifetime.
6230
+ *
6231
+ * @typeParam I Typed Entity identifier.
6232
+ * @typeParam S Generated Entity state.
6233
+ * @param lifetime Active repository registry.
6234
+ * @param bucket Entity slots for one model and history configuration.
6235
+ * @param key Canonical tenant key.
6236
+ * @param existing Previously retained read-only slot, if any.
6237
+ * @param full Completed Entity storage.
6238
+ * @param entity Shared Entity read port.
6239
+ * @param commit Newly acquired commit port.
6240
+ */
6241
+ retainUpgradedSlot(lifetime, bucket, key, existing, full, entity, commit) {
6242
+ const slot = existing ?? { entity };
6243
+ slot.full = full;
6244
+ bucket.set(key, slot);
6245
+ lifetime.ports.add(entity);
6246
+ lifetime.commits.add(commit);
6531
6247
  },
6532
6248
  /**
6533
6249
  * Builds Entity storage input with configured state and Event history flags.
@@ -6537,12 +6253,15 @@ const RepositoryStorage = {
6537
6253
  * @returns Entity storage description for the provider.
6538
6254
  */
6539
6255
  entityStorageInput(repository, context) {
6540
- const descriptor = entityStorageDescriptor(context, repositorySpecScanner.scan(repository.entityType, repository.metadata), repository.metadata);
6256
+ const snapshot = repositorySnapshots.get(repository);
6257
+ if (snapshot === undefined)
6258
+ throw new TypeError("Entity storage requires a Repository instance.");
6259
+ const descriptor = entityStorageDescriptor(context, repositorySpecScanner.scan(snapshot.entityType, snapshot.metadata), snapshot.metadata);
6541
6260
  const history = RepositoryStorage.historyConfiguration(repository);
6542
6261
  return {
6543
6262
  ...descriptor,
6544
6263
  stateHistory: history.stateHistory,
6545
- eventHistory: repository.entityFamily === "aggregate" || history.processManagerEventHistory,
6264
+ eventHistory: snapshot.entityFamily === "aggregate" || history.processManagerEventHistory,
6546
6265
  };
6547
6266
  },
6548
6267
  /**
@@ -6597,23 +6316,129 @@ const RepositoryHistoryInternals = {
6597
6316
  /**
6598
6317
  * Binds state and Event history readers to one restored Entity instance.
6599
6318
  *
6319
+ * @param repository Registered repository admitting history reads.
6600
6320
  * @param entity Instance receiving the history accessors.
6601
6321
  * @param storage Entity storage used for reads and maintenance.
6602
6322
  * @param entityId Identifier used to select history records.
6603
6323
  * @param schema Schema used to unpack state records.
6324
+ * @param tenantId Captured tenant for later live membership checks.
6604
6325
  */
6605
- bindEntityHistory(entity, storage, entityId, schema) {
6606
- const stateCache = RepositoryHistoryInternals.createHistoryCache((depth, startingFromVersion) => storage.states.backward(entityId, depth, startingFromVersion), (record) => BigInt(EntityRecords.unpack(schema, record).versionMessage.number), { requireDescendingVersions: true });
6326
+ bindEntityHistory(repository, entity, storage, entityId, schema, tenantId) {
6327
+ const lifetime = RepositoryStorage.lifetime(repository);
6328
+ const runtime = repositoryRuntimes.get(repository);
6329
+ const onAdmit = async () => {
6330
+ if (runtime === undefined || repositoryRuntimes.get(repository) !== runtime)
6331
+ throw new Error("Repository history requires its active runtime.");
6332
+ await runtime.onAdmit(tenantId);
6333
+ };
6334
+ const { stateCache, eventCache } = this.boundHistoryCaches(storage, entityId, schema);
6607
6335
  entityStateHistoryCaches.set(entity, stateCache);
6608
- const eventCache = RepositoryHistoryInternals.createHistoryCache((depth, startingFromVersion) => storage.events.backward(entityId, depth, startingFromVersion), (event) => RepositorySignals.readEventVersion(event), { cacheCompleteVersionGroups: true });
6609
6336
  entityHistoryAccess.bind(entity, {
6610
- stateAt: async (time) => RepositoryHistoryInternals.cloneHistoryState(await storage.states.stateAt(entityId, time)),
6611
- states: async (depth) => RepositoryHistoryInternals.freezeHistoryStates((await stateCache.read(depth)).map((record) => EntityRecords.unpack(schema, record).state)),
6612
- events: async (depth) => RepositoryHistoryInternals.freezeHistoryEvents(await eventCache.read(depth)),
6613
- stateMaintenance: storage.states,
6614
- eventMaintenance: storage.events,
6337
+ stateAt: async (time) => {
6338
+ await onAdmit();
6339
+ return this.readBoundStateAt(repository, lifetime, storage, entityId, time);
6340
+ },
6341
+ states: async (depth) => {
6342
+ await onAdmit();
6343
+ return this.readBoundStates(repository, lifetime, schema, stateCache, depth);
6344
+ },
6345
+ events: async (depth) => {
6346
+ await onAdmit();
6347
+ return this.readBoundEvents(repository, lifetime, eventCache, depth);
6348
+ },
6349
+ stateMaintenance: this.boundStateMaintenance(repository, lifetime, storage.states, onAdmit),
6350
+ eventMaintenance: this.boundEventMaintenance(repository, lifetime, storage.events, onAdmit),
6615
6351
  });
6616
6352
  },
6353
+ /**
6354
+ * Creates state and Event caches over retained history ports.
6355
+ *
6356
+ * @param storage Retained history ports.
6357
+ * @param entityId Captured Entity identifier.
6358
+ * @param schema State schema for record version decoding.
6359
+ * @returns Bound state and Event caches.
6360
+ */
6361
+ boundHistoryCaches(storage, entityId, schema) {
6362
+ const stateCache = this.createHistoryCache((depth, startingFromVersion) => storage.states.backward(entityId, depth, startingFromVersion), (record) => BigInt(EntityRecords.unpack(schema, record).versionMessage.number), { requireDescendingVersions: true });
6363
+ const eventCache = this.createHistoryCache((depth, startingFromVersion) => storage.events.backward(entityId, depth, startingFromVersion), (event) => RepositorySignals.readEventVersion(event), { cacheCompleteVersionGroups: true });
6364
+ return { stateCache, eventCache };
6365
+ },
6366
+ /**
6367
+ * Admits complete state-history maintenance against a captured runtime.
6368
+ *
6369
+ * @param repository Repository with the bound Entity.
6370
+ * @param lifetime Runtime that provided the retained port.
6371
+ * @param states Retained state-history port.
6372
+ * @param onAdmit Checks current membership before physical work.
6373
+ * @returns Guarded maintenance methods.
6374
+ */
6375
+ boundStateMaintenance(repository, lifetime, states, onAdmit) {
6376
+ return {
6377
+ trim: async (id, keep) => {
6378
+ await onAdmit();
6379
+ return RepositoryStorage.track(repository, () => states.trim(id, keep), lifetime);
6380
+ },
6381
+ truncate: async (time) => {
6382
+ await onAdmit();
6383
+ return RepositoryStorage.track(repository, () => states.truncate(time), lifetime);
6384
+ },
6385
+ };
6386
+ },
6387
+ /**
6388
+ * Admits complete Event-history maintenance against a captured runtime.
6389
+ *
6390
+ * @param repository Repository with the bound Entity.
6391
+ * @param lifetime Runtime that provided the retained port.
6392
+ * @param events Retained Event-history port.
6393
+ * @param onAdmit Checks current membership before physical work.
6394
+ * @returns Guarded maintenance method.
6395
+ */
6396
+ boundEventMaintenance(repository, lifetime, events, onAdmit) {
6397
+ return {
6398
+ truncate: async (time) => {
6399
+ await onAdmit();
6400
+ return RepositoryStorage.track(repository, () => events.truncate(time), lifetime);
6401
+ },
6402
+ };
6403
+ },
6404
+ /**
6405
+ * Reads one bound historical state within repository admission.
6406
+ *
6407
+ * @param repository Registered repository.
6408
+ * @param lifetime Captured repository storage lifetime.
6409
+ * @param storage Retained Entity read port.
6410
+ * @param entityId Captured Entity identifier.
6411
+ * @param time Historical instant.
6412
+ * @returns Historical state, if present.
6413
+ */
6414
+ readBoundStateAt(repository, lifetime, storage, entityId, time) {
6415
+ return RepositoryStorage.track(repository, async () => this.cloneHistoryState(await storage.states.stateAt(entityId, time)), lifetime);
6416
+ },
6417
+ /**
6418
+ * Reads a complete bound state-history page within repository admission.
6419
+ *
6420
+ * @param repository Registered repository.
6421
+ * @param lifetime Captured repository storage lifetime.
6422
+ * @param schema State schema used to unpack rows.
6423
+ * @param cache Bound state-history cache.
6424
+ * @param depth Requested history depth.
6425
+ * @returns Frozen historical states.
6426
+ */
6427
+ readBoundStates(repository, lifetime, schema, cache, depth) {
6428
+ return RepositoryStorage.track(repository, async () => this.freezeHistoryStates((await cache.read(depth)).map((record) => EntityRecords.unpack(schema, record).state)), lifetime);
6429
+ },
6430
+ /**
6431
+ * Reads a complete bound Event-history page within repository admission.
6432
+ *
6433
+ * @param repository Registered repository.
6434
+ * @param lifetime Captured repository storage lifetime.
6435
+ * @param cache Bound Event-history cache.
6436
+ * @param depth Requested history depth.
6437
+ * @returns Frozen historical Events.
6438
+ */
6439
+ readBoundEvents(repository, lifetime, cache, depth) {
6440
+ return RepositoryStorage.track(repository, async () => this.freezeHistoryEvents(await cache.read(depth)), lifetime);
6441
+ },
6617
6442
  /**
6618
6443
  * Creates a history cache that serializes reads and extends prior results.
6619
6444
  *
@@ -7001,14 +6826,11 @@ const DispatchGuards = {
7001
6826
  */
7002
6827
  async wasDispatched(repository, runtime, event, entityId, journalEventId, depth) {
7003
6828
  const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForEvent(runtime.context, event));
7004
- const storage = RepositoryStorage.openEntityStorage(runtime.storageFactory, input);
7005
- try {
6829
+ return RepositoryStorage.track(repository, async () => {
6830
+ const storage = RepositoryStorage.repositoryReadEntityStorage(repository, runtime.storageFactory, input);
7006
6831
  const persisted = await storage.events.backward(entityId, depth);
7007
6832
  return persisted.some((candidate) => candidate.id?.value === journalEventId);
7008
- }
7009
- finally {
7010
- storage.close();
7011
- }
6833
+ });
7012
6834
  },
7013
6835
  /**
7014
6836
  * Records a successful delivery and discards the oldest excess identifiers.
@@ -7166,7 +6988,7 @@ const InboxMessages = {
7166
6988
  if (message.label !== "HANDLE_COMMAND") {
7167
6989
  throw new Error(`Entity Inbox replay does not handle "${message.label}" messages.`);
7168
6990
  }
7169
- const command = message.signal === undefined ? undefined : AnyMessages.unpack(message.signal, CommandSchema);
6991
+ const command = message.signal === undefined ? undefined : AnyPacker.tryUnpack(message.signal, CommandSchema);
7170
6992
  if (command === undefined) {
7171
6993
  throw CommandValidationError.invalidPayload();
7172
6994
  }
@@ -7209,7 +7031,7 @@ const InboxMessages = {
7209
7031
  typeUrl: message.signal.typeUrl,
7210
7032
  value: new Uint8Array(message.signal.value),
7211
7033
  });
7212
- const event = signal === undefined ? undefined : AnyMessages.unpack(signal, EventSchema);
7034
+ const event = signal === undefined ? undefined : AnyPacker.tryUnpack(signal, EventSchema);
7213
7035
  if (event === undefined) {
7214
7036
  throw new Error(unreadableMessage);
7215
7037
  }
@@ -7306,7 +7128,7 @@ const InboxReplay = {
7306
7128
  const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
7307
7129
  if (assignee === undefined)
7308
7130
  throw new Error("Agent Command has no selected assignee.");
7309
- await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityId, { kind: "command", value: command }, command.context?.actorContext ?? create(ActorContextSchema), [assignee.registeredHandler]);
7131
+ await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityId, { case: "command", value: command }, [assignee.registeredHandler]);
7310
7132
  },
7311
7133
  /**
7312
7134
  * Admits matching Event handlers from the stored target without rerouting.
@@ -7326,7 +7148,7 @@ const InboxReplay = {
7326
7148
  const handlers = InboxReplay.agentEventHandlers(routing, route.messageFullTypeName, event);
7327
7149
  if (handlers.length === 0)
7328
7150
  return;
7329
- await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityIds[0], { kind: "event", value: event }, runtime.signalMetadata.originFromEvent(event).actorContext ?? create(ActorContextSchema), handlers);
7151
+ await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityIds[0], { case: "event", value: event }, handlers);
7330
7152
  },
7331
7153
  /**
7332
7154
  * Restores selected Event and Command reactions from the original payload.
@@ -7355,64 +7177,154 @@ const InboxReplay = {
7355
7177
  * @param tenantId Selected delivery tenant.
7356
7178
  * @param entityId Typed target from the stored route.
7357
7179
  * @param signal Original source envelope.
7358
- * @param actor Original actor metadata.
7359
7180
  * @param handlers Selected handler metadata in execution order.
7360
7181
  * @returns When the fenced repository operation completes.
7361
7182
  */
7362
- async admitAgent(repository, runtime, message, tenantId, entityId, signal, actor, handlers) {
7363
- const revisions = InboxReplay.agentRevisions(repository, runtime, handlers);
7364
- const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
7365
- const execution = InboxReplay.agentExecutionHandle(repository, runtime, input);
7366
- await execution.admit(AgentAdmission.create({
7367
- stateType: repository.stateSchema.typeName,
7368
- agentKey: input.id.key(entityId),
7369
- recipientId: message.inboxId.targetId,
7183
+ async admitAgent(repository, runtime, message, tenantId, entityId, signal, handlers) {
7184
+ const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
7185
+ const input = RepositoryStorage.entityStorageInput(repository, context);
7186
+ const deliverySignal = AgentDeliveryScope.signal();
7187
+ if (deliverySignal === undefined)
7188
+ throw new Error("Agent admission requires delivery scope.");
7189
+ const cancellation = AbortSignal.any([deliverySignal, runtime.agentSignal]);
7190
+ const key = InboxReplay.capacityKeyFor(repository, input.context, input.id.key(entityId));
7191
+ const reservation = runtime.reserveAgent(key, cancellation);
7192
+ if (reservation === undefined) {
7193
+ runtime.deferAgent(message, tenantId);
7194
+ AgentDeliveryScope.record({ kind: "deferred" });
7195
+ return;
7196
+ }
7197
+ await InboxReplay.admitReservedAgent({
7198
+ repository,
7199
+ runtime,
7200
+ context: input.context,
7201
+ message,
7202
+ tenantId,
7203
+ entityId,
7370
7204
  signal,
7371
- actor,
7372
- inbox: message,
7373
7205
  handlers,
7374
- codeRevision: revisions.codeRevision,
7375
- schemaRevision: revisions.schema,
7376
- policyRevision: revisions.policy,
7377
- }));
7378
- runtime.wakeAcceptedAgent(repository, tenantId);
7206
+ reservation,
7207
+ });
7379
7208
  },
7380
7209
  /**
7381
- * Opens the actual tenant's mandatory execution handle.
7210
+ * Serializes the complete local Agent identity for one capacity slot.
7382
7211
  *
7383
- * @typeParam I Typed Agent identifier.
7384
- * @typeParam S Generated Agent state message.
7385
- * @param repository Registered Entity repository for this operation.
7386
- * @param runtime Bounded Context storage and publication services.
7387
- * @param input Original Inbox or storage input.
7388
- * @returns Fenced execution handle for the accepted tenant.
7212
+ * @param context Captured tenant storage context.
7213
+ * @param stateType Declared Agent state type.
7214
+ * @param id Canonical typed Agent identifier.
7215
+ * @returns Stable local capacity key for this Agent and tenant.
7389
7216
  */
7390
- agentExecutionHandle(repository, runtime, input) {
7391
- const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
7392
- if (storage.agentExecution === undefined)
7393
- throw new Error("Agent Inbox replay requires durable execution storage.");
7394
- return storage.agentExecution;
7217
+ agentCapacityKey(context, stateType, id) {
7218
+ return JSON.stringify([context.name, String(TenantBoundary.of(context).key), stateType, id]);
7395
7219
  },
7396
7220
  /**
7397
- * Records the registered code, schema, and AI policy before Inbox handoff.
7221
+ * Builds a capacity key for one repository entity in the selected tenant.
7398
7222
  *
7399
- * @param repository Registered Entity repository for this operation.
7400
- * @param runtime Bounded Context storage and publication services.
7401
- * @param handlers Generated handlers in their saved order.
7402
- * @returns Pinned code, schema, and AI policy digests.
7403
- */
7404
- agentRevisions(repository, runtime, handlers) {
7405
- const ai = runtime.ai;
7406
- const configuration = repositoryAccess.agentConfiguration(repository);
7407
- if (ai === undefined ||
7408
- configuration.ai === undefined ||
7409
- configuration.codeRevision === undefined)
7410
- throw new Error("Agent Inbox replay requires registered AI capabilities and revisions.");
7411
- return {
7412
- codeRevision: configuration.codeRevision,
7413
- ...AgentRevisions.atAdmission(repository.stateSchema, handlers, ai, configuration.ai),
7223
+ * @param repository Agent repository declaring the state type.
7224
+ * @param context Captured tenant storage context.
7225
+ * @param id Canonical typed Agent identifier.
7226
+ * @returns Stable local capacity key for this repository entity.
7227
+ */
7228
+ capacityKeyFor(repository, context, id) {
7229
+ return this.agentCapacityKey(context, repository.stateSchema.typeName, id);
7230
+ },
7231
+ /**
7232
+ * Completes receipt admission or returns a nonacknowledging provider error.
7233
+ * @param input Captured signal, runtime, and local capacity reservation.
7234
+ * @returns Completion of receipt admission or recorded provider failure.
7235
+ */
7236
+ async admitReservedAgent(input) {
7237
+ const { repository, runtime, context, message, signal, reservation } = input;
7238
+ try {
7239
+ const accepted = await InboxReplay.acceptAgentReceipt(runtime, context, repository, message, signal);
7240
+ if (!accepted) {
7241
+ reservation.release();
7242
+ AgentDeliveryScope.record({ kind: "duplicate" });
7243
+ return;
7244
+ }
7245
+ InboxReplay.recordAcceptedAgent(input);
7246
+ }
7247
+ catch (error) {
7248
+ reservation.release();
7249
+ AgentDeliveryScope.record({ kind: "error", error });
7250
+ }
7251
+ },
7252
+ /**
7253
+ * Records one accepted typed signal to its post-ack serialized live turn.
7254
+ * @param input Accepted signal and its retained capacity reservation.
7255
+ */
7256
+ recordAcceptedAgent(input) {
7257
+ const { repository, runtime, message, tenantId, entityId, signal, handlers, reservation } = input;
7258
+ const invocation = {
7259
+ stateType: repository.stateSchema.typeName,
7260
+ recipientId: clone(AnySchema, message.inboxId.targetId),
7261
+ source: signal,
7262
+ };
7263
+ AgentDeliveryScope.record({
7264
+ kind: "accepted",
7265
+ discard: () => {
7266
+ reservation.release();
7267
+ },
7268
+ run: (fence, checkSession) => {
7269
+ const cancellation = AbortSignal.any([fence, runtime.agentSignal]);
7270
+ const checkAgentSession = InboxReplay.agentSessionGuard(cancellation, checkSession);
7271
+ return reservation.run(() => withDeliveryCommitFence(checkAgentSession, () => new AgentLiveRunner(repository, runtime, tenantId, entityId, invocation, handlers, cancellation, checkAgentSession).run()));
7272
+ },
7273
+ });
7274
+ },
7275
+ /**
7276
+ * Verifies context cancellation on both sides of asynchronous shard validation.
7277
+ * @param cancellation Combined context and shard cancellation signal.
7278
+ * @param checkSession Validates the current shard session.
7279
+ * @returns A guard for live Agent effects and commits.
7280
+ */
7281
+ agentSessionGuard(cancellation, checkSession) {
7282
+ return async () => {
7283
+ cancellation.throwIfAborted();
7284
+ await checkSession();
7285
+ cancellation.throwIfAborted();
7414
7286
  };
7415
7287
  },
7288
+ /**
7289
+ * Writes one typed receipt after a local Agent capacity reservation.
7290
+ * @param context Captured tenant storage context.
7291
+ * @param message Original Inbox row with the typed recipient.
7292
+ * @param repository Agent repository declaring the state type.
7293
+ * @param runtime Bound repository runtime containing the receipt port.
7294
+ * @param signal Original Command or Event signal.
7295
+ * @returns Whether this exact Agent signal was newly accepted.
7296
+ */
7297
+ acceptAgentReceipt(runtime, context, repository, message, signal) {
7298
+ const source = signal.case === "command"
7299
+ ? { kind: "command", id: InboxReplay.requireAgentCommandId(signal.value) }
7300
+ : { kind: "event", id: InboxReplay.requireAgentEventId(signal.value) };
7301
+ return runtime.agentReceipts.accept(context, {
7302
+ stateType: repository.stateSchema.typeName,
7303
+ recipientId: message.inboxId.targetId,
7304
+ source,
7305
+ action: message.label,
7306
+ });
7307
+ },
7308
+ /**
7309
+ * Validates the original typed Command ID before writing an Agent receipt.
7310
+ * @param command Original Command addressed to the Agent.
7311
+ * @returns Original nonempty typed Command ID.
7312
+ */
7313
+ requireAgentCommandId(command) {
7314
+ if (command.id === undefined || command.id.uuid.trim().length === 0)
7315
+ throw new Error("Agent acceptance requires the original Command ID.");
7316
+ return command.id;
7317
+ },
7318
+ /**
7319
+ * Validates the original typed Event ID before writing an Agent receipt.
7320
+ * @param event Original Event addressed to the Agent.
7321
+ * @returns Original nonempty typed Event ID.
7322
+ */
7323
+ requireAgentEventId(event) {
7324
+ if (event.id === undefined || event.id.value.trim().length === 0)
7325
+ throw new Error("Agent acceptance requires the original Event ID.");
7326
+ return event.id;
7327
+ },
7416
7328
  /**
7417
7329
  * Dispatches a stored Process Manager Command after tenant and payload validation.
7418
7330
  *
@@ -7521,7 +7433,7 @@ const InboxReplay = {
7521
7433
  validateReplayedCommandPayload(routing, command) {
7522
7434
  const commandMessage = EntityInvocation.requireSignalMessage(command.message, "command");
7523
7435
  const commandSchema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, commandMessage.typeUrl, "command");
7524
- const payload = AnyMessages.unpack(commandMessage, commandSchema);
7436
+ const payload = AnyPacker.tryUnpack(commandMessage, commandSchema);
7525
7437
  if (payload === undefined) {
7526
7438
  throw CommandValidationError.invalidPayload();
7527
7439
  }
@@ -7549,7 +7461,7 @@ const InboxReplay = {
7549
7461
  validateReplayedEventPayload(routing, event, invalidPayloadMessage = "Projection inbox replay requires a readable event payload.") {
7550
7462
  const eventMessage = EntityInvocation.requireSignalMessage(event.message, "event");
7551
7463
  const eventSchema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, eventMessage.typeUrl, "event");
7552
- const payload = AnyMessages.unpack(eventMessage, eventSchema);
7464
+ const payload = AnyPacker.tryUnpack(eventMessage, eventSchema);
7553
7465
  if (payload === undefined) {
7554
7466
  throw new Error(invalidPayloadMessage);
7555
7467
  }
@@ -7730,18 +7642,14 @@ const InboxHandoff = {
7730
7642
  const commandId = RepositorySignals.requireCommandId(command);
7731
7643
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7732
7644
  const deliveryTenantId = RepositoryTenants.requireCommandTenant(runtime.context, command);
7733
- const delivery = new Delivery({
7734
- context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
7735
- storageFactory: runtime.storageFactory,
7736
- strategy: runtime.entityInbox.strategy(),
7737
- });
7645
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7738
7646
  await runtime.entityInbox.receive(delivery, {
7739
7647
  inboxId: {
7740
7648
  targetId: InboxMessages.inboxTargetId(route.entityId, repository.idField),
7741
7649
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
7742
7650
  },
7743
7651
  signalId: commandId.uuid,
7744
- signal: AnyMessages.pack(CommandSchema, command, { validate: false }),
7652
+ signal: AnyPacker.pack(CommandSchema, command),
7745
7653
  label: "HANDLE_COMMAND",
7746
7654
  status: "TO_DELIVER",
7747
7655
  keepUntil,
@@ -7760,17 +7668,14 @@ const InboxHandoff = {
7760
7668
  const eventId = RepositorySignals.requireEventId(event);
7761
7669
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7762
7670
  const deliveryTenantId = RepositoryTenants.requireProjectionTenant(runtime.context, event);
7763
- const delivery = new Delivery({
7764
- context: RepositoryTenants.projectionDeliveryContext(runtime.context, deliveryTenantId),
7765
- storageFactory: runtime.storageFactory,
7766
- });
7671
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7767
7672
  await runtime.projectionInbox.receive(delivery, {
7768
7673
  inboxId: {
7769
7674
  targetId: InboxMessages.inboxTargetId(entityId, repository.idField),
7770
7675
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
7771
7676
  },
7772
7677
  signalId: eventId.value,
7773
- signal: AnyMessages.pack(EventSchema, event, { validate: false }),
7678
+ signal: AnyPacker.pack(EventSchema, event),
7774
7679
  label: "UPDATE_SUBSCRIBER",
7775
7680
  status: "TO_DELIVER",
7776
7681
  shard: ShardIndex.single(),
@@ -7790,11 +7695,7 @@ const InboxHandoff = {
7790
7695
  const eventId = RepositorySignals.requireEventId(event);
7791
7696
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7792
7697
  const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
7793
- const delivery = new Delivery({
7794
- context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
7795
- storageFactory: runtime.storageFactory,
7796
- strategy: runtime.entityInbox.strategy(),
7797
- });
7698
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7798
7699
  await runtime.entityInbox.receive(delivery, InboxHandoff.pmEventInboxInput(repository, eventId.value, event, entityId, keepUntil), deliveryTenantId);
7799
7700
  },
7800
7701
  /**
@@ -7810,11 +7711,7 @@ const InboxHandoff = {
7810
7711
  const eventId = RepositorySignals.requireEventId(event);
7811
7712
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7812
7713
  const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
7813
- const delivery = new Delivery({
7814
- context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
7815
- storageFactory: runtime.storageFactory,
7816
- strategy: runtime.entityInbox.strategy(),
7817
- });
7714
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7818
7715
  for (let start = 0; start < entityIds.length; start += 1_000) {
7819
7716
  const inputs = entityIds
7820
7717
  .slice(start, start + 1_000)
@@ -7839,7 +7736,7 @@ const InboxHandoff = {
7839
7736
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
7840
7737
  },
7841
7738
  signalId,
7842
- signal: AnyMessages.pack(EventSchema, event, { validate: false }),
7739
+ signal: AnyPacker.pack(EventSchema, event),
7843
7740
  label: "REACT_UPON_EVENT",
7844
7741
  status: "TO_DELIVER",
7845
7742
  keepUntil,
@@ -7847,255 +7744,6 @@ const InboxHandoff = {
7847
7744
  },
7848
7745
  };
7849
7746
  Object.freeze(InboxHandoff);
7850
- /**
7851
- * Rebinds generated repository recipients without repeating accepted route queries.
7852
- */
7853
- const SavedRepositoryDispatch = {
7854
- /**
7855
- * Maps a generated repository family to its serialized binding value.
7856
- *
7857
- * @param repository Registered Entity repository for this operation.
7858
- * @returns Repository family encoded in a saved dispatch target.
7859
- */
7860
- family(repository) {
7861
- switch (repository.entityFamily) {
7862
- case "aggregate":
7863
- return AgentSavedRepositoryFamily.AGENT_SAVED_AGGREGATE;
7864
- case "projection":
7865
- return AgentSavedRepositoryFamily.AGENT_SAVED_PROJECTION;
7866
- case "process-manager":
7867
- return AgentSavedRepositoryFamily.AGENT_SAVED_PROCESS_MANAGER;
7868
- case "agent":
7869
- return AgentSavedRepositoryFamily.AGENT_SAVED_AGENT;
7870
- }
7871
- },
7872
- /**
7873
- * Finds the current generated handlers that determine an Event handoff.
7874
- *
7875
- * @param routing Registered generated handler routing metadata.
7876
- * @param event Original typed Event envelope.
7877
- * @returns Generated handlers matching the outgoing Event.
7878
- */
7879
- eventHandlers(routing, event) {
7880
- const packed = EntityInvocation.requireSignalMessage(event.message, "event");
7881
- const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
7882
- const value = EntityInvocation.unpackRequired(packed, schema, "event");
7883
- const external = event.context?.external === true;
7884
- return [
7885
- ...routing.eventReactors(schema.typeName, value, external),
7886
- ...routing.commandReactions(schema.typeName, value, external),
7887
- ...routing
7888
- .eventSubscribers(schema.typeName, value, external)
7889
- .map((entry) => entry.registeredHandler),
7890
- ];
7891
- },
7892
- /**
7893
- * Calculates a digest of ordered generated metadata and message descriptors.
7894
- *
7895
- * @param repository Registered Entity repository for this operation.
7896
- * @param handlers Generated handlers in their saved order.
7897
- * @returns Digest of ordered generated handler metadata.
7898
- */
7899
- fingerprint(repository, handlers) {
7900
- const selected = handlers.map(({ entity, handler }, ordinal) => ({
7901
- ordinal,
7902
- receiver: entity.schema.typeName,
7903
- kind: handler.kind,
7904
- method: handler.methodName,
7905
- signal: handler.messageFullTypeName,
7906
- origin: handler.origin,
7907
- parameters: handler.parameterCount,
7908
- where: handler.where === undefined ? null : [handler.where.eventField, handler.where.equals],
7909
- descriptor: Buffer.from(toBinary(FileDescriptorProtoSchema, handler.schema.file.proto)).toString("base64"),
7910
- }));
7911
- const state = Buffer.from(toBinary(FileDescriptorProtoSchema, repository.stateSchema.file.proto));
7912
- return createHash("sha256").update(state).update(JSON.stringify(selected)).digest("hex");
7913
- },
7914
- /**
7915
- * Restores a typed ID only when its canonical wire representation is unchanged.
7916
- *
7917
- * @param repository Registered Entity repository for this operation.
7918
- * @param packed Original typed Protobuf payload.
7919
- * @returns Typed recipient identifier from the frozen route.
7920
- */
7921
- recipient(repository, packed) {
7922
- const id = InboxMessages.targetEntityId(packed, repository.idField);
7923
- if (!equals(AnySchema, InboxMessages.inboxTargetId(id, repository.idField), packed))
7924
- throw new Error("Saved Agent output recipient ID is not canonical for its repository.");
7925
- return id;
7926
- },
7927
- /**
7928
- * Checks the stable repository and signal identity before saved transport.
7929
- *
7930
- * @param repository Registered Entity repository for this operation.
7931
- * @param target Frozen recipient binding.
7932
- * @param kind Generation or decision model kind.
7933
- * @returns Whether the checked condition holds.
7934
- */
7935
- matches(repository, target, kind) {
7936
- return (target.kind === kind &&
7937
- target.receiverStateType === repository.stateSchema.typeName &&
7938
- target.repositoryFamily === SavedRepositoryDispatch.family(repository));
7939
- },
7940
- /**
7941
- * Prepares one typed Command recipient with its selected generated assignee.
7942
- *
7943
- * @param repository Registered Entity repository for this operation.
7944
- * @param routing Registered generated handler routing metadata.
7945
- * @param command Original typed Command envelope.
7946
- * @returns Validated frozen Command recipient target.
7947
- */
7948
- async prepareCommand(repository, routing, command) {
7949
- const route = await repository.routeCommand(command);
7950
- const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
7951
- if (assignee === undefined)
7952
- throw new Error("Saved Agent Command has no generated assignee.");
7953
- return create(SavedTargetSchema, {
7954
- kind: AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND,
7955
- receiverStateType: repository.stateSchema.typeName,
7956
- repositoryFamily: SavedRepositoryDispatch.family(repository),
7957
- signalType: route.messageFullTypeName,
7958
- bindingFingerprint: SavedRepositoryDispatch.fingerprint(repository, [
7959
- assignee.registeredHandler,
7960
- ]),
7961
- recipients: [InboxMessages.inboxTargetId(route.entityId, repository.idField)],
7962
- });
7963
- },
7964
- /**
7965
- * Resolves the original Command recipient without calling its route again.
7966
- *
7967
- * @param repository Registered Entity repository for this operation.
7968
- * @param routing Registered generated handler routing metadata.
7969
- * @param command Original typed Command envelope.
7970
- * @param target Frozen recipient binding.
7971
- * @returns When the fenced repository operation completes.
7972
- */
7973
- bindCommand(repository, routing, command, target) {
7974
- const { runtime, signalType, recipient } = SavedRepositoryDispatch.requireCommandBinding(repository, routing, command, target);
7975
- const entityId = SavedRepositoryDispatch.recipient(repository, recipient);
7976
- const route = {
7977
- entityId,
7978
- messageFullTypeName: signalType,
7979
- invocation: "deferred",
7980
- };
7981
- return Promise.resolve(() => InboxHandoff.handoffEntityCommand(repository, runtime, command, route));
7982
- },
7983
- /**
7984
- * Checks saved Command metadata, typed target presence, and runtime registration.
7985
- *
7986
- * @param repository Registered Entity repository for this operation.
7987
- * @param routing Registered generated handler routing metadata.
7988
- * @param command Original typed Command envelope.
7989
- * @param target Frozen recipient binding.
7990
- * @returns Registered Command route and original recipient.
7991
- */
7992
- requireCommandBinding(repository, routing, command, target) {
7993
- const packed = EntityInvocation.requireSignalMessage(command.message, "command");
7994
- const schema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, packed.typeUrl, "command");
7995
- const assignee = routing.commandReadiness?.findCommandAssignee(schema.typeName);
7996
- const fingerprint = assignee === undefined
7997
- ? ""
7998
- : SavedRepositoryDispatch.fingerprint(repository, [assignee.registeredHandler]);
7999
- const recipient = target.recipients[0];
8000
- const runtime = repositoryRuntimes.get(repository);
8001
- if (!SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND) ||
8002
- target.signalType !== schema.typeName ||
8003
- target.bindingFingerprint !== fingerprint ||
8004
- target.recipients.length !== 1 ||
8005
- recipient === undefined ||
8006
- runtime === undefined)
8007
- throw new Error("Saved Agent Command repository binding changed before delivery.");
8008
- return { runtime, signalType: schema.typeName, recipient };
8009
- },
8010
- /**
8011
- * Prepares the exact ordered Event recipients and selected generated handlers.
8012
- *
8013
- * @param repository Registered Entity repository for this operation.
8014
- * @param routing Registered generated handler routing metadata.
8015
- * @param event Original typed Event envelope.
8016
- * @returns Validated frozen Event recipient target.
8017
- */
8018
- async prepareEvent(repository, routing, event) {
8019
- const route = await repository.routeEvent(event);
8020
- return create(SavedTargetSchema, {
8021
- kind: AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_EVENT,
8022
- receiverStateType: repository.stateSchema.typeName,
8023
- repositoryFamily: SavedRepositoryDispatch.family(repository),
8024
- signalType: route.messageFullTypeName,
8025
- bindingFingerprint: SavedRepositoryDispatch.fingerprint(repository, SavedRepositoryDispatch.eventHandlers(routing, event)),
8026
- recipients: route.entityIds.map((id) => InboxMessages.inboxTargetId(id, repository.idField)),
8027
- });
8028
- },
8029
- /**
8030
- * Restores the saved Event route and current generated handler metadata.
8031
- *
8032
- * @param repository Registered Entity repository for this operation.
8033
- * @param routing Registered generated handler routing metadata.
8034
- * @param event Original typed Event envelope.
8035
- * @param target Frozen recipient binding.
8036
- * @returns When the fenced repository operation completes.
8037
- */
8038
- bindEvent(repository, routing, event, target) {
8039
- const { signalType } = SavedRepositoryDispatch.requireEventBinding(repository, routing, event, target);
8040
- const entityIds = target.recipients.map((id) => SavedRepositoryDispatch.recipient(repository, id));
8041
- const route = {
8042
- entityIds,
8043
- messageFullTypeName: signalType,
8044
- invocation: "deferred",
8045
- };
8046
- return Promise.resolve(() => RepositoryDispatch.dispatchRepositoryEvent(repository, routing, event, route));
8047
- },
8048
- /**
8049
- * Checks saved Event metadata and active repository registration.
8050
- *
8051
- * @param repository Registered Entity repository for this operation.
8052
- * @param routing Registered generated handler routing metadata.
8053
- * @param event Original typed Event envelope.
8054
- * @param target Frozen recipient binding.
8055
- * @returns Registered Event route for the frozen target.
8056
- */
8057
- requireEventBinding(repository, routing, event, target) {
8058
- const packed = EntityInvocation.requireSignalMessage(event.message, "event");
8059
- const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
8060
- const fingerprint = SavedRepositoryDispatch.fingerprint(repository, SavedRepositoryDispatch.eventHandlers(routing, event));
8061
- const runtime = repositoryRuntimes.get(repository);
8062
- if (!SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_EVENT) ||
8063
- target.signalType !== schema.typeName ||
8064
- target.bindingFingerprint !== fingerprint ||
8065
- runtime === undefined)
8066
- throw new Error("Saved Agent Event repository binding changed before delivery.");
8067
- return { runtime, signalType: schema.typeName };
8068
- },
8069
- /**
8070
- * Creates one internal Command binding from the repository registration.
8071
- *
8072
- * @param repository Registered Entity repository for this operation.
8073
- * @param routing Registered generated handler routing metadata.
8074
- * @returns Saved Command binding for this registration.
8075
- */
8076
- commandBinding(repository, routing) {
8077
- return {
8078
- prepare: (command) => SavedRepositoryDispatch.prepareCommand(repository, routing, command),
8079
- matches: (target) => SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND),
8080
- bind: (command, target) => SavedRepositoryDispatch.bindCommand(repository, routing, command, target),
8081
- };
8082
- },
8083
- /**
8084
- * Creates one internal Event binding from the repository registration.
8085
- *
8086
- * @param repository Registered Entity repository for this operation.
8087
- * @param routing Registered generated handler routing metadata.
8088
- * @returns Saved Event binding for this registration.
8089
- */
8090
- eventBinding(repository, routing) {
8091
- return {
8092
- prepare: (event) => SavedRepositoryDispatch.prepareEvent(repository, routing, event),
8093
- matches: (target) => SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_EVENT),
8094
- bind: (event, target) => SavedRepositoryDispatch.bindEvent(repository, routing, event, target),
8095
- };
8096
- },
8097
- };
8098
- Object.freeze(SavedRepositoryDispatch);
8099
7747
  /**
8100
7748
  * Connects bus dispatchers and Inbox replay to repository execution.
8101
7749
  */
@@ -8130,7 +7778,7 @@ const RepositoryDispatch = {
8130
7778
  messageSchemas: () => routing.commandSchemas,
8131
7779
  dispatch: (command) => RepositoryDispatch.dispatchRepositoryCommand(repository, routing, command),
8132
7780
  });
8133
- return SavedDispatcherBindings.command(dispatcher, SavedRepositoryDispatch.commandBinding(repository, routing));
7781
+ return dispatcher;
8134
7782
  },
8135
7783
  /**
8136
7784
  * Creates an Event dispatcher that preserves routes computed during acceptance.
@@ -8156,7 +7804,7 @@ const RepositoryDispatch = {
8156
7804
  },
8157
7805
  });
8158
7806
  const originAware = EventDispatcherOriginSchemas.define(dispatcher, routing.domesticEventSchemas, routing.externalEventSchemas);
8159
- return SavedDispatcherBindings.event(originAware, SavedRepositoryDispatch.eventBinding(repository, routing));
7807
+ return originAware;
8160
7808
  },
8161
7809
  /**
8162
7810
  * Creates a system-Event dispatcher for registered Entity-state subscriptions.