@spine-event-engine/server 2.0.0-snapshot.24 → 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 (286) hide show
  1. package/README.md +38 -11
  2. package/REFERENCE.md +201 -130
  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/{bus/saved-dispatcher-binding.js → agent/agent-live-inputs.js} +2 -14
  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 +17 -4
  68. package/dist/bus/event-bus.d.ts.map +1 -1
  69. package/dist/bus/event-bus.js +42 -12
  70. package/dist/bus/event-bus.js.map +1 -1
  71. package/dist/context/bounded-context.d.ts +140 -87
  72. package/dist/context/bounded-context.d.ts.map +1 -1
  73. package/dist/context/bounded-context.js +702 -329
  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 +22 -13
  78. package/dist/context/entity-inbox.js.map +1 -1
  79. package/dist/context/local-inbox-handoff.d.ts +34 -34
  80. package/dist/context/local-inbox-handoff.d.ts.map +1 -1
  81. package/dist/context/local-inbox-handoff.js +108 -150
  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 +10 -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 +1 -1
  104. package/dist/delivery/delivery-monitor.js +1 -1
  105. package/dist/delivery/delivery-run-control.d.ts +6 -0
  106. package/dist/delivery/delivery-run-control.d.ts.map +1 -1
  107. package/dist/delivery/delivery-run-control.js +10 -0
  108. package/dist/delivery/delivery-run-control.js.map +1 -1
  109. package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
  110. package/dist/delivery/delivery-supervisor.js +28 -4
  111. package/dist/delivery/delivery-supervisor.js.map +1 -1
  112. package/dist/delivery/delivery-tenant.d.ts +17 -0
  113. package/dist/delivery/delivery-tenant.d.ts.map +1 -0
  114. package/dist/delivery/delivery-tenant.js +50 -0
  115. package/dist/delivery/delivery-tenant.js.map +1 -0
  116. package/dist/delivery/delivery.d.ts +96 -2
  117. package/dist/delivery/delivery.d.ts.map +1 -1
  118. package/dist/delivery/delivery.js +693 -53
  119. package/dist/delivery/delivery.js.map +1 -1
  120. package/dist/delivery/inbox-records.d.ts.map +1 -1
  121. package/dist/delivery/inbox-records.js +9 -16
  122. package/dist/delivery/inbox-records.js.map +1 -1
  123. package/dist/delivery/inbox-storage.d.ts +6 -0
  124. package/dist/delivery/inbox-storage.d.ts.map +1 -1
  125. package/dist/delivery/inbox-storage.js +43 -6
  126. package/dist/delivery/inbox-storage.js.map +1 -1
  127. package/dist/delivery/inbox.d.ts.map +1 -1
  128. package/dist/delivery/inbox.js +3 -2
  129. package/dist/delivery/inbox.js.map +1 -1
  130. package/dist/delivery/reception-observation.d.ts +12 -4
  131. package/dist/delivery/reception-observation.d.ts.map +1 -1
  132. package/dist/delivery/reception-observation.js +14 -3
  133. package/dist/delivery/reception-observation.js.map +1 -1
  134. package/dist/delivery/sharded-work-registry.d.ts +6 -0
  135. package/dist/delivery/sharded-work-registry.d.ts.map +1 -1
  136. package/dist/delivery/sharded-work-registry.js +41 -4
  137. package/dist/delivery/sharded-work-registry.js.map +1 -1
  138. package/dist/entity/entity-storage-descriptor.d.ts.map +1 -1
  139. package/dist/entity/entity-storage-descriptor.js +7 -4
  140. package/dist/entity/entity-storage-descriptor.js.map +1 -1
  141. package/dist/index.d.ts +4 -2
  142. package/dist/index.d.ts.map +1 -1
  143. package/dist/index.js +2 -1
  144. package/dist/index.js.map +1 -1
  145. package/dist/integration/external-messages.d.ts.map +1 -1
  146. package/dist/integration/external-messages.js +9 -15
  147. package/dist/integration/external-messages.js.map +1 -1
  148. package/dist/integration/integration-broker.d.ts.map +1 -1
  149. package/dist/integration/integration-broker.js +112 -6
  150. package/dist/integration/integration-broker.js.map +1 -1
  151. package/dist/integration/third-party-context.d.ts.map +1 -1
  152. package/dist/integration/third-party-context.js +7 -10
  153. package/dist/integration/third-party-context.js.map +1 -1
  154. package/dist/repository/primitive-id.js +26 -11
  155. package/dist/repository/primitive-id.js.map +1 -1
  156. package/dist/repository/repository.d.ts +32 -60
  157. package/dist/repository/repository.d.ts.map +1 -1
  158. package/dist/repository/repository.js +1446 -1787
  159. package/dist/repository/repository.js.map +1 -1
  160. package/dist/runtime/runtime.d.ts +13 -0
  161. package/dist/runtime/runtime.d.ts.map +1 -1
  162. package/dist/runtime/runtime.js +18 -0
  163. package/dist/runtime/runtime.js.map +1 -1
  164. package/dist/runtime/signal-metadata.d.ts.map +1 -1
  165. package/dist/runtime/signal-metadata.js +6 -6
  166. package/dist/runtime/signal-metadata.js.map +1 -1
  167. package/dist/runtime/signal-publisher.d.ts +0 -6
  168. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  169. package/dist/runtime/signal-publisher.js +1 -9
  170. package/dist/runtime/signal-publisher.js.map +1 -1
  171. package/dist/runtime/standalone-handler-runtime.d.ts +0 -7
  172. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  173. package/dist/runtime/standalone-handler-runtime.js +13 -99
  174. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  175. package/dist/server/managed-delivery-gate.d.ts +34 -0
  176. package/dist/server/managed-delivery-gate.d.ts.map +1 -0
  177. package/dist/server/managed-delivery-gate.js +70 -0
  178. package/dist/server/managed-delivery-gate.js.map +1 -0
  179. package/dist/server/managed-server-application.d.ts.map +1 -1
  180. package/dist/server/managed-server-application.js +7 -4
  181. package/dist/server/managed-server-application.js.map +1 -1
  182. package/dist/server/server-environment.d.ts +203 -25
  183. package/dist/server/server-environment.d.ts.map +1 -1
  184. package/dist/server/server-environment.js +661 -133
  185. package/dist/server/server-environment.js.map +1 -1
  186. package/dist/server/server-log.d.ts +7 -0
  187. package/dist/server/server-log.d.ts.map +1 -1
  188. package/dist/server/server-log.js +14 -1
  189. package/dist/server/server-log.js.map +1 -1
  190. package/dist/server/server.d.ts +18 -32
  191. package/dist/server/server.d.ts.map +1 -1
  192. package/dist/server/server.js +50 -204
  193. package/dist/server/server.js.map +1 -1
  194. package/dist/services/spine-services.d.ts +1 -1
  195. package/dist/services/spine-services.d.ts.map +1 -1
  196. package/dist/services/spine-services.js +236 -335
  197. package/dist/services/spine-services.js.map +1 -1
  198. package/dist/stand/event-origin-filter.js +3 -3
  199. package/dist/stand/event-origin-filter.js.map +1 -1
  200. package/dist/stand/stand.d.ts +14 -0
  201. package/dist/stand/stand.d.ts.map +1 -1
  202. package/dist/stand/stand.js +53 -9
  203. package/dist/stand/stand.js.map +1 -1
  204. package/dist/stand/subscription-observer.d.ts.map +1 -1
  205. package/dist/stand/subscription-observer.js +54 -40
  206. package/dist/stand/subscription-observer.js.map +1 -1
  207. package/dist/stand/subscription-registry.d.ts +6 -2
  208. package/dist/stand/subscription-registry.d.ts.map +1 -1
  209. package/dist/stand/subscription-registry.js +15 -11
  210. package/dist/stand/subscription-registry.js.map +1 -1
  211. package/dist/stand/subscription-runtime.d.ts +5 -3
  212. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  213. package/dist/stand/subscription-runtime.js +52 -9
  214. package/dist/stand/subscription-runtime.js.map +1 -1
  215. package/dist/testing/index.d.ts.map +1 -1
  216. package/dist/testing/index.js +4 -3
  217. package/dist/testing/index.js.map +1 -1
  218. package/dist/tsconfig.tsbuildinfo +1 -1
  219. package/package.json +14 -14
  220. package/dist/agent/agent-admission.d.ts +0 -80
  221. package/dist/agent/agent-admission.d.ts.map +0 -1
  222. package/dist/agent/agent-admission.js +0 -127
  223. package/dist/agent/agent-admission.js.map +0 -1
  224. package/dist/agent/agent-ai-runtime.d.ts +0 -76
  225. package/dist/agent/agent-ai-runtime.d.ts.map +0 -1
  226. package/dist/agent/agent-ai-runtime.js +0 -1269
  227. package/dist/agent/agent-ai-runtime.js.map +0 -1
  228. package/dist/agent/agent-execution-fault.d.ts +0 -18
  229. package/dist/agent/agent-execution-fault.d.ts.map +0 -1
  230. package/dist/agent/agent-execution-fault.js.map +0 -1
  231. package/dist/agent/agent-execution-session.d.ts +0 -103
  232. package/dist/agent/agent-execution-session.d.ts.map +0 -1
  233. package/dist/agent/agent-execution-session.js +0 -238
  234. package/dist/agent/agent-execution-session.js.map +0 -1
  235. package/dist/agent/agent-interaction-audit.d.ts +0 -58
  236. package/dist/agent/agent-interaction-audit.d.ts.map +0 -1
  237. package/dist/agent/agent-interaction-audit.js +0 -114
  238. package/dist/agent/agent-interaction-audit.js.map +0 -1
  239. package/dist/agent/agent-interaction-events.d.ts +0 -40
  240. package/dist/agent/agent-interaction-events.d.ts.map +0 -1
  241. package/dist/agent/agent-interaction-events.js +0 -129
  242. package/dist/agent/agent-interaction-events.js.map +0 -1
  243. package/dist/agent/agent-mcp-budget.d.ts +0 -125
  244. package/dist/agent/agent-mcp-budget.d.ts.map +0 -1
  245. package/dist/agent/agent-mcp-budget.js +0 -566
  246. package/dist/agent/agent-mcp-budget.js.map +0 -1
  247. package/dist/agent/agent-mcp-host.d.ts +0 -125
  248. package/dist/agent/agent-mcp-host.d.ts.map +0 -1
  249. package/dist/agent/agent-mcp-host.js.map +0 -1
  250. package/dist/agent/agent-mcp-runtime.d.ts.map +0 -1
  251. package/dist/agent/agent-mcp-runtime.js.map +0 -1
  252. package/dist/agent/agent-read-runtime.d.ts +0 -70
  253. package/dist/agent/agent-read-runtime.d.ts.map +0 -1
  254. package/dist/agent/agent-read-runtime.js +0 -320
  255. package/dist/agent/agent-read-runtime.js.map +0 -1
  256. package/dist/agent/agent-revisions.d.ts +0 -35
  257. package/dist/agent/agent-revisions.d.ts.map +0 -1
  258. package/dist/agent/agent-revisions.js +0 -133
  259. package/dist/agent/agent-revisions.js.map +0 -1
  260. package/dist/agent/agent-scheduler.d.ts +0 -99
  261. package/dist/agent/agent-scheduler.d.ts.map +0 -1
  262. package/dist/agent/agent-scheduler.js +0 -398
  263. package/dist/agent/agent-scheduler.js.map +0 -1
  264. package/dist/bus/saved-dispatcher-binding.d.ts +0 -53
  265. package/dist/bus/saved-dispatcher-binding.d.ts.map +0 -1
  266. package/dist/bus/saved-dispatcher-binding.js.map +0 -1
  267. package/dist/delivery/delivery-run-coordinator.d.ts +0 -218
  268. package/dist/delivery/delivery-run-coordinator.d.ts.map +0 -1
  269. package/dist/delivery/delivery-run-coordinator.js +0 -609
  270. package/dist/delivery/delivery-run-coordinator.js.map +0 -1
  271. package/dist/delivery/parked-delivery-obligations.d.ts +0 -182
  272. package/dist/delivery/parked-delivery-obligations.d.ts.map +0 -1
  273. package/dist/delivery/parked-delivery-obligations.js +0 -390
  274. package/dist/delivery/parked-delivery-obligations.js.map +0 -1
  275. package/dist/server/environment-attachment.d.ts +0 -356
  276. package/dist/server/environment-attachment.d.ts.map +0 -1
  277. package/dist/server/environment-attachment.js +0 -2062
  278. package/dist/server/environment-attachment.js.map +0 -1
  279. package/dist/server/environment-delivery-records.d.ts +0 -77
  280. package/dist/server/environment-delivery-records.d.ts.map +0 -1
  281. package/dist/server/environment-delivery-records.js +0 -282
  282. package/dist/server/environment-delivery-records.js.map +0 -1
  283. package/dist/server/environment-delivery-worker.d.ts +0 -161
  284. package/dist/server/environment-delivery-worker.d.ts.map +0 -1
  285. package/dist/server/environment-delivery-worker.js +0 -973
  286. 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
21
  import { EventStore, StorageQueryPolicy, } from "@spine-event-engine/storage";
23
- import { TenantBoundary } from "@spine-event-engine/storage/provider";
22
+ import { TenantBoundary, eventStoreAccess, StoragePreparations, } from "@spine-event-engine/storage/provider";
24
23
  import { EntityCommitStorageFactories } from "@spine-event-engine/storage/provider";
25
24
  import { AgentHistoryStorageFactories, } from "@spine-event-engine/storage/provider";
26
- import { AgentExecutionStorageFactories, } from "@spine-event-engine/storage/provider";
27
- import { AgentHistoryEntrySchema, AgentInvocationTerminatedSchema, AgentModelSelectionChangedSchema as SelectionChangedSchema, AiModelKind, AiDiagnosticIdSchema, ModelRefSchema, ToolEffect, ConversationIdSchema, } from "@spine-event-engine/proto/agent";
28
- import { AgentInvocationKeySchema, AgentExecutionStartSchema, AgentExecutionCompletionSchema, AgentInvocationBoundsSchema, AgentInvocationStatus, AgentOutgoingCommandSchema, AgentSignalKeySchema, AgentSavedDispatchTargetSchema as SavedTargetSchema, AgentSavedRepositoryFamily, AgentSavedTargetKind, } from "@spine-event-engine/proto/generated/spine/server/agent/execution_record_pb.js";
25
+ import { AgentHistoryEntrySchema, AgentInvocationTerminatedSchema, AgentModelSelectionChangedSchema as SelectionChangedSchema, AiModelKind, ModelRefSchema, ModelPreferenceSchema, 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,20 +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
36
  import { receptionObservation } from "../delivery/reception-observation.js";
42
- import { commitFenced } from "./commit-fence.js";
37
+ import { commitFenced, withDeliveryCommitFence } from "./commit-fence.js";
43
38
  import { AgentHistoryReads } from "../agent/agent-history.js";
44
- import { AgentReadRuntime } from "../agent/agent-read-runtime.js";
45
- import { AgentAdmission } from "../agent/agent-admission.js";
46
- import { AgentRevisions } from "../agent/agent-revisions.js";
47
- import { AgentExecutionSession } from "../agent/agent-execution-session.js";
48
- import { AgentExecutionFault } from "../agent/agent-execution-fault.js";
39
+ import { 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";
49
46
  import { AgentModelSelection } from "../agent/agent-model-selection.js";
50
- import { AgentAiRuntime, AgentUncertainAttemptError } from "../agent/agent-ai-runtime.js";
51
- import { AgentInteractionEvents } from "../agent/agent-interaction-events.js";
52
- import { AgentInteractionAudit } from "../agent/agent-interaction-audit.js";
53
47
  import { AgentAiBindings } from "../agent/agent-ai-binding.js";
54
48
  import { InboxTargets } from "../delivery/inbox.js";
55
49
  import { ShardIndex } from "../delivery/shard-index.js";
@@ -150,12 +144,13 @@ export class Repository {
150
144
  return this.#metadata.schema;
151
145
  }
152
146
  /**
153
- * 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.
154
149
  *
155
150
  * This JVM-parity switch is for controlled administration/testing, not
156
151
  * routine request-time behavior. It never deletes or reconstructs history.
157
152
  *
158
- * @param enabled Whether later successful stores append state-history rows.
153
+ * @param enabled Whether newly admitted stores append state-history rows.
159
154
  */
160
155
  setStateHistoryEnabled(enabled) {
161
156
  if (typeof enabled !== "boolean") {
@@ -165,12 +160,6 @@ export class Repository {
165
160
  if (configuration.stateHistory === enabled)
166
161
  return;
167
162
  configuration.stateHistory = enabled;
168
- const handles = repositoryEntityHandles.get(this);
169
- if (handles !== undefined) {
170
- for (const handle of handles.values())
171
- handle.close();
172
- handles.clear();
173
- }
174
163
  }
175
164
  /**
176
165
  * Returns descriptor metadata for this repository's Entity state.
@@ -216,19 +205,6 @@ export class Repository {
216
205
  agentHistory(id, scope) {
217
206
  return RepositoryAgentReads.history(this, id, scope);
218
207
  }
219
- /**
220
- * Reads the exact recorded phase for one accepted Command or Event.
221
- * The trusted application must authorize the caller before using this read.
222
- *
223
- * @param this Registered Agent repository.
224
- * @param id Typed Agent identifier.
225
- * @param source Original Command or Event ID.
226
- * @param scope Explicit tenant boundary; pass `{}` in a single-tenant Bounded Context.
227
- * @returns Recorded phase, or `undefined` when no invocation was recorded.
228
- */
229
- agentExecution(id, source, scope) {
230
- return RepositoryAgentReads.execution(this, id, source, scope);
231
- }
232
208
  /**
233
209
  * Routes a command to one entity ID without invoking a handler.
234
210
  *
@@ -339,13 +315,10 @@ const RepositoryAgentReads = {
339
315
  async historyPage(repository, captured, view, request) {
340
316
  if (repositoryRuntimes.get(repository) !== captured.runtime)
341
317
  throw new Error("Agent read requires an active repository.");
342
- const input = RepositoryStorage.entityStorageInput(repository, captured.context);
343
- const history = AgentHistoryStorageFactories.create(captured.runtime.storageFactory, {
344
- context: captured.context,
345
- stateType: repository.stateSchema.typeName,
346
- id: { key: input.id.key },
347
- });
348
- try {
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);
349
322
  const page = await AgentHistoryReads.readBound({
350
323
  storage: history,
351
324
  entityId: captured.id,
@@ -355,10 +328,7 @@ const RepositoryAgentReads = {
355
328
  items: page.items,
356
329
  ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
357
330
  };
358
- }
359
- finally {
360
- history.close();
361
- }
331
+ });
362
332
  },
363
333
  /**
364
334
  * Returns one typed category from the full Proto history oneof.
@@ -379,76 +349,6 @@ const RepositoryAgentReads = {
379
349
  ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
380
350
  };
381
351
  },
382
- /**
383
- * Reads the exact saved invocation and exposes only its phase.
384
- *
385
- * @param repository Registered Agent repository.
386
- * @param id Typed Agent identifier.
387
- * @param source Original Command or Event ID.
388
- * @param scope Explicit tenant selection.
389
- * @returns Current phase, or `undefined` for no saved invocation.
390
- */
391
- async execution(repository, id, source, scope) {
392
- const captured = this.capture(repository, id, scope);
393
- const sourceSignal = this.source(source);
394
- const input = RepositoryStorage.entityStorageInput(repository, captured.context);
395
- const key = create(AgentInvocationKeySchema, {
396
- scope: { stateType: repository.stateSchema.typeName, agentKey: input.id.key(captured.id) },
397
- sourceSignal,
398
- });
399
- if (repositoryRuntimes.get(repository) !== captured.runtime)
400
- throw new Error("Agent read requires an active repository.");
401
- const storage = AgentExecutionStorageFactories.create(captured.runtime.storageFactory, {
402
- entity: input,
403
- stateType: repository.stateSchema.typeName,
404
- });
405
- try {
406
- const record = await storage.read(key);
407
- return record === undefined ? undefined : this.status(record.status);
408
- }
409
- finally {
410
- storage.close();
411
- }
412
- },
413
- /**
414
- * Copies an original signal identifier into the private execution key.
415
- *
416
- * @param source Original Command or Event ID.
417
- * @returns Typed private source key.
418
- */
419
- source(source) {
420
- if (source.$typeName === CommandIdSchema.typeName && source.uuid.length > 0)
421
- return create(AgentSignalKeySchema, {
422
- id: { case: "command", value: clone(CommandIdSchema, source) },
423
- });
424
- if (source.$typeName === EventIdSchema.typeName && source.value.length > 0)
425
- return create(AgentSignalKeySchema, {
426
- id: { case: "event", value: clone(EventIdSchema, source) },
427
- });
428
- throw new TypeError("Agent execution requires an original CommandId or EventId.");
429
- },
430
- /**
431
- * Returns the public status for a private execution phase.
432
- *
433
- * @param status Saved private phase.
434
- * @returns Public phase string.
435
- */
436
- status(status) {
437
- switch (status) {
438
- case AgentInvocationStatus.AGENT_INVOCATION_ACCEPTED:
439
- return "accepted";
440
- case AgentInvocationStatus.AGENT_INVOCATION_ACTIVE:
441
- return "active";
442
- case AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_COMMANDS:
443
- return "completed-pending-commands";
444
- case AgentInvocationStatus.AGENT_INVOCATION_COMPLETED:
445
- return "completed";
446
- case AgentInvocationStatus.AGENT_INVOCATION_TERMINATED:
447
- return "terminated";
448
- default:
449
- throw new Error("Agent execution provider returned an unsupported phase.");
450
- }
451
- },
452
352
  };
453
353
  const repositorySnapshots = new WeakMap();
454
354
  const repositoryCreators = new WeakMap();
@@ -459,11 +359,10 @@ const repositoryEntityInboxTargets = new WeakMap();
459
359
  const repositoryProjectionInboxTargets = new WeakMap();
460
360
  const repositoryProjectionDirect = new WeakMap();
461
361
  const repositoryRuntimes = new WeakMap();
462
- const repositoryEntityHandles = new WeakMap();
362
+ const repositoryStorageLifetimes = new WeakMap();
463
363
  const entityStateHistoryCaches = new WeakMap();
464
364
  const repositoryHistoryConfigurations = new WeakMap();
465
365
  const repositoryAiConfigurations = new WeakMap();
466
- const repositoryAgentCodeRevisions = new WeakMap();
467
366
  const repositoryDispatchGuards = new WeakMap();
468
367
  /**
469
368
  * Installs repository routing, history, and callback state after identity validation.
@@ -493,7 +392,7 @@ const RepositoryRegistration = {
493
392
  RepositoryRegistration.installTargets(repository, routing);
494
393
  },
495
394
  /**
496
- * Captures Agent capability and code revisions during repository registration.
395
+ * Captures Agent capability policy during repository registration.
497
396
  *
498
397
  * @typeParam EntityType Concrete generated Entity constructor.
499
398
  * @param repository Registered Entity repository for this operation.
@@ -505,8 +404,6 @@ const RepositoryRegistration = {
505
404
  return;
506
405
  if (options.ai !== undefined)
507
406
  repositoryAiConfigurations.set(repository, options.ai);
508
- if (options.agentCodeRevision !== undefined)
509
- repositoryAgentCodeRevisions.set(repository, options.agentCodeRevision);
510
407
  },
511
408
  /**
512
409
  * Registers durable Entity and direct Projection delivery targets.
@@ -555,6 +452,24 @@ export const repositoryAccess = Object.freeze({
555
452
  }
556
453
  return RepositoryIdentity.cloneRepositorySnapshot(snapshot);
557
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
+ },
558
473
  /**
559
474
  * Reads schemas for Events this repository can emit.
560
475
  *
@@ -568,32 +483,9 @@ export const repositoryAccess = Object.freeze({
568
483
  }
569
484
  return schemas;
570
485
  },
571
- producedCommandSchemas(repository) {
572
- const routing = repositoryRoutings.get(repository);
573
- if (routing === undefined)
574
- throw new TypeError("Produced Command schemas require a Repository instance.");
575
- return routing.producedCommandSchemas;
576
- },
577
486
  agentConfiguration(repository) {
578
487
  const ai = repositoryAiConfigurations.get(repository);
579
- const codeRevision = repositoryAgentCodeRevisions.get(repository);
580
- return {
581
- ...(ai === undefined ? {} : { ai }),
582
- ...(codeRevision === undefined ? {} : { codeRevision }),
583
- };
584
- },
585
- runAcceptedAgent(repository, tenantId, key, signal) {
586
- return AgentExecutionRunner.run(repository, tenantId, key, signal);
587
- },
588
- pendingAcceptedAgents(repository, tenantId, after, count) {
589
- const runtime = repositoryRuntimes.get(repository);
590
- if (runtime === undefined || repository.entityFamily !== "agent")
591
- throw new Error("Agent pending discovery requires a registered Agent repository.");
592
- const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
593
- const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
594
- if (storage.agentExecution === undefined)
595
- throw new Error("Agent pending discovery requires execution storage.");
596
- return storage.agentExecution.pending({ count, ...(after === undefined ? {} : { after }) });
488
+ return ai === undefined ? {} : { ai };
597
489
  },
598
490
  /**
599
491
  * Opens a scoped provider read without restoring an application Entity.
@@ -612,18 +504,13 @@ export const repositoryAccess = Object.freeze({
612
504
  if (runtime === undefined)
613
505
  throw new Error("Agent history requires an active repository.");
614
506
  const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
507
+ await runtime.onAdmit(context.tenantId);
615
508
  const input = RepositoryStorage.entityStorageInput(repository, context);
616
- const history = AgentHistoryStorageFactories.create(runtime.storageFactory, {
617
- context,
618
- stateType: repository.stateSchema.typeName,
619
- id: { key: input.id.key },
620
- });
621
- try {
622
- return await history.read({ entityId, view, ...read });
623
- }
624
- finally {
625
- history.close();
626
- }
509
+ return RepositoryStorage.track(repository, () => RepositoryStorage.openRepositoryAgentHistory(repository, runtime.storageFactory, input).read({
510
+ entityId,
511
+ view,
512
+ ...read,
513
+ }));
627
514
  },
628
515
  async agentHistoryPage(repository, entityId, request, tenantId) {
629
516
  if (repository.entityFamily !== "agent")
@@ -632,22 +519,16 @@ export const repositoryAccess = Object.freeze({
632
519
  if (runtime === undefined)
633
520
  throw new Error("Agent history requires an active repository.");
634
521
  const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
522
+ await runtime.onAdmit(context.tenantId);
635
523
  const input = RepositoryStorage.entityStorageInput(repository, context);
636
- const history = AgentHistoryStorageFactories.create(runtime.storageFactory, {
637
- context,
638
- stateType: repository.stateSchema.typeName,
639
- id: { key: input.id.key },
640
- });
641
- try {
642
- return await AgentHistoryReads.readBound({
524
+ return RepositoryStorage.track(repository, () => {
525
+ const history = RepositoryStorage.openRepositoryAgentHistory(repository, runtime.storageFactory, input);
526
+ return AgentHistoryReads.readBound({
643
527
  storage: history,
644
528
  entityId,
645
529
  scope: this.agentHistoryScope(context, input, entityId),
646
530
  }, { kind: "full" }, request);
647
- }
648
- finally {
649
- history.close();
650
- }
531
+ });
651
532
  },
652
533
  /**
653
534
  * Computes the opaque cursor scope for a registered Agent history read.
@@ -759,6 +640,7 @@ export const repositoryAccess = Object.freeze({
759
640
  */
760
641
  bindRuntime(repository, runtime) {
761
642
  repositoryRuntimes.set(repository, Object.freeze(runtime));
643
+ repositoryStorageLifetimes.set(repository, RepositoryStorage.newLifetime());
762
644
  },
763
645
  /**
764
646
  * Removes runtime dispatch state and closes tracked storage handles.
@@ -768,12 +650,18 @@ export const repositoryAccess = Object.freeze({
768
650
  clearRuntime(repository) {
769
651
  repositoryRuntimes.delete(repository);
770
652
  repositoryDispatchGuards.delete(repository);
771
- const handles = repositoryEntityHandles.get(repository);
772
- if (handles !== undefined) {
773
- for (const handle of handles.values())
774
- handle.close();
775
- handles.clear();
776
- }
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);
777
665
  },
778
666
  });
779
667
  const inboxDedupMs = 30_000;
@@ -931,14 +819,12 @@ class RepositoryReadScope {
931
819
  throw new Error("Repository read access has finished.");
932
820
  if (query.schema !== this.#repository.stateSchema)
933
821
  throw new TypeError("Repository query must select the receiving Entity state schema.");
822
+ await this.#runtime.onAdmit(this.#context.tenantId);
934
823
  const plan = RepositoryReadPredicates.plan(query.buildPlan());
935
- const storage = RepositoryStorage.openReadEntityStorage(this.#runtime.storageFactory, RepositoryStorage.entityStorageInput(this.#repository, this.#context));
936
- try {
937
- return await storage.current.query(plan);
938
- }
939
- finally {
940
- storage.close();
941
- }
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
+ });
942
828
  }
943
829
  }
944
830
  /**
@@ -1046,7 +932,20 @@ class AggregateExecutionSupport {
1046
932
  ? {}
1047
933
  : { tenantId: this.#storageContext.tenantId });
1048
934
  const entity = this.#instantiateAggregate(entityId, current);
1049
- 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) {
1050
949
  return Object.freeze({
1051
950
  commits: storage.commits,
1052
951
  current: current === undefined
@@ -1135,9 +1034,7 @@ class AggregateExecutionSupport {
1135
1034
  entity: loaded.storageInput,
1136
1035
  entityId,
1137
1036
  next: record,
1138
- ...(RepositoryStorage.historyConfiguration(this.#repository).stateHistory
1139
- ? { states: [record] }
1140
- : {}),
1037
+ ...(loaded.storageInput.stateHistory ? { states: [record] } : {}),
1141
1038
  diagnostics: diagnostics.map((event) => clone(EventSchema, event)),
1142
1039
  events,
1143
1040
  });
@@ -1338,11 +1235,11 @@ class AggregateCommandExecution {
1338
1235
  const produced = await EntityInvocation.invokeEntityMethod(entity, handler.methodName, message, handler.parameterCount, context);
1339
1236
  const signals = this.#support.normalizeProducedSignals(produced);
1340
1237
  if (signals.length === 0)
1341
- 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."));
1342
1239
  RepositoryHandlers.requireDeclaredOutputs(handler, signals);
1343
1240
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, true));
1344
1241
  if (commit.status === "rejected") {
1345
- throw new TransitionValidationError(commit.validation.error);
1242
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1346
1243
  }
1347
1244
  return produced;
1348
1245
  }
@@ -1394,7 +1291,7 @@ class AggregateCommandExecution {
1394
1291
  }
1395
1292
  return create(EventSchema, {
1396
1293
  id: metadata.id,
1397
- message: AnyMessages.pack(schema, message),
1294
+ message: RepositorySignals.packProduced(schema, message),
1398
1295
  context: metadata.context,
1399
1296
  });
1400
1297
  }
@@ -1564,7 +1461,7 @@ class AggregateEventExecution {
1564
1461
  async #commitEntity(entity, producedEvents) {
1565
1462
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, producedEvents));
1566
1463
  if (commit.status === "rejected")
1567
- throw new TransitionValidationError(commit.validation.error);
1464
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1568
1465
  }
1569
1466
  /**
1570
1467
  * Binds reactor results using the pre-dispatch Aggregate version.
@@ -1597,7 +1494,7 @@ class AggregateEventExecution {
1597
1494
  });
1598
1495
  return create(EventSchema, {
1599
1496
  id: metadata.id,
1600
- message: AnyMessages.pack(schema, signal),
1497
+ message: RepositorySignals.packProduced(schema, signal),
1601
1498
  context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId, version),
1602
1499
  });
1603
1500
  }
@@ -1805,9 +1702,7 @@ class ProjectionEventExecution {
1805
1702
  entity: loaded.storageInput,
1806
1703
  entityId,
1807
1704
  next: record,
1808
- ...(RepositoryStorage.historyConfiguration(this.#repository).stateHistory
1809
- ? { states: [record] }
1810
- : {}),
1705
+ ...(loaded.storageInput.stateHistory ? { states: [record] } : {}),
1811
1706
  });
1812
1707
  }
1813
1708
  /**
@@ -1846,7 +1741,7 @@ class ProjectionEventExecution {
1846
1741
  }
1847
1742
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
1848
1743
  if (commit.status === "rejected") {
1849
- throw new TransitionValidationError(commit.validation.error);
1744
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1850
1745
  }
1851
1746
  }
1852
1747
  catch (error) {
@@ -1867,7 +1762,7 @@ class ProjectionEventExecution {
1867
1762
  const context = EntityInvocation.eventHandlerContext(this.#event);
1868
1763
  const result = await EntityInvocation.invokeEntityMethod(entity, subscriber.handler.methodName, message, subscriber.handler.parameterCount, context);
1869
1764
  if (result !== undefined)
1870
- 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.`));
1871
1766
  }
1872
1767
  /**
1873
1768
  * Invokes state subscribers in one Entity transaction.
@@ -1884,11 +1779,11 @@ class ProjectionEventExecution {
1884
1779
  const context = EntityInvocation.eventHandlerContext(this.#event);
1885
1780
  const result = await EntityInvocation.invokeEntityMethod(entity, subscriber.handler.methodName, state, subscriber.handler.parameterCount, context);
1886
1781
  if (result !== undefined)
1887
- 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.`));
1888
1783
  }
1889
1784
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
1890
1785
  if (commit.status === "rejected")
1891
- throw new TransitionValidationError(commit.validation.error);
1786
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
1892
1787
  }
1893
1788
  catch (error) {
1894
1789
  transactionalEntityAccess.rollback(entity);
@@ -2034,13 +1929,7 @@ class ProcessManagerExecutionSupport {
2034
1929
  const priorNumber = loaded.current?.version?.number ?? 0;
2035
1930
  if (version.number === priorNumber) {
2036
1931
  await this.#appendUnchangedDiagnostics(loaded, diagnostics);
2037
- const store = new EventStore(this.#runtime.context, this.#runtime.storageFactory);
2038
- try {
2039
- await store.appendAll(producedEvents);
2040
- }
2041
- finally {
2042
- store.close();
2043
- }
1932
+ await RepositoryStorage.eventStore(this.#repository, this.#runtime).appendAll(producedEvents);
2044
1933
  await this.#appendAgentEvents(loaded, producedEvents);
2045
1934
  return true;
2046
1935
  }
@@ -2127,7 +2016,7 @@ class ProcessManagerExecutionSupport {
2127
2016
  entity: loaded.storageInput,
2128
2017
  entityId,
2129
2018
  next: record,
2130
- ...(history.stateHistory ? { states: [record] } : {}),
2019
+ ...(loaded.storageInput.stateHistory ? { states: [record] } : {}),
2131
2020
  ...(history.processManagerEventHistory
2132
2021
  ? { diagnostics: diagnostics.map((event) => clone(EventSchema, event)) }
2133
2022
  : {}),
@@ -2150,1268 +2039,706 @@ class ProcessManagerExecutionSupport {
2150
2039
  }
2151
2040
  }
2152
2041
  /**
2153
- * Advances admitted Agent work only under a provider claim and exact record fence.
2042
+ * Binds normal outgoing Agent signals from one live turn.
2154
2043
  */
2155
- const AgentExecutionRunner = {
2044
+ const AgentLiveOutputs = {
2156
2045
  /**
2157
- * Processes one accepted Agent transition under its provider claim.
2046
+ * Stores only committed domain Events in the Agent's emitted history.
2158
2047
  *
2159
- * @param repository Registered Agent repository.
2160
- * @param tenantId Tenant selected for this invocation.
2161
- * @param key Exact accepted invocation key.
2162
- * @param signal Cancellation signal for the current accepted execution.
2163
- * @returns When the fenced repository operation completes.
2048
+ * @param outputs Prepared outgoing domain signals.
2049
+ * @returns Committed domain Event rows for Agent history.
2164
2050
  */
2165
- async run(repository, tenantId, key, signal) {
2166
- if (signal?.aborted)
2167
- return;
2168
- const runtime = repositoryRuntimes.get(repository);
2169
- if (runtime?.ai === undefined || repository.entityFamily !== "agent")
2170
- throw new Error("Agent execution requires a bound Agent repository and AI registry.");
2171
- const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
2172
- const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
2173
- const executions = storage.agentExecution;
2174
- if (executions === undefined)
2175
- throw new Error("Agent execution storage is required.");
2176
- const token = randomUUID();
2177
- const claimed = await executions.claim(key, token, addMillis(Time.currentTime(), 30_000));
2178
- if (claimed === undefined)
2179
- return;
2180
- const session = new AgentExecutionSession(executions, claimed.record, token, claimed.preferences);
2181
- await this.runClaimed(repository, runtime, tenantId, session, signal);
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
+ });
2182
2066
  },
2183
2067
  /**
2184
- * Updates and releases one exact claim while its saved work advances.
2068
+ * Binds one declared result using its generated Event or Command role.
2185
2069
  *
2186
2070
  * @param repository Registered Entity repository for this operation.
2187
2071
  * @param runtime Bounded Context storage and publication services.
2188
- * @param tenantId Tenant selected for this accepted signal.
2189
- * @param session Fenced Agent execution session.
2190
- * @param signal Cancellation signal for the current accepted execution.
2191
- * @returns When the fenced repository operation completes.
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.
2192
2078
  */
2193
- async runClaimed(repository, runtime, tenantId, session, signal) {
2194
- const abort = () => {
2195
- session.stop();
2196
- };
2197
- signal?.addEventListener("abort", abort, { once: true });
2198
- if (signal?.aborted)
2199
- abort();
2200
- const renewal = setInterval(() => {
2201
- // spine-log-boundary: server.agent_lease_renewal
2202
- void session.renew(addMillis(Time.currentTime(), 30_000)).catch(() => undefined);
2203
- }, 10_000);
2204
- try {
2205
- if (session.signal.aborted)
2206
- return;
2207
- await this.advanceClaimed(repository, runtime, tenantId, session);
2208
- }
2209
- catch (error) {
2210
- if (signal?.aborted)
2211
- return;
2212
- await this.handleRunFailure(repository, runtime, tenantId, session, error);
2213
- }
2214
- finally {
2215
- clearInterval(renewal);
2216
- signal?.removeEventListener("abort", abort);
2217
- session.stop();
2218
- }
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);
2219
2088
  },
2220
2089
  /**
2221
- * Handles transient failures while giving expired work a final audit.
2090
+ * Creates the original domain Event envelope and producer context.
2222
2091
  *
2223
2092
  * @param repository Registered Entity repository for this operation.
2224
2093
  * @param runtime Bounded Context storage and publication services.
2225
- * @param tenantId Tenant selected for this accepted signal.
2226
- * @param session Fenced Agent execution session.
2227
- * @param error Failure raised while processing this accepted signal.
2228
- * @returns When the fenced repository operation completes.
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.
2229
2100
  */
2230
- async handleRunFailure(repository, runtime, tenantId, session, error) {
2231
- if (session.record().status ===
2232
- AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_COMMANDS ||
2233
- session.record().status === AgentInvocationStatus.AGENT_INVOCATION_COMPLETED)
2234
- throw error;
2235
- if (error instanceof AgentUncertainAttemptError) {
2236
- const reason = this.hasUnresolvedToolWrite(session.record())
2237
- ? "TOOL_OUTCOME_UNKNOWN"
2238
- : "MODEL_OUTCOME_UNKNOWN";
2239
- return this.terminate(repository, runtime, tenantId, session, reason);
2240
- }
2241
- if (error instanceof AgentExecutionFault) {
2242
- if (session.signal.aborted)
2243
- throw error;
2244
- return this.terminate(repository, runtime, tenantId, session, error.reason);
2245
- }
2246
- if (this.deadlineExpired(session.record()))
2247
- return this.terminate(repository, runtime, tenantId, session, "DEADLINE_EXCEEDED");
2248
- throw error;
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 };
2249
2111
  },
2250
2112
  /**
2251
- * Processes one claimed record according to its saved completion or request state.
2113
+ * Creates the original Command envelope from its accepted origin.
2252
2114
  *
2253
- * @param repository Registered Entity repository for this operation.
2254
2115
  * @param runtime Bounded Context storage and publication services.
2255
- * @param tenantId Tenant selected for this accepted signal.
2256
- * @param session Fenced Agent execution session.
2257
- * @returns When the fenced repository operation completes.
2258
- */
2259
- async advanceClaimed(repository, runtime, tenantId, session) {
2260
- if (this.hasUnresolvedPhysicalRequest(session.record()))
2261
- return this.terminate(repository, runtime, tenantId, session, this.hasUnresolvedToolWrite(session.record())
2262
- ? "TOOL_OUTCOME_UNKNOWN"
2263
- : "MODEL_OUTCOME_UNKNOWN");
2264
- if (session.record().status === AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_COMMANDS)
2265
- return this.deliverCommands(runtime, session);
2266
- if (this.deadlineExpired(session.record()))
2267
- return this.terminate(repository, runtime, tenantId, session, "DEADLINE_EXCEEDED");
2268
- return this.execute(repository, runtime, tenantId, session);
2269
- },
2270
- /**
2271
- * Tests the original saved deadline without granting recovery fresh time.
2272
- *
2273
- * @param record Durable Agent execution record.
2274
- * @returns Whether the checked condition holds.
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.
2275
2120
  */
2276
- deadlineExpired(record) {
2277
- const deadline = record.started?.deadline;
2278
- return (deadline !== undefined &&
2279
- Time.currentTimeMillis() >=
2280
- Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000));
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 };
2281
2131
  },
2132
+ };
2133
+ /**
2134
+ * Waits only for a current in-process handler invocation.
2135
+ */
2136
+ const AgentLiveHandler = {
2282
2137
  /**
2283
- * Checks for a dispatched model reservation whose response was never saved.
2138
+ * Awaits an application handler within its live deadline and current session.
2284
2139
  *
2285
- * @param record Durable Agent execution record.
2286
- * @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.
2287
2145
  */
2288
- hasUnresolvedPhysicalRequest(record) {
2289
- const model = record.journal.some((entry) => {
2290
- if (entry.evidence.case !== "attempt")
2291
- return false;
2292
- const attempt = entry.evidence.value;
2293
- return (attempt.transportReserved &&
2294
- attempt.response.case === undefined &&
2295
- !this.hasSavedOperationResult(record, attempt.operation?.value));
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();
2296
2167
  });
2297
- return model || this.hasUnresolvedToolWrite(record);
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
+ }
2298
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
+ }
2299
2255
  /**
2300
- * Checks for a dispatched write with no recorded tool result.
2256
+ * Writes only definite pre-handler model-use denial with its original source.
2301
2257
  *
2302
- * @param record Durable Agent execution record.
2303
- * @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.
2304
2261
  */
2305
- hasUnresolvedToolWrite(record) {
2306
- return record.journal.some((entry) => {
2307
- if (entry.evidence.case !== "tool")
2308
- return false;
2309
- const tool = entry.evidence.value;
2310
- return (tool.request?.effect === ToolEffect.WRITE &&
2311
- tool.response === undefined &&
2312
- (tool.dispatched || tool.outcomeUnknown) &&
2313
- !this.hasSavedOperationResult(record, tool.operation?.value));
2262
+ async recordModelDenial(audit, denial) {
2263
+ const agent = create(MessageIdSchema, {
2264
+ id: this.invocation.recipientId,
2265
+ typeUrl: TypeUrls.derive(this.repository.stateSchema),
2314
2266
  });
2315
- },
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
+ }
2316
2275
  /**
2317
- * Checks for a settled named result even when physical evidence is incomplete.
2276
+ * Converts only a declared Command rejection into a normal outgoing Event.
2318
2277
  *
2319
- * @param record Durable Agent execution record.
2320
- * @param id Saved signal, operation, or Entity identifier.
2321
- * @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.
2322
2284
  */
2323
- hasSavedOperationResult(record, id) {
2324
- return record.journal.some((entry) => entry.evidence.case === "operation" &&
2325
- entry.evidence.value.operation?.value === id &&
2326
- entry.evidence.value.result.case !== undefined);
2327
- },
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
+ }
2328
2304
  /**
2329
- * Records termination of an unreplayable physical request under the provider fence.
2305
+ * Finds each enabled model from the preference image loaded at turn start.
2330
2306
  *
2331
- * @param repository Registered Entity repository for this operation.
2332
- * @param runtime Bounded Context storage and publication services.
2333
- * @param tenantId Tenant selected for this accepted signal.
2334
- * @param session Fenced Agent execution session.
2335
- * @param reason Safe terminal reason retained in Agent audit.
2336
- * @returns When the fenced repository operation completes.
2307
+ * @param preferences Current or staged model preferences.
2308
+ * @returns The selected model image for the current Agent turn.
2337
2309
  */
2338
- async terminate(repository, runtime, tenantId, session, reason) {
2339
- const record = session.record();
2340
- const event = this.terminationEvent(repository, runtime, tenantId, record, reason);
2341
- const occurredAt = event.context?.timestamp;
2342
- if (occurredAt === undefined)
2343
- throw new Error("Agent termination Event requires Time.");
2344
- runtime.registerSystemEventSchema(AgentInvocationTerminatedSchema);
2345
- await session.update((next) => {
2346
- next.status = AgentInvocationStatus.AGENT_INVOCATION_TERMINATED;
2347
- next.terminalFailure = create(AiDiagnosticIdSchema, { value: randomUUID() });
2348
- return next;
2349
- }, [
2350
- create(AgentHistoryEntrySchema, {
2351
- occurredAt,
2352
- item: { case: "systemEvent", value: event },
2353
- }),
2354
- ]);
2355
- await runtime.publishAgentSystemEvent(event);
2356
- },
2357
- /**
2358
- * Builds one original termination envelope for a saved terminal reason.
2359
- *
2360
- * @param repository Registered Entity repository for this operation.
2361
- * @param runtime Bounded Context storage and publication services.
2362
- * @param tenantId Tenant selected for this accepted signal.
2363
- * @param record Durable Agent execution record.
2364
- * @param reason Safe terminal reason retained in Agent audit.
2365
- * @returns Original AgentInvocationTerminated System Event envelope.
2366
- */
2367
- terminationEvent(repository, runtime, tenantId, record, reason) {
2368
- const accepted = record.accepted;
2369
- if (accepted?.recipientId === undefined)
2370
- throw new Error("Agent termination requires the original typed recipient.");
2371
- const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2372
- const unresolvedToolCalls = this.unresolvedToolCalls(record);
2373
- const payload = create(AgentInvocationTerminatedSchema, {
2374
- agent: scope.agent,
2375
- sourceSignal: scope.source,
2376
- reason,
2377
- unresolvedAttempts: this.unresolvedAttempts(record),
2378
- unresolvedToolCalls,
2379
- terminatedAt: Time.currentTime(),
2380
- });
2381
- return AgentInteractionEvents.envelope(accepted, runtime.signalMetadata, accepted.recipientId, AgentInvocationTerminatedSchema, payload);
2382
- },
2383
- /**
2384
- * Returns IDs only for physically reserved model requests without saved outcomes.
2385
- *
2386
- * @param record Durable execution journal.
2387
- * @returns Original attempt IDs for unresolved physical sends.
2388
- */
2389
- unresolvedAttempts(record) {
2390
- return record.journal.flatMap((entry) => {
2391
- if (entry.evidence.case !== "attempt")
2392
- return [];
2393
- const attempt = entry.evidence.value;
2394
- return attempt.transportReserved &&
2395
- attempt.response.case === undefined &&
2396
- !this.hasSavedOperationResult(record, attempt.operation?.value) &&
2397
- attempt.attempt !== undefined
2398
- ? [attempt.attempt]
2399
- : [];
2400
- });
2401
- },
2402
- /**
2403
- * Returns exact IDs for dispatched write effects without saved results.
2404
- *
2405
- * @param record Durable Agent execution record.
2406
- * @returns Original ToolCall IDs for unresolved dispatched writes.
2407
- */
2408
- unresolvedToolCalls(record) {
2409
- return record.journal.flatMap((entry) => {
2410
- if (entry.evidence.case !== "tool")
2411
- return [];
2412
- const tool = entry.evidence.value;
2413
- return tool.request?.effect === ToolEffect.WRITE &&
2414
- tool.response === undefined &&
2415
- (tool.dispatched || tool.outcomeUnknown) &&
2416
- tool.request.call !== undefined
2417
- ? [tool.request.call]
2418
- : [];
2419
- });
2420
- },
2421
- /**
2422
- * Persists start facts, runs selected handlers, and conditionally completes a no-op.
2423
- *
2424
- * @param repository Agent registration.
2425
- * @param runtime Bound services.
2426
- * @param tenantId Complete tenant, when applicable.
2427
- * @param session Fenced Agent execution session.
2428
- * @returns When the fenced repository operation completes.
2429
- */
2430
- async execute(repository, runtime, tenantId, session) {
2431
- const accepted = session.record().accepted;
2432
- if (accepted?.key === undefined || accepted.recipientId === undefined)
2433
- throw new Error("Claimed Agent invocation lacks accepted source and recipient.");
2434
- const entityId = InboxMessages.targetEntityId(accepted.recipientId, repository.idField);
2435
- const support = new ProcessManagerExecutionSupport(repository, runtime);
2436
- const loaded = await support.load(entityId, tenantId === undefined ? {} : { tenantId });
2437
- const handlers = this.handlers(repository, accepted);
2438
- const configuration = repositoryAccess.agentConfiguration(repository);
2439
- if (runtime.ai === undefined)
2440
- throw new Error("Agent execution requires a bound AI registry.");
2441
- this.assertRevisions(repository, accepted, handlers, runtime.ai, configuration);
2442
- const record = session.record().started === undefined
2443
- ? await this.start(repository, runtime, session, loaded, entityId, tenantId)
2444
- : await this.selectMissingModels(repository, runtime, session, accepted, tenantId);
2445
- if (session.signal.aborted)
2446
- return;
2447
- const outputs = await this.completeExecution(repository, runtime, tenantId, session, loaded, accepted, handlers, entityId, record.started?.initialVersion ?? create(VersionSchema));
2448
- await this.deliverInitial(runtime, session, outputs);
2449
- },
2450
- /**
2451
- * Creates or rejects the draft before one conditional Entity completion.
2452
- *
2453
- * @param repository Registered Entity repository for this operation.
2454
- * @param runtime Bounded Context storage and publication services.
2455
- * @param tenantId Tenant selected for this accepted signal.
2456
- * @param session Fenced Agent execution session.
2457
- * @param loaded Loaded Entity and its repository storage state.
2458
- * @param accepted Durable original signal and saved handler bindings.
2459
- * @param handlers Generated handlers in their saved order.
2460
- * @param entityId Typed recipient Entity identifier.
2461
- * @param initialVersion Entity version before selected handlers run.
2462
- * @returns Record after conditional Entity and execution completion.
2463
- */
2464
- async completeExecution(repository, runtime, tenantId, session, loaded, accepted, handlers, entityId, initialVersion) {
2465
- const outputs = await this.outputsOrRejection(repository, runtime, tenantId, session, loaded.entity, accepted, handlers, entityId, initialVersion);
2466
- await this.complete(repository, runtime, tenantId, session, loaded, entityId, outputs);
2467
- return outputs;
2468
- },
2469
- /**
2470
- * Rejects a Command draft and persists only its declared rejection.
2471
- *
2472
- * @param repository Registered Entity repository for this operation.
2473
- * @param runtime Bounded Context storage and publication services.
2474
- * @param tenantId Tenant selected for this accepted signal.
2475
- * @param session Fenced Agent execution session.
2476
- * @param entity Loaded recipient Entity instance.
2477
- * @param accepted Durable original signal and saved handler bindings.
2478
- * @param handlers Generated handlers in their saved order.
2479
- * @param entityId Typed recipient Entity identifier.
2480
- * @param initialVersion Entity version before selected handlers run.
2481
- * @returns Prepared outgoing signals, or only the declared rejection.
2482
- */
2483
- async outputsOrRejection(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion) {
2484
- try {
2485
- return await this.produceOutputs(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion);
2486
- }
2487
- catch (error) {
2488
- if (!RejectionThrowable.is(error) || accepted.signal.case !== "command")
2489
- throw error;
2490
- const handler = handlers[0];
2491
- if (handler === undefined)
2492
- throw new Error("Agent rejection requires a Command assignee.");
2493
- RepositorySignals.requireDeclaredRejection(handler.handler, error);
2494
- session.discardPreferenceChanges();
2495
- return [this.rejectionOutput(runtime, repository, accepted.signal.value, entityId, error)];
2496
- }
2497
- },
2498
- /**
2499
- * Wraps the original rejection Event for one normal EventBus handoff.
2500
- *
2501
- * @param runtime Bounded Context storage and publication services.
2502
- * @param repository Registered Entity repository for this operation.
2503
- * @param command Original typed Command envelope.
2504
- * @param entityId Typed recipient Entity identifier.
2505
- * @param error Failure raised while processing this accepted signal.
2506
- * @returns Prepared original rejection Event for one normal EventBus handoff.
2507
- */
2508
- rejectionOutput(runtime, repository, command, entityId, error) {
2509
- return {
2510
- case: "event",
2511
- value: RepositorySignals.rejectionEvent(runtime, repository, command, entityId, error),
2512
- };
2513
- },
2514
- /**
2515
- * Restores handler results and binds their declared original output envelopes.
2516
- *
2517
- * @param repository Registered Entity repository for this operation.
2518
- * @param runtime Bounded Context storage and publication services.
2519
- * @param tenantId Tenant selected for this accepted signal.
2520
- * @param session Fenced Agent execution session.
2521
- * @param entity Loaded recipient Entity instance.
2522
- * @param accepted Durable original signal and saved handler bindings.
2523
- * @param handlers Generated handlers in their saved order.
2524
- * @param entityId Typed recipient Entity identifier.
2525
- * @param initialVersion Entity version before selected handlers run.
2526
- * @returns Prepared outgoing signals from accepted handler results.
2527
- */
2528
- async produceOutputs(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion) {
2529
- const produced = await this.invoke(repository, runtime, tenantId, session, entity, accepted, handlers);
2530
- return this.bindOutputs(repository, runtime, accepted, entityId, initialVersion, produced);
2531
- },
2532
- /**
2533
- * Commits changed state and the fenced execution record as one provider mutation.
2534
- *
2535
- * @param repository Agent registration.
2536
- * @param runtime Bounded Context Stand and provider services.
2537
- * @param tenantId Complete delivery tenant.
2538
- * @param session Serialized provider claim and current record.
2539
- * @param loaded Entity after handler transaction commit.
2540
- * @param entityId Original typed recipient.
2541
- * @param outputs Original emitted Event or Command envelopes.
2542
- * @returns Complete record after the atomic provider mutation.
2543
- */
2544
- async complete(repository, runtime, tenantId, session, loaded, entityId, outputs) {
2545
- await this.authorizePreferenceChanges(repository, runtime, tenantId, session);
2546
- const version = RepositoryEntities.repositoryVersion(loaded.entity);
2547
- const preferences = session.preferences();
2548
- const emitted = this.emittedHistory(outputs);
2549
- const preferenceEvents = this.preferenceEvents(repository, runtime, tenantId, session);
2550
- const historyEntries = [...emitted, ...this.preferenceHistory(preferenceEvents)];
2551
- const makeNext = (record) => this.completionFacts(record, version, outputs, preferences);
2552
- const completed = await this.commitCompletion(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries);
2553
- for (const event of preferenceEvents)
2554
- await runtime.publishAgentSystemEvent(event);
2555
- return completed;
2556
- },
2557
- /**
2558
- * Checks effective staged model changes before any completion mutation.
2559
- *
2560
- * @param repository Agent registration and model policy.
2561
- * @param runtime Bounded Context and AI registry.
2562
- * @param tenantId Accepted tenant when applicable.
2563
- * @param session Fenced Agent execution and staged preferences.
2564
- * @returns When every effective change is authorized.
2565
- */
2566
- async authorizePreferenceChanges(repository, runtime, tenantId, session) {
2567
- const policy = repositoryAccess.agentConfiguration(repository).ai;
2568
- if (policy?.authorizeSelection === undefined)
2569
- return;
2570
- const accepted = session.record().accepted;
2571
- if (runtime.ai === undefined || accepted === undefined)
2572
- throw new Error("Agent model preference requires accepted start facts.");
2573
- const deadlineMs = this.savedModelDeadline(session, "preference");
2574
- const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2575
- for (const kind of [AiModelKind.GENERATION, AiModelKind.DECISION]) {
2576
- const previous = this.preferenceModel(session.initialPreferences(), kind);
2577
- const selected = this.preferenceModel(session.preferences(), kind);
2578
- if (previous === undefined && selected === undefined)
2579
- continue;
2580
- if (previous !== undefined &&
2581
- selected !== undefined &&
2582
- equals(ModelRefSchema, previous, selected))
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))
2583
2319
  continue;
2584
- await AgentModelSelection.authorizeSelection(runtime.ai, policy, scope, selected, session.signal, deadlineMs);
2585
- }
2586
- },
2587
- /**
2588
- * Reads an optional saved model deadline and detects incomplete persisted bounds.
2589
- *
2590
- * @param session Fenced Agent execution session.
2591
- * @param context Selection or preference authorization context.
2592
- * @returns Saved absolute deadline, when configured.
2593
- */
2594
- savedModelDeadline(session, context) {
2595
- const record = session.record();
2596
- const deadline = record.started?.deadline;
2597
- if (deadline === undefined && record.started?.bounds?.deadlineMillis !== undefined)
2598
- throw new Error(`Agent model ${context} deadline was not saved.`);
2599
- return deadline === undefined
2600
- ? undefined
2601
- : Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000);
2602
- },
2603
- /**
2604
- * Applies the same provider fence to preference events and Entity completion.
2605
- *
2606
- * @param repository Registered Entity repository for this operation.
2607
- * @param runtime Bounded Context storage and publication services.
2608
- * @param tenantId Tenant selected for this accepted signal.
2609
- * @param session Fenced Agent execution session.
2610
- * @param loaded Loaded Entity and its repository storage state.
2611
- * @param entityId Typed recipient Entity identifier.
2612
- * @param makeNext Callback constructing the next durable execution record.
2613
- * @param historyEntries Agent history rows included in the fenced mutation.
2614
- * @returns Saved execution record after conditional completion.
2615
- */
2616
- commitCompletion(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries) {
2617
- return RepositoryEntities.repositoryChanged(loaded.entity)
2618
- ? this.commitChanged(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries)
2619
- : session.complete(makeNext, historyEntries);
2620
- },
2621
- /**
2622
- * Wraps original preference Events as indexed Agent history entries.
2623
- *
2624
- * @param events Original Event envelopes retained for diagnostics.
2625
- * @returns Typed Agent history rows for saved preference changes.
2626
- */
2627
- preferenceHistory(events) {
2628
- return events.map((event) => {
2629
- const occurredAt = event.context?.timestamp;
2630
- if (occurredAt === undefined)
2631
- throw new Error("Agent preference Event requires Time.");
2632
- return create(AgentHistoryEntrySchema, {
2633
- occurredAt,
2634
- item: { case: "systemEvent", value: event },
2635
- });
2636
- });
2637
- },
2638
- /**
2639
- * Captures only effective preference changes committed by this signal.
2640
- *
2641
- * @param repository Registered Entity repository for this operation.
2642
- * @param runtime Bounded Context storage and publication services.
2643
- * @param tenantId Tenant selected for this accepted signal.
2644
- * @param session Fenced Agent execution session.
2645
- * @returns Original model-selection System Events.
2646
- */
2647
- preferenceEvents(repository, runtime, tenantId, session) {
2648
- const accepted = session.record().accepted;
2649
- if (accepted?.recipientId === undefined)
2650
- throw new Error("Agent preference requires accepted work.");
2651
- const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2652
- return [AiModelKind.GENERATION, AiModelKind.DECISION].flatMap((kind) => {
2653
- const previous = this.preferenceModel(session.initialPreferences(), kind);
2654
- const selected = this.preferenceModel(session.preferences(), kind);
2655
- if (previous === undefined && selected === undefined)
2656
- return [];
2657
- if (previous !== undefined &&
2658
- selected !== undefined &&
2659
- equals(ModelRefSchema, previous, selected))
2660
- return [];
2661
- return [this.preferenceEvent(accepted, scope, kind, previous, selected, runtime)];
2662
- });
2663
- },
2664
- /**
2665
- * Builds one effective selection change with its original causal context.
2666
- *
2667
- * @param accepted Durable original signal and saved handler bindings.
2668
- * @param scope Accepted actor, source, Bounded Context, and tenant.
2669
- * @param kind Generation or decision model kind.
2670
- * @param previous Previously selected model deployment.
2671
- * @param selected Newly selected model deployment.
2672
- * @param runtime Bounded Context storage and publication services.
2673
- * @returns Original model-selection System Event.
2674
- */
2675
- preferenceEvent(accepted, scope, kind, previous, selected, runtime) {
2676
- const recipientId = accepted.recipientId;
2677
- if (recipientId === undefined)
2678
- throw new Error("Agent preference requires a recipient.");
2679
- const payload = create(SelectionChangedSchema, {
2680
- agent: scope.agent,
2681
- sourceSignal: scope.source,
2682
- kind,
2683
- ...(previous === undefined ? {} : { previousModel: previous }),
2684
- ...(selected === undefined ? {} : { selectedModel: selected }),
2685
- changedAt: Time.currentTime(),
2686
- });
2687
- runtime.registerSystemEventSchema(SelectionChangedSchema);
2688
- return AgentInteractionEvents.envelope(accepted, runtime.signalMetadata, recipientId, SelectionChangedSchema, payload);
2689
- },
2690
- /**
2691
- * Reads an explicit model selection, including inherited defaults as absence.
2692
- *
2693
- * @param preferences Current Agent instance model preferences.
2694
- * @param kind Generation or decision model kind.
2695
- * @returns Selected deployment reference, or inherited default.
2696
- */
2697
- preferenceModel(preferences, kind) {
2698
- const selected = preferences.find((entry) => entry.kind === kind);
2699
- return selected?.selection.case === "model" ? selected.selection.value : undefined;
2700
- },
2701
- /**
2702
- * Builds the immutable completion image and emitted domain history.
2703
- *
2704
- * @param record Durable Agent execution record.
2705
- * @param version Entity version associated with the signal.
2706
- * @param outputs Prepared outgoing domain signals.
2707
- * @param preferences Current Agent instance model preferences.
2708
- * @returns Record completed with original outputs and resulting version.
2709
- */
2710
- completionFacts(record, version, outputs, preferences) {
2711
- const initialVersion = record.started?.initialVersion;
2712
- if (initialVersion === undefined)
2713
- throw new Error("Agent completion requires its started Version.");
2714
- const commands = outputs
2715
- .filter((output) => output.case === "command")
2716
- .map((output) => create(AgentOutgoingCommandSchema, { command: output.value }));
2717
- record.status =
2718
- commands.length === 0
2719
- ? AgentInvocationStatus.AGENT_INVOCATION_COMPLETED
2720
- : AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_COMMANDS;
2721
- record.completion = create(AgentExecutionCompletionSchema, {
2722
- initialVersion,
2723
- resultingVersion: version,
2724
- preferences: [...preferences],
2725
- commands,
2726
- });
2727
- return record;
2728
- },
2729
- /**
2730
- * Stores only committed domain Events in the Agent's emitted history.
2731
- *
2732
- * @param outputs Prepared outgoing domain signals.
2733
- * @returns Committed domain Event rows for Agent history.
2734
- */
2735
- emittedHistory(outputs) {
2736
- return outputs.flatMap((output) => {
2737
- if (output.case !== "event" || output.value.context?.rejection !== undefined)
2738
- return [];
2739
- const event = output.value;
2740
- const occurredAt = event.context?.timestamp;
2741
- if (occurredAt === undefined)
2742
- throw new Error("Agent emitted Event requires its original timestamp.");
2743
- return [
2744
- create(AgentHistoryEntrySchema, {
2745
- occurredAt,
2746
- item: { case: "domainEvent", value: event },
2747
- }),
2748
- ];
2749
- });
2750
- },
2751
- /**
2752
- * Commits changed Entity state with the already prepared execution image.
2753
- *
2754
- * @param repository Registered Entity repository for this operation.
2755
- * @param runtime Bounded Context storage and publication services.
2756
- * @param tenantId Tenant selected for this accepted signal.
2757
- * @param session Fenced Agent execution session.
2758
- * @param loaded Loaded Entity and its repository storage state.
2759
- * @param entityId Typed recipient Entity identifier.
2760
- * @param makeNext Callback constructing the next durable execution record.
2761
- * @param historyEntries Agent history rows included in the fenced mutation.
2762
- * @returns Saved execution record after the changed Entity commit.
2763
- */
2764
- async commitChanged(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries) {
2765
- const version = RepositoryEntities.repositoryVersion(loaded.entity);
2766
- const state = RepositoryEntities.repositoryState(loaded.entity);
2767
- const lifecycle = RepositoryEntities.repositoryLifecycle(loaded.entity);
2768
- const deferred = await standAccess.deferUpdate(runtime.stand, repository.stateSchema, state, RepositoryStand.standUpdateOptions(tenantId, version, lifecycle), repository.metadata);
2769
- const prepared = RepositoryStand.preparedRecord(repository, deferred, entityId);
2770
- return this.commitDeferred(repository, session, loaded, entityId, makeNext, historyEntries, deferred, prepared);
2771
- },
2772
- /**
2773
- * Applies the deferred Stand image only after fenced provider completion.
2774
- *
2775
- * @param repository Registered Entity repository for this operation.
2776
- * @param session Fenced Agent execution session.
2777
- * @param loaded Loaded Entity and its repository storage state.
2778
- * @param entityId Typed recipient Entity identifier.
2779
- * @param makeNext Callback constructing the next durable execution record.
2780
- * @param historyEntries Agent history rows included in the fenced mutation.
2781
- * @param deferred Prepared Stand update awaiting conditional commit.
2782
- * @param prepared Prepared conditional Entity commit.
2783
- * @returns Saved execution record after the deferred Entity commit.
2784
- */
2785
- async commitDeferred(repository, session, loaded, entityId, makeNext, historyEntries, deferred, prepared) {
2786
- try {
2787
- const completed = await session.complete(makeNext, historyEntries, {
2788
- context: loaded.storageInput.context,
2789
- entity: loaded.storageInput,
2790
- entityId,
2791
- next: prepared,
2792
- ...(RepositoryStorage.historyConfiguration(repository).stateHistory
2793
- ? { states: [prepared] }
2794
- : {}),
2795
- });
2796
- deferred.notify();
2797
- return completed;
2798
- }
2799
- catch (error) {
2800
- deferred.cancel();
2801
- throw error;
2802
- }
2803
- },
2804
- /**
2805
- * Binds declared generated Event and Command results in handler order.
2806
- *
2807
- * @param repository Agent declaration.
2808
- * @param runtime Signal metadata factory.
2809
- * @param accepted Original accepted envelope.
2810
- * @param entityId Typed recipient.
2811
- * @param version Entity Version before this transition.
2812
- * @param produced Handler results in declaration order.
2813
- * @returns Original outgoing envelopes ready for one conditional completion.
2814
- */
2815
- bindOutputs(repository, runtime, accepted, entityId, version, produced) {
2816
- const source = accepted.signal;
2817
- if (source.case !== "command" && source.case !== "event")
2818
- throw new Error("Agent output requires its accepted source signal.");
2819
- const routing = repositoryRoutings.get(repository);
2820
- if (routing === undefined)
2821
- throw new Error("Agent output requires registered schemas.");
2822
- return produced.map((signal) => this.bindOutput(repository, runtime, routing, source, entityId, version, signal));
2823
- },
2824
- /**
2825
- * Binds one declared result using its generated Event or Command role.
2826
- *
2827
- * @param repository Registered Entity repository for this operation.
2828
- * @param runtime Bounded Context storage and publication services.
2829
- * @param routing Registered generated handler routing metadata.
2830
- * @param source Original accepted source signal.
2831
- * @param entityId Typed recipient Entity identifier.
2832
- * @param version Entity version associated with the signal.
2833
- * @param signal Original domain signal or execution cancellation signal.
2834
- * @returns Typed outgoing signal with its original envelope.
2835
- */
2836
- bindOutput(repository, runtime, routing, source, entityId, version, signal) {
2837
- const typeName = EntityInvocation.messageTypeName(signal);
2838
- const eventSchema = routing.producedEventSchemas.find((entry) => entry.typeName === typeName);
2839
- if (eventSchema !== undefined)
2840
- return this.bindEventOutput(repository, runtime, source, entityId, version, signal, eventSchema);
2841
- const schema = routing.producedCommandSchemas.find((entry) => entry.typeName === typeName);
2842
- if (schema === undefined)
2843
- throw new Error(`Agent cannot emit undeclared signal "${typeName}".`);
2844
- return this.bindCommandOutput(runtime, source, signal, schema);
2845
- },
2846
- /**
2847
- * Creates the original domain Event envelope and producer context.
2848
- *
2849
- * @param repository Registered Entity repository for this operation.
2850
- * @param runtime Bounded Context storage and publication services.
2851
- * @param source Original accepted source signal.
2852
- * @param entityId Typed recipient Entity identifier.
2853
- * @param version Entity version associated with the signal.
2854
- * @param signal Original domain signal or execution cancellation signal.
2855
- * @param schema Generated message descriptor for the signal.
2856
- * @returns Typed original Event envelope for one normal EventBus handoff.
2857
- */
2858
- bindEventOutput(repository, runtime, source, entityId, version, signal, schema) {
2859
- if (source.case !== "command" && source.case !== "event")
2860
- throw new Error("Missing source.");
2861
- const metadata = source.case === "command"
2862
- ? runtime.signalMetadata.eventFromCommand(source.value, { version: version.number })
2863
- : runtime.signalMetadata.eventFromEvent(source.value, { version: version.number });
2864
- const event = create(EventSchema, {
2865
- id: metadata.id,
2866
- message: AnyMessages.pack(schema, signal),
2867
- context: RepositorySignals.eventContextWithProducer(metadata.context, repository, entityId, version),
2868
- });
2869
- return { case: "event", value: event };
2870
- },
2871
- /**
2872
- * Creates the original Command envelope from its accepted origin.
2873
- *
2874
- * @param runtime Bounded Context storage and publication services.
2875
- * @param source Original accepted source signal.
2876
- * @param signal Original domain signal or execution cancellation signal.
2877
- * @param schema Generated message descriptor for the signal.
2878
- * @returns Typed original Command envelope for durable delivery.
2879
- */
2880
- bindCommandOutput(runtime, source, signal, schema) {
2881
- if (source.case !== "command" && source.case !== "event")
2882
- throw new Error("Missing source.");
2883
- const metadata = source.case === "command"
2884
- ? runtime.signalMetadata.commandFromCommand(source.value)
2885
- : runtime.signalMetadata.commandFromEvent(source.value);
2886
- const command = create(CommandSchema, {
2887
- id: metadata.id,
2888
- message: AnyMessages.pack(schema, signal),
2889
- context: metadata.context,
2890
- });
2891
- return { case: "command", value: command };
2892
- },
2893
- /**
2894
- * Dispatches every emitted Event to the normal EventBus and advances saved Commands.
2895
- *
2896
- * @param runtime Bound normal bus acceptance callbacks.
2897
- * @param session Fenced record containing pending saved envelopes.
2898
- * @param outputs Locally produced outputs in original handler order.
2899
- * @returns When the fenced repository operation completes.
2900
- */
2901
- async deliverInitial(runtime, session, outputs) {
2902
- let commandIndex = 0;
2903
- let failed = false;
2904
- let firstFailure;
2905
- for (const output of outputs) {
2906
- try {
2907
- if (output.case === "event") {
2908
- await runtime.publishAgentEvent(output.value);
2909
- }
2910
- else {
2911
- await this.deliverCommand(runtime, session, commandIndex);
2912
- }
2913
- }
2914
- catch (error) {
2915
- if (!failed) {
2916
- firstFailure = error;
2917
- failed = true;
2918
- }
2919
- }
2920
- if (output.case === "command")
2921
- commandIndex += 1;
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);
2922
2323
  }
2923
- if (failed)
2924
- throw firstFailure;
2925
- },
2926
- /**
2927
- * Retries pending saved Commands without rerunning the Agent handler.
2928
- *
2929
- * @param runtime Bound normal Command bus callbacks.
2930
- * @param session Fenced record containing saved Commands.
2931
- * @returns Completion after pending Command attempts.
2932
- */
2933
- async deliverCommands(runtime, session) {
2934
- for (const [index] of (session.record().completion?.commands ?? []).entries()) {
2935
- if (session.signal.aborted)
2936
- return;
2937
- await this.deliverCommand(runtime, session, index);
2938
- }
2939
- },
2940
- /**
2941
- * Sends one saved Command under the current claim and records its acknowledgement.
2942
- *
2943
- * @param runtime Bound normal Command bus callbacks.
2944
- * @param session Fenced record containing saved Commands.
2945
- * @param index Command position in the saved completion.
2946
- * @returns Completion after the Command is acknowledged or already delivered.
2947
- */
2948
- async deliverCommand(runtime, session, index) {
2949
- const output = session.record().completion?.commands[index];
2950
- if (output === undefined || output.delivered)
2951
- return;
2952
- const command = output.command;
2953
- if (command === undefined)
2954
- throw new Error("Agent outgoing Command has no original envelope.");
2955
- const key = this.outputKey(output);
2956
- const plan = output.plan ?? (await runtime.prepareSavedCommand(command));
2957
- if (output.plan === undefined) {
2958
- await session.update((current) => {
2959
- const pending = current.completion?.commands[index];
2960
- if (pending === undefined ||
2961
- pending.plan !== undefined ||
2962
- pending.command?.id?.uuid !== command.id?.uuid)
2963
- throw new Error("Saved Agent Command changed before plan installation.");
2964
- pending.plan = plan;
2965
- return current;
2966
- });
2967
- }
2968
- await runtime.publishSavedCommand(command, plan);
2969
- await session.markDelivered([key]);
2970
- runtime.recordAcceptedSaved(command);
2971
- },
2324
+ return selected;
2325
+ }
2972
2326
  /**
2973
- * Returns the typed original ID of one completed outgoing Command.
2327
+ * Captures the configured live invocation deadline.
2974
2328
  *
2975
- * @param output Prepared outgoing Command.
2976
- * @returns Original durable Command ID.
2329
+ * @param ai Configured AI capability.
2330
+ * @returns The sampled live invocation deadline, when configured.
2977
2331
  */
2978
- outputKey(output) {
2979
- if (output.command?.id !== undefined)
2980
- return output.command.id;
2981
- throw new Error("Agent outgoing Command requires its original typed ID.");
2982
- },
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
+ }
2983
2341
  /**
2984
- * Validates every stored binding against the current generated selectors.
2342
+ * Applies only the narrower per-repository bounds to the shared registry policy.
2985
2343
  *
2986
- * @param repository Agent registration.
2987
- * @param accepted Immutable source and ordered bindings.
2988
- * @returns Current matching handler metadata.
2344
+ * @param ai Configured AI capability.
2345
+ * @returns The current configured limits for the start audit.
2989
2346
  */
2990
- handlers(repository, accepted) {
2991
- const routing = repositoryRoutings.get(repository);
2992
- if (routing === undefined)
2993
- throw new Error("Agent execution requires registered handlers.");
2994
- const current = this.selectedHandlers(routing, accepted);
2995
- if (accepted.handlers.length !== current.length || current.length === 0)
2996
- throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent handler binding changed before replay.");
2997
- for (const [ordinal, handler] of current.entries()) {
2998
- const binding = accepted.handlers[ordinal];
2999
- if (binding === undefined || !AgentAdmission.matchesHandler(binding, handler, ordinal))
3000
- throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent handler binding changed before replay.");
3001
- }
3002
- return current;
3003
- },
3004
- /**
3005
- * Finds current Command or Event handlers from the immutable source envelope.
3006
- *
3007
- * @param routing Registered generated handler routing metadata.
3008
- * @param accepted Durable original signal and saved handler bindings.
3009
- * @returns Generated handlers selected for the accepted signal.
3010
- */
3011
- selectedHandlers(routing, accepted) {
3012
- if (accepted.signal.case === "command") {
3013
- const binding = accepted.handlers[0];
3014
- const current = routing.commandReadiness?.findCommandAssignee(binding?.signalType ?? "");
3015
- return current === undefined ? [] : [current.registeredHandler];
3016
- }
3017
- if (accepted.signal.case !== "event")
3018
- throw new Error("Agent source envelope is missing.");
3019
- const event = accepted.signal.value;
3020
- const packed = EntityInvocation.requireSignalMessage(event.message, "event");
3021
- const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
3022
- const value = EntityInvocation.unpackRequired(packed, schema, "event");
3023
- const external = event.context?.external === true;
3024
- return [
3025
- ...routing.eventReactors(schema.typeName, value, external),
3026
- ...routing.commandReactions(schema.typeName, value, external),
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",
3027
2359
  ];
3028
- },
3029
- /**
3030
- * Records start facts and the original dispatch System Event before handler execution.
3031
- *
3032
- * @param repository Agent registration.
3033
- * @param runtime Bound services.
3034
- * @param session Fenced provider claim and current record.
3035
- * @param loaded Restored Entity and initial Version.
3036
- * @param entityId Original typed recipient.
3037
- * @param tenantId Accepted delivery tenant when applicable.
3038
- * @returns Updated record image for exact subsequent CAS.
3039
- */
3040
- async start(repository, runtime, session, loaded, entityId, tenantId) {
3041
- const accepted = session.record().accepted;
3042
- if (accepted === undefined)
3043
- throw new Error("Agent start requires accepted work.");
3044
- const started = this.startFacts(repository, runtime, accepted, loaded);
3045
- const audit = this.startAudit(runtime, repository, accepted, entityId);
3046
- await session.update((current) => {
3047
- current.started = started;
3048
- return current;
3049
- }, [this.startHistory(audit)]);
3050
- if (accepted.signal.case === "command")
3051
- HandlerDispatchPublisher.publishCommand(runtime, audit);
3052
- else
3053
- HandlerDispatchPublisher.publishReactor(runtime, audit);
3054
- return this.selectMissingModels(repository, runtime, session, accepted, tenantId);
3055
- },
3056
- /**
3057
- * Stores the original dispatch audit envelope in mandatory Agent history.
3058
- *
3059
- * @param audit Original Agent System event envelope.
3060
- * @returns Typed Agent history row for the original dispatch Event.
3061
- */
3062
- startHistory(audit) {
3063
- const occurredAt = audit.context?.timestamp;
3064
- if (occurredAt === undefined)
3065
- throw new Error("Agent dispatch audit requires its timestamp.");
3066
- return create(AgentHistoryEntrySchema, {
3067
- occurredAt,
3068
- item: { case: "systemEvent", value: audit },
3069
- });
3070
- },
3071
- /**
3072
- * Verifies admission revisions and fixes whole-invocation limits before execution.
3073
- *
3074
- * @param repository Registered Entity repository for this operation.
3075
- * @param runtime Bounded Context storage and publication services.
3076
- * @param accepted Durable original signal and saved handler bindings.
3077
- * @param loaded Loaded Entity and its repository storage state.
3078
- * @returns Durable deadline, initial version, and whole-invocation bounds.
3079
- */
3080
- startFacts(repository, runtime, accepted, loaded) {
3081
- const configuration = repositoryAccess.agentConfiguration(repository);
3082
- const ai = runtime.ai;
3083
- if (accepted.key === undefined || ai === undefined || configuration.ai === undefined)
3084
- throw new Error("Agent execution start requires accepted work and AI policy.");
3085
- const limits = registryOptions(ai).invocationLimits ?? {};
3086
- return create(AgentExecutionStartSchema, {
3087
- ...(limits.deadlineMs === undefined
3088
- ? {}
3089
- : { deadline: addMillis(Time.currentTime(), limits.deadlineMs) }),
3090
- initialVersion: RepositoryEntities.priorVersion(loaded.current),
3091
- bounds: this.invocationBounds(limits),
3092
- });
3093
- },
3094
- /**
3095
- * Persists each model kind without a saved connection identity.
3096
- *
3097
- * @param repository Agent registration and permitted capabilities.
3098
- * @param runtime Bound AI registry.
3099
- * @param session Current fenced execution record.
3100
- * @param accepted Original accepted signal and actor.
3101
- * @param tenantId Accepted tenant when applicable.
3102
- * @returns Record after each missing identity has been saved.
3103
- */
3104
- async selectMissingModels(repository, runtime, session, accepted, tenantId) {
3105
- const ai = runtime.ai;
3106
- const configuration = repositoryAccess.agentConfiguration(repository).ai;
3107
- if (ai === undefined || configuration === undefined)
3108
- throw new Error("Agent model selection requires saved start facts and AI policy.");
3109
- const deadlineMs = this.savedModelDeadline(session, "selection");
3110
- const source = accepted.signal.value?.message;
3111
- for (const kind of ["generation", "decision"]) {
3112
- if (!configuration.models.some((model) => model.definition.kind === kind))
3113
- continue;
3114
- const modelKind = AgentModelSelection.kind(kind);
3115
- if (session.record().started?.models.some((model) => model.kind === modelKind))
3116
- continue;
3117
- const selected = await AgentModelSelection.selectKind(ai, configuration, AgentModelSelection.scope(accepted, repository.stateSchema, tenantId), session.initialPreferences(), session.signal, deadlineMs, kind, source);
3118
- await this.saveSelectedModel(session, selected);
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;
3119
2366
  }
3120
- return session.record();
3121
- },
2367
+ return limits;
2368
+ }
3122
2369
  /**
3123
- * Writes one selected model under the current Agent claim.
2370
+ * Binds one original System audit writer to retained Agent history.
3124
2371
  *
3125
- * @param session Fenced Agent execution session.
3126
- * @param selected Authorized deployment and connection identity.
3127
- * @returns When the selection is durable.
2372
+ * @param loaded Current Entity and retained storage for this turn.
2373
+ * @returns The history writer bound to this Agent and source.
3128
2374
  */
3129
- async saveSelectedModel(session, selected) {
3130
- await session.update((record) => {
3131
- if (record.started === undefined)
3132
- throw new Error("Agent start facts disappeared.");
3133
- record.started.models.push(selected);
3134
- return record;
3135
- });
3136
- },
3137
- /**
3138
- * Rejects accepted work when its exact handler and AI policy changed.
3139
- *
3140
- * @param repository Registered Entity repository for this operation.
3141
- * @param accepted Durable original signal and saved handler bindings.
3142
- * @param handlers Generated handlers in their saved order.
3143
- * @param ai Configured AI registry for this Bounded Context.
3144
- * @param configuration Repository and AI policy used for revision checks.
3145
- */
3146
- assertRevisions(repository, accepted, handlers, ai, configuration) {
3147
- if (configuration.ai === undefined)
3148
- throw new Error("Agent AI policy is required.");
3149
- const revisions = AgentRevisions.atAdmission(repository.stateSchema, handlers, ai, configuration.ai);
3150
- if (accepted.codeRevision?.value !== configuration.codeRevision ||
3151
- accepted.schemaRevision?.value !== revisions.schema ||
3152
- accepted.policyRevision?.value !== revisions.policy)
3153
- throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent code, schema, or policy changed before execution.");
3154
- },
3155
- /**
3156
- * Copies the whole-invocation budget into its durable integer fields.
3157
- *
3158
- * @param limits Configured whole-invocation limits.
3159
- * @returns Whole-transition limits persisted at invocation start.
3160
- */
3161
- invocationBounds(limits) {
3162
- return create(AgentInvocationBoundsSchema, {
3163
- ...(limits.operations === undefined ? {} : { operations: BigInt(limits.operations) }),
3164
- ...(limits.modelRequests === undefined
3165
- ? {}
3166
- : { modelRequests: BigInt(limits.modelRequests) }),
3167
- ...(limits.toolCalls === undefined ? {} : { toolCalls: BigInt(limits.toolCalls) }),
3168
- ...(limits.recordedReads === undefined
3169
- ? {}
3170
- : { recordedReads: BigInt(limits.recordedReads) }),
3171
- ...(limits.deadlineMs === undefined ? {} : { deadlineMillis: BigInt(limits.deadlineMs) }),
3172
- ...(limits.totalInputBytes === undefined
3173
- ? {}
3174
- : { totalInputBytes: BigInt(limits.totalInputBytes) }),
3175
- ...(limits.totalOutputBytes === undefined
3176
- ? {}
3177
- : { totalOutputBytes: BigInt(limits.totalOutputBytes) }),
3178
- ...(limits.maxRecoveryBytes === undefined
3179
- ? {}
3180
- : { maxRecoveryBytes: BigInt(limits.maxRecoveryBytes) }),
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();
3181
2383
  });
3182
- },
2384
+ }
3183
2385
  /**
3184
- * Creates a genuine source-specific dispatch System Event before the handler.
2386
+ * Writes the genuine dispatch System row before publishing its original Event.
3185
2387
  *
3186
- * @param runtime Bounded Context storage and publication services.
3187
- * @param repository Registered Entity repository for this operation.
3188
- * @param accepted Durable original signal and saved handler bindings.
3189
- * @param entityId Typed recipient Entity identifier.
3190
- * @returns Original dispatch System Event envelope.
2388
+ * @param loaded Current Entity and retained storage for this turn.
2389
+ * @returns Completion after the original dispatch System Event is saved.
3191
2390
  */
3192
- startAudit(runtime, repository, accepted, entityId) {
3193
- const source = accepted.signal;
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();
3194
2404
  if (source.case === "command")
3195
- return HandlerDispatchPublisher.commandEvent(runtime, repository, source.value, entityId);
3196
- if (source.case === "event")
3197
- return HandlerDispatchPublisher.reactorEvent(runtime, repository, source.value, entityId);
3198
- throw new Error("Agent start requires its accepted envelope.");
3199
- },
2405
+ HandlerDispatchPublisher.publishCommand(this.runtime, event);
2406
+ else
2407
+ HandlerDispatchPublisher.publishReactor(this.runtime, event);
2408
+ }
3200
2409
  /**
3201
- * Invokes saved Command or Event bindings inside one Entity transaction.
2410
+ * Executes current generated handlers inside the Entity's local transaction.
3202
2411
  *
3203
- * @param entity Restored Agent instance.
3204
- * @param accepted Original accepted signal.
3205
- * @param handlers Validated current handler bindings.
3206
- * @param repository Registered Entity repository for this operation.
3207
- * @param runtime Bounded Context storage and publication services.
3208
- * @param tenantId Tenant selected for this accepted signal.
3209
- * @param session Fenced Agent execution session.
3210
- * @returns Outputs returned by the selected generated handlers.
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.
3211
2418
  */
3212
- async invoke(repository, runtime, tenantId, session, entity, accepted, handlers) {
3213
- transactionalEntityAccess.start(entity);
2419
+ async invoke(loaded, selected, preferences, audit, budget) {
2420
+ transactionalEntityAccess.start(loaded.entity);
3214
2421
  try {
3215
- const signals = [];
3216
- for (const [ordinal, handler] of handlers.entries()) {
3217
- signals.push(...(await this.invokeBoundHandler(repository, runtime, tenantId, session, entity, accepted, handler, ordinal)));
3218
- }
3219
- await this.commitInvokedEntity(entity);
3220
- return signals;
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;
3221
2429
  }
3222
2430
  catch (error) {
3223
- transactionalEntityAccess.rollback(entity);
2431
+ transactionalEntityAccess.rollback(loaded.entity);
3224
2432
  throw error;
3225
2433
  }
3226
- },
2434
+ }
3227
2435
  /**
3228
- * Validates the transaction result before Agent output completion.
2436
+ * Executes one handler with live AI and read facades scoped to its returned work.
3229
2437
  *
3230
- * @param entity Loaded recipient Entity instance.
3231
- * @returns When the fenced repository operation completes.
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.
3232
2445
  */
3233
- async commitInvokedEntity(entity) {
3234
- const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
3235
- if (commit.status === "rejected")
3236
- throw new TransitionValidationError(commit.validation.error);
3237
- },
3238
- /**
3239
- * Binds named AI and history reads around one generated handler call.
3240
- *
3241
- * @param repository Registered Entity repository for this operation.
3242
- * @param runtime Bounded Context storage and publication services.
3243
- * @param tenantId Tenant selected for this accepted signal.
3244
- * @param session Fenced Agent execution session.
3245
- * @param entity Loaded recipient Entity instance.
3246
- * @param accepted Durable original signal and saved handler bindings.
3247
- * @param handler Generated handler selected for this signal.
3248
- * @param ordinal Saved position of the selected handler.
3249
- * @returns Outputs returned by one generated handler.
3250
- */
3251
- async invokeBoundHandler(repository, runtime, tenantId, session, entity, accepted, handler, ordinal) {
3252
- const facade = this.handlerFacade(repository, runtime, tenantId, session, accepted, ordinal);
3253
- const reads = new AgentReadRuntime(session, ordinal);
3254
- const release = this.bindHandlerCapabilities(entity, runtime, tenantId, accepted, reads, facade);
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);
3255
2451
  try {
3256
- const deadlineEpochMs = this.handlerDeadline(session);
3257
- const signals = await this.awaitHandler(this.invokeHandler(entity, accepted, handler), session.signal, deadlineEpochMs);
2452
+ const output = await AgentLiveHandler.awaitHandler(this.callHandler(loaded.entity, handler), this.signal, deadline);
3258
2453
  reads.finish();
3259
2454
  facade.finish();
3260
- return signals;
2455
+ return output;
3261
2456
  }
3262
2457
  finally {
3263
2458
  facade.close();
3264
2459
  release();
3265
2460
  }
3266
- },
2461
+ }
3267
2462
  /**
3268
- * Binds Agent AI, indexed history and routed projection queries for one handler.
2463
+ * Creates live AI and actor capabilities to one generated handler.
3269
2464
  *
3270
- * @param entity Loaded recipient Entity instance.
3271
- * @param runtime Bounded Context storage and publication services.
3272
- * @param tenantId Tenant selected for this accepted signal.
3273
- * @param accepted Durable original signal and saved handler bindings.
3274
- * @param reads Handler-scoped durable read facade.
3275
- * @param facade Handler-scoped AI facade.
3276
- * @returns Release callback for handler-scoped AI and read facades.
3277
- */
3278
- bindHandlerCapabilities(entity, runtime, tenantId, accepted, reads, facade) {
3279
- const releaseHistory = AgentHistoryReads.withJournal(entity, (scope, view, request, live, maxBytes) => reads.history(scope, view, request, live, maxBytes));
3280
- let releaseQuery;
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.
2470
+ */
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
+ }
2482
+ /**
2483
+ * Prepares a per-kind selection without changing the observed preference image.
2484
+ *
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.
2488
+ */
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
+ }
2496
+ /**
2497
+ * Binds current protected history, query, and AI capabilities to one handler.
2498
+ *
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.
2504
+ */
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;
3281
2508
  try {
3282
- releaseQuery = ProcessManagerQueries.bind(entity, runtime, accepted.actor, tenantId, (schema, query, live) => reads.query(schema, query, live));
3283
- const releaseAi = AgentAiBindings.bind(entity, facade);
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);
3284
2511
  return () => {
3285
- releaseQuery?.();
3286
- releaseHistory();
3287
- releaseAi();
2512
+ ai();
2513
+ query?.();
2514
+ history();
3288
2515
  };
3289
- }
3290
- catch (error) {
3291
- releaseQuery?.();
3292
- releaseHistory();
3293
- throw error;
3294
- }
3295
- },
3296
- /**
3297
- * Creates one handler-scoped facade from its accepted model policy.
3298
- *
3299
- * @param repository Registered Entity repository for this operation.
3300
- * @param runtime Bounded Context storage and publication services.
3301
- * @param tenantId Tenant selected for this accepted signal.
3302
- * @param session Fenced Agent execution session.
3303
- * @param accepted Durable original signal and saved handler bindings.
3304
- * @param ordinal Saved position of the selected handler.
3305
- * @returns AI facade bound to one selected handler.
3306
- */
3307
- handlerFacade(repository, runtime, tenantId, session, accepted, ordinal) {
3308
- const ai = runtime.ai;
3309
- const configuration = repositoryAccess.agentConfiguration(repository).ai;
3310
- if (ai === undefined || configuration === undefined)
3311
- throw new Error("Agent handler requires its bound AI registry.");
3312
- return new AgentAiRuntime(ai, configuration, AgentModelSelection.scope(accepted, repository.stateSchema, tenantId), session, ordinal, new AgentInteractionAudit(session, accepted, repository.stateSchema, runtime.signalMetadata, runtime.registerSystemEventSchema, runtime.publishAgentSystemEvent));
3313
- },
2516
+ }
2517
+ catch (error) {
2518
+ query?.();
2519
+ history();
2520
+ throw error;
2521
+ }
2522
+ }
3314
2523
  /**
3315
- * Invokes one selected generated handler and validates its declared results.
2524
+ * Invokes and validates one declared generated Command or Event handler.
3316
2525
  *
3317
- * @param entity Loaded recipient Entity instance.
3318
- * @param accepted Durable original signal and saved handler bindings.
3319
- * @param handler Generated handler selected for this signal.
3320
- * @returns Declared outputs returned by one generated handler.
2526
+ * @param entity Current Entity instance.
2527
+ * @param handler Selected generated handler.
2528
+ * @returns The validated generated handler result.
3321
2529
  */
3322
- async invokeHandler(entity, accepted, handler) {
3323
- const source = accepted.signal;
3324
- if (source.case !== "command" && source.case !== "event")
3325
- throw new Error("Agent source envelope is missing.");
2530
+ async callHandler(entity, handler) {
2531
+ const source = this.invocation.source;
3326
2532
  const kind = source.case;
3327
2533
  const message = EntityInvocation.unpackRequired(EntityInvocation.requireSignalMessage(source.value.message, kind), handler.handler.schema, kind);
3328
- const context = source.case === "command"
2534
+ const context = kind === "command"
3329
2535
  ? EntityInvocation.commandHandlerContext(source.value)
3330
2536
  : EntityInvocation.eventHandlerContext(source.value);
3331
2537
  const produced = await EntityInvocation.invokeEntityMethod(entity, handler.handler.methodName, message, handler.handler.parameterCount, context);
3332
- const signals = produced === undefined
2538
+ const outputs = produced === undefined
3333
2539
  ? []
3334
2540
  : Array.isArray(produced)
3335
2541
  ? Array.from(produced)
3336
2542
  : [produced];
3337
- RepositoryHandlers.requireDeclaredOutputs(handler.handler, signals);
3338
- return signals;
3339
- },
2543
+ RepositoryHandlers.requireDeclaredOutputs(handler.handler, outputs);
2544
+ return outputs;
2545
+ }
3340
2546
  /**
3341
- * Reads the original invocation deadline before starting an application callback.
2547
+ * Commits the observed state/preferences image and its final audit atomically.
3342
2548
  *
3343
- * @param session Current fenced execution session.
3344
- * @returns Absolute saved deadline in milliseconds.
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.
3345
2554
  */
3346
- handlerDeadline(session) {
3347
- const record = session.record();
3348
- const deadline = record.started?.deadline;
3349
- if (deadline === undefined && record.started?.bounds?.deadlineMillis !== undefined)
3350
- throw new Error("Agent handler requires its saved deadline.");
3351
- if (session.signal.aborted || this.deadlineExpired(record))
3352
- throw new Error("Agent handler deadline or claim ended.");
3353
- return deadline === undefined
3354
- ? undefined
3355
- : Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000);
3356
- },
2555
+ async complete(loaded, expected, preferences, produced) {
2556
+ const version = RepositoryEntities.repositoryVersion(loaded.entity);
2557
+ const routing = repositoryRoutings.get(this.repository);
2558
+ if (routing === undefined)
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)),
2569
+ ];
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
+ }
3357
2576
  /**
3358
- * Awaits an application handler within its saved deadline and current claim.
2577
+ * Applies final conditional state and preference mutation under the observed Version.
3359
2578
  *
3360
- * @typeParam Value Handler result.
3361
- * @param work Already-started generated handler invocation.
3362
- * @param signal Session cancellation when claim authority ends.
3363
- * @param deadlineEpochMs Original saved invocation deadline.
3364
- * @returns Handler result only while its claim and deadline remain valid.
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.
3365
2584
  */
3366
- async awaitHandler(work, signal, deadlineEpochMs) {
3367
- let timer;
3368
- let abort = () => undefined;
3369
- const cutoff = new Promise((_resolve, reject) => {
3370
- abort = () => {
3371
- reject(new Error("Agent handler claim ended."));
3372
- };
3373
- const tick = () => {
3374
- if (deadlineEpochMs === undefined)
3375
- return;
3376
- const remaining = deadlineEpochMs - Time.currentTimeMillis();
3377
- if (remaining <= 0)
3378
- reject(new Error("Agent handler deadline expired."));
3379
- else
3380
- timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
3381
- };
3382
- signal.addEventListener("abort", abort, { once: true });
3383
- if (signal.aborted)
3384
- abort();
3385
- else
3386
- tick();
3387
- });
2585
+ async commit(loaded, expected, preferences, history) {
2586
+ const deferred = await this.deferredUpdate(loaded);
3388
2587
  try {
3389
- const result = await Promise.race([work, cutoff]);
3390
- if (signal.aborted ||
3391
- (deadlineEpochMs !== undefined && Time.currentTimeMillis() >= deadlineEpochMs))
3392
- throw new Error("Agent handler deadline or claim ended.");
3393
- return result;
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();
3394
2601
  }
3395
- finally {
3396
- clearTimeout(timer);
3397
- signal.removeEventListener("abort", abort);
2602
+ catch (error) {
2603
+ deferred?.cancel();
2604
+ throw error;
3398
2605
  }
3399
- },
3400
- };
3401
- /**
3402
- * Adds a positive timeout to a Time-sampled Protobuf timestamp.
3403
- *
3404
- * @param at Provider-independent current time.
3405
- * @param millis Bounded additional milliseconds.
3406
- * @returns Absolute timestamp retaining nanosecond precision.
3407
- */
3408
- function addMillis(at, millis) {
3409
- const nanos = at.nanos + (millis % 1000) * 1_000_000;
3410
- return {
3411
- ...at,
3412
- seconds: at.seconds + BigInt(Math.floor(millis / 1000)) + BigInt(Math.floor(nanos / 1_000_000_000)),
3413
- nanos: nanos % 1_000_000_000,
3414
- };
2606
+ }
2607
+ /**
2608
+ * Creates current state and its enabled history only for a changed Entity.
2609
+ *
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.
2613
+ */
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
+ }
2623
+ /**
2624
+ * Prepares the changed Entity state for publication only after atomic commit.
2625
+ *
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
+ }
2634
+ /**
2635
+ * Checks authorization of effective changed selections before final persistence.
2636
+ *
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));
2657
+ }
2658
+ }
2659
+ /**
2660
+ * Returns the explicit model reference for one kind, or inherited absence.
2661
+ *
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
+ }
2670
+ /**
2671
+ * Creates original selection System Events for changed committed preferences.
2672
+ *
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.
2676
+ */
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
+ }
2691
+ /**
2692
+ * Creates one original committed model-selection System Event.
2693
+ *
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.
2699
+ */
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
+ }
2712
+ /**
2713
+ * Wraps one committed original System Event in indexed history.
2714
+ *
2715
+ * @param event Original typed Event envelope.
2716
+ * @returns The indexed history entry for the original System Event.
2717
+ */
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 },
2725
+ });
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);
2740
+ }
2741
+ }
3415
2742
  }
3416
2743
  /**
3417
2744
  * Executes one Process Manager Command against its loaded Entity state.
@@ -3604,12 +2931,11 @@ class ProcessManagerCommandExecution {
3604
2931
  const produced = await EntityInvocation.invokeEntityMethod(entity, assignee.handler.methodName, message, assignee.handler.parameterCount, EntityInvocation.commandHandlerContext(this.#command));
3605
2932
  const signals = this.#support.normalizeProducedSignals(produced);
3606
2933
  if (signals.length === 0)
3607
- throw new Error("Repository command handlers must return a signal.");
2934
+ throw receptionObservation.recordPostHandlerFailure(new Error("Repository command handlers must return a signal."));
3608
2935
  RepositoryHandlers.requireDeclaredOutputs(assignee.handler, signals);
3609
2936
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, assignee.handler.kind === "command-assignment" && signals.length > 0));
3610
- if (commit.status === "rejected") {
3611
- throw new TransitionValidationError(commit.validation.error);
3612
- }
2937
+ if (commit.status === "rejected")
2938
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
3613
2939
  return signals;
3614
2940
  }
3615
2941
  catch (error) {
@@ -3647,7 +2973,7 @@ class ProcessManagerCommandExecution {
3647
2973
  });
3648
2974
  return create(EventSchema, {
3649
2975
  id: metadata.id,
3650
- message: AnyMessages.pack(schema, signal),
2976
+ message: RepositorySignals.packProduced(schema, signal),
3651
2977
  context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId, version),
3652
2978
  });
3653
2979
  }
@@ -3667,7 +2993,7 @@ class ProcessManagerCommandExecution {
3667
2993
  const metadata = this.#runtime.signalMetadata.commandFromCommand(this.#command);
3668
2994
  return create(CommandSchema, {
3669
2995
  id: metadata.id,
3670
- message: AnyMessages.pack(schema, signal),
2996
+ message: RepositorySignals.packProduced(schema, signal),
3671
2997
  context: metadata.context,
3672
2998
  });
3673
2999
  }));
@@ -3781,7 +3107,7 @@ class ProcessManagerEventExecution {
3781
3107
  *
3782
3108
  * @param entityId Target Entity identifier.
3783
3109
  * @param intake Decoded Event and matching handlers.
3784
- * @returns Completion after accepted results are published.
3110
+ * @returns Completion after accepted results are registered for publication.
3785
3111
  */
3786
3112
  async #executeEntity(entityId, intake) {
3787
3113
  const tenantOptions = RepositoryTenants.standTenantOptions(this.#runtime.context, this.#event);
@@ -3798,7 +3124,7 @@ class ProcessManagerEventExecution {
3798
3124
  return;
3799
3125
  this.#publishChangedState(loaded, entityId);
3800
3126
  this.#postEvents(events);
3801
- await this.#postCommands(commands);
3127
+ this.#postCommands(commands);
3802
3128
  }
3803
3129
  /**
3804
3130
  * Publishes a state-change System Event when the Entity changed.
@@ -3914,7 +3240,7 @@ class ProcessManagerEventExecution {
3914
3240
  async #commitEntity(entity, producedEvents) {
3915
3241
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, producedEvents));
3916
3242
  if (commit.status === "rejected")
3917
- throw new TransitionValidationError(commit.validation.error);
3243
+ throw EntityInvocation.rejectedTransition(commit.validation.error);
3918
3244
  }
3919
3245
  /**
3920
3246
  * Binds reactor results as Entity Events.
@@ -3946,7 +3272,7 @@ class ProcessManagerEventExecution {
3946
3272
  });
3947
3273
  return create(EventSchema, {
3948
3274
  id: metadata.id,
3949
- message: AnyMessages.pack(schema, signal),
3275
+ message: RepositorySignals.packProduced(schema, signal),
3950
3276
  context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId, version),
3951
3277
  });
3952
3278
  }
@@ -3966,7 +3292,7 @@ class ProcessManagerEventExecution {
3966
3292
  const metadata = this.#runtime.signalMetadata.commandFromEvent(this.#event);
3967
3293
  return create(CommandSchema, {
3968
3294
  id: metadata.id,
3969
- message: AnyMessages.pack(schema, signal),
3295
+ message: RepositorySignals.packProduced(schema, signal),
3970
3296
  context: metadata.context,
3971
3297
  });
3972
3298
  }));
@@ -3975,11 +3301,11 @@ class ProcessManagerEventExecution {
3975
3301
  * Publishes each produced Command in handler order.
3976
3302
  *
3977
3303
  * @param commands Bound Commands to publish.
3978
- * @returns Completion after all Commands are published.
3979
3304
  */
3980
- async #postCommands(commands) {
3305
+ #postCommands(commands) {
3981
3306
  for (const command of commands) {
3982
- await this.#runtime.publisher.publishCommand(command);
3307
+ // spine-log-boundary: server.process_manager_command_follow_up
3308
+ void this.#runtime.publisher.publishCommand(command);
3983
3309
  }
3984
3310
  }
3985
3311
  /**
@@ -4284,6 +3610,15 @@ Object.freeze(RepositoryIdentity);
4284
3610
  * Internal entity invocation operations.
4285
3611
  */
4286
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
+ },
4287
3622
  /**
4288
3623
  * Reads a generated message's type name or rejects malformed output.
4289
3624
  *
@@ -4361,7 +3696,7 @@ const EntityInvocation = {
4361
3696
  * @returns Decoded generated message.
4362
3697
  */
4363
3698
  unpackRequired(message, schema, signalKind) {
4364
- const unpacked = AnyMessages.unpack(message, schema);
3699
+ const unpacked = AnyPacker.tryUnpack(message, schema);
4365
3700
  if (unpacked === undefined) {
4366
3701
  throw new Error(`Repository ${signalKind} execution requires a readable message.`);
4367
3702
  }
@@ -4399,9 +3734,9 @@ const RepositoryEntities = {
4399
3734
  bindStorage(repository, runtime, tenantId, entityId, entity) {
4400
3735
  const storageInput = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
4401
3736
  const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, storageInput);
4402
- RepositoryHistoryInternals.bindEntityHistory(entity, storage, entityId, repository.stateSchema);
3737
+ RepositoryHistoryInternals.bindEntityHistory(repository, entity, storage, entityId, repository.stateSchema, storageInput.context.tenantId);
4403
3738
  if (repository.entityFamily === "agent")
4404
- this.bindAgentHistory(entity, entityId, storageInput, storage.agentHistory);
3739
+ this.bindAgentHistory(repository, entity, entityId, storageInput, storage.agentHistory);
4405
3740
  return {
4406
3741
  commits: storage.commits,
4407
3742
  events: storage.events,
@@ -4412,17 +3747,26 @@ const RepositoryEntities = {
4412
3747
  /**
4413
3748
  * Attaches the indexed history port to one restored Agent.
4414
3749
  *
3750
+ * @param repository Repository tracking the captured runtime.
4415
3751
  * @param entity Restored Agent instance.
4416
3752
  * @param entityId Typed Agent identifier.
4417
3753
  * @param input Entity storage scope.
4418
3754
  * @param history Mandatory Agent history port.
4419
3755
  */
4420
- bindAgentHistory(entity, entityId, input, history) {
3756
+ bindAgentHistory(repository, entity, entityId, input, history) {
4421
3757
  if (history === undefined)
4422
3758
  throw new Error("Agent history storage is required.");
3759
+ const lifetime = RepositoryStorage.lifetime(repository);
3760
+ const runtime = repositoryRuntimes.get(repository);
4423
3761
  AgentHistoryReads.bind(entity, {
4424
3762
  storage: history,
4425
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
+ },
4426
3770
  scope: {
4427
3771
  context: input.context.name,
4428
3772
  tenant: JSON.stringify(input.context.tenantId ?? null),
@@ -4535,6 +3879,34 @@ Object.freeze(RepositoryEntities);
4535
3879
  * Internal repository signals operations.
4536
3880
  */
4537
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
+ },
4538
3910
  /**
4539
3911
  * Validates that a rejection type was declared by the invoked handler.
4540
3912
  *
@@ -4622,7 +3994,7 @@ const RepositorySignals = {
4622
3994
  const metadata = runtime.signalMetadata.eventFromCommand(command, {});
4623
3995
  return create(EventSchema, {
4624
3996
  id: metadata.id,
4625
- message: AnyMessages.pack(rejection.schema, rejection.messageThrown()),
3997
+ message: RepositorySignals.packValidated(rejection.schema, rejection.messageThrown()),
4626
3998
  context: create(EventContextSchema, {
4627
3999
  ...RepositorySignals.eventContextWithProducer(metadata.context, repository, entityId),
4628
4000
  rejection: create(RejectionEventContextSchema, {
@@ -4686,7 +4058,7 @@ class EntityStateChangePublishing {
4686
4058
  this.#publish(runtime, () => runtime.signalMetadata.eventFromCommand(command, {
4687
4059
  version,
4688
4060
  }), {
4689
- id: AnyMessages.pack(CommandIdSchema, command.id),
4061
+ id: RepositorySignals.packValidated(CommandIdSchema, command.id),
4690
4062
  typeUrl: command.message?.typeUrl ?? "",
4691
4063
  }, { repository, entityId, oldState, oldLifecycle, newState, lifecycle, version });
4692
4064
  }
@@ -4707,7 +4079,7 @@ class EntityStateChangePublishing {
4707
4079
  this.#publish(runtime, () => runtime.signalMetadata.eventFromEvent(source, {
4708
4080
  version,
4709
4081
  }), {
4710
- id: AnyMessages.pack(EventIdSchema, source.id),
4082
+ id: RepositorySignals.packValidated(EventIdSchema, source.id),
4711
4083
  typeUrl: source.message?.typeUrl ?? "",
4712
4084
  }, { repository, entityId, oldState, oldLifecycle, newState, lifecycle, version });
4713
4085
  }
@@ -4726,7 +4098,7 @@ class EntityStateChangePublishing {
4726
4098
  runtime.registerSystemEventSchema(draft.schema);
4727
4099
  const event = create(EventSchema, {
4728
4100
  id: metadata.id,
4729
- message: AnyMessages.pack(draft.schema, draft.messageAt(metadata.context.timestamp)),
4101
+ message: RepositorySignals.packValidated(draft.schema, draft.messageAt(metadata.context.timestamp)),
4730
4102
  context: RepositorySignals.eventContextWithProducer(metadata.context, change.repository, change.entityId),
4731
4103
  });
4732
4104
  this.#post(runtime, event);
@@ -4760,7 +4132,7 @@ class EntityStateChangePublishing {
4760
4132
  return {
4761
4133
  entity,
4762
4134
  signalId: [create(MessageIdSchema, { id: origin.id, typeUrl: origin.typeUrl })],
4763
- state: AnyMessages.pack(change.repository.stateSchema, change.newState),
4135
+ state: RepositorySignals.packValidated(change.repository.stateSchema, change.newState),
4764
4136
  version: create(VersionSchema, { number: change.version }),
4765
4137
  };
4766
4138
  }
@@ -4803,7 +4175,7 @@ class EntityStateChangePublishing {
4803
4175
  ...(change.oldState === undefined
4804
4176
  ? {}
4805
4177
  : {
4806
- oldState: AnyMessages.pack(change.repository.stateSchema, change.oldState),
4178
+ oldState: RepositorySignals.packValidated(change.repository.stateSchema, change.oldState),
4807
4179
  }),
4808
4180
  newState: fields.state,
4809
4181
  signalId: fields.signalId,
@@ -4968,7 +4340,7 @@ class HandlerDispatchPublishing {
4968
4340
  });
4969
4341
  return create(EventSchema, {
4970
4342
  id: runtime.signalMetadata.eventId(),
4971
- 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)),
4972
4344
  context,
4973
4345
  });
4974
4346
  }
@@ -5018,7 +4390,7 @@ class HandlerDispatchPublishing {
5018
4390
  });
5019
4391
  return create(EventSchema, {
5020
4392
  id: runtime.signalMetadata.eventId(),
5021
- 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)),
5022
4394
  context,
5023
4395
  });
5024
4396
  }
@@ -5047,7 +4419,7 @@ class HandlerDispatchPublishing {
5047
4419
  });
5048
4420
  const diagnostic = create(EventSchema, {
5049
4421
  id: runtime.signalMetadata.eventId(),
5050
- 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)),
5051
4423
  context,
5052
4424
  });
5053
4425
  this.#post(runtime, schema, diagnostic);
@@ -5136,6 +4508,7 @@ const HandlerDispatchPublisher = Object.freeze(new HandlerDispatchPublishing());
5136
4508
  Object.freeze(RepositorySignals);
5137
4509
  /**
5138
4510
  * Internal repository stand operations.
4511
+ *
5139
4512
  * @internal
5140
4513
  */
5141
4514
  export const RepositoryStand = {
@@ -5168,60 +4541,12 @@ export const RepositoryStand = {
5168
4541
  lifecycle,
5169
4542
  });
5170
4543
  },
5171
- /**
5172
- * Maps one Event position to its Process Manager producer version.
5173
- *
5174
- * @param sequence One-based position among produced Events.
5175
- * @returns Producer sequence number for Event metadata.
5176
- */
5177
4544
  };
5178
4545
  Object.freeze(RepositoryStand);
5179
4546
  /**
5180
4547
  * Internal repository tenants operations.
5181
4548
  */
5182
4549
  const RepositoryTenants = {
5183
- /**
5184
- * Builds tenant-aware storage context for Entity inbox delivery.
5185
- *
5186
- * @param context Bounded context storage mode.
5187
- * @param tenantId Delivery tenant when multitenant.
5188
- * @returns Storage context for durable Entity inbox rows.
5189
- */
5190
- entityInboxDeliveryContext(context, tenantId) {
5191
- if (!context.multitenant) {
5192
- return Object.freeze({ name: context.name, multitenant: false });
5193
- }
5194
- const tid = tenantId;
5195
- if (tid === undefined) {
5196
- throw new Error(`Multitenant Entity Inbox handoff for "${context.name}" requires tenantId.`);
5197
- }
5198
- return Object.freeze({
5199
- name: context.name,
5200
- multitenant: true,
5201
- tenantId: RepositoryTenants.require(tid),
5202
- });
5203
- },
5204
- /**
5205
- * Builds tenant-aware storage context for Projection inbox delivery.
5206
- *
5207
- * @param context Bounded context storage mode.
5208
- * @param tenantId Delivery tenant when multitenant.
5209
- * @returns Storage context for durable Projection inbox rows.
5210
- */
5211
- projectionDeliveryContext(context, tenantId) {
5212
- if (!context.multitenant) {
5213
- return Object.freeze({ name: context.name, multitenant: false });
5214
- }
5215
- const tid = tenantId;
5216
- if (tid === undefined) {
5217
- throw new Error(`Multitenant projection inbox handoff for "${context.name}" requires tenantId.`);
5218
- }
5219
- return Object.freeze({
5220
- name: context.name,
5221
- multitenant: true,
5222
- tenantId: RepositoryTenants.require(tid),
5223
- });
5224
- },
5225
4550
  /**
5226
4551
  * Validates an Event tenant before Projection inbox handoff.
5227
4552
  *
@@ -5428,9 +4753,15 @@ const RepositoryHandlers = {
5428
4753
  requireDeclaredOutputs(handler, signals) {
5429
4754
  const schemas = HandlerMetadataValues.returnedSchemas(handler);
5430
4755
  for (const signal of signals) {
5431
- 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
+ }
5432
4763
  if (!schemas.some((schema) => schema.typeName === typeName)) {
5433
- throw new Error(`Handler "${handler.methodName}" returned undeclared message "${typeName}".`);
4764
+ throw receptionObservation.recordPostHandlerFailure(new Error(`Handler "${handler.methodName}" returned undeclared message "${typeName}".`));
5434
4765
  }
5435
4766
  }
5436
4767
  },
@@ -5971,7 +5302,7 @@ const RepositoryRoutes = {
5971
5302
  * @returns Candidate target Entity ID.
5972
5303
  */
5973
5304
  async callCommandRoute(route, message, schema, context, reads) {
5974
- const unpacked = AnyMessages.unpack(message, schema);
5305
+ const unpacked = AnyPacker.tryUnpack(message, schema);
5975
5306
  if (unpacked === undefined) {
5976
5307
  throw new Error("Repository command routing requires a readable Command message.");
5977
5308
  }
@@ -6135,7 +5466,7 @@ const RepositoryRoutes = {
6135
5466
  if (message?.typeUrl !== TypeUrls.derive(EntityLog.EntityStateChangedSchema)) {
6136
5467
  throw new Error(`${operation} requires an EntityStateChanged System event.`);
6137
5468
  }
6138
- const changed = AnyMessages.unpack(message, EntityLog.EntityStateChangedSchema);
5469
+ const changed = AnyPacker.tryUnpack(message, EntityLog.EntityStateChangedSchema);
6139
5470
  const packedState = changed?.newState;
6140
5471
  if (packedState === undefined || packedState.typeUrl === "") {
6141
5472
  throw new Error(`${operation} requires EntityStateChanged.newState.`);
@@ -6143,7 +5474,7 @@ const RepositoryRoutes = {
6143
5474
  const schema = schemas.find((candidate) => TypeUrls.derive(candidate) === packedState.typeUrl);
6144
5475
  if (schema === undefined)
6145
5476
  return undefined;
6146
- const state = AnyMessages.unpack(packedState, schema);
5477
+ const state = AnyPacker.tryUnpack(packedState, schema);
6147
5478
  if (state === undefined)
6148
5479
  throw new Error(`${operation} requires a readable Entity state.`);
6149
5480
  return Object.freeze({ schema, state });
@@ -6186,7 +5517,7 @@ const RepositoryRoutes = {
6186
5517
  * @returns Validated target Entity IDs.
6187
5518
  */
6188
5519
  async callEventRoute(route, message, schema, context, targetIdField, reads) {
6189
- const unpacked = AnyMessages.unpack(message, schema);
5520
+ const unpacked = AnyPacker.tryUnpack(message, schema);
6190
5521
  if (unpacked === undefined) {
6191
5522
  throw new Error("Repository event routing requires a readable Event message.");
6192
5523
  }
@@ -6240,7 +5571,7 @@ const RepositoryRoutes = {
6240
5571
  * @returns Nonempty first-field value.
6241
5572
  */
6242
5573
  readFirstFieldId(message, schema, signalKind) {
6243
- const unpacked = AnyMessages.unpack(message, schema);
5574
+ const unpacked = AnyPacker.tryUnpack(message, schema);
6244
5575
  const firstField = schema.fields[0];
6245
5576
  if (unpacked === undefined || firstField === undefined) {
6246
5577
  throw new Error(`Repository ${signalKind} routing requires a readable first field.`);
@@ -6479,12 +5810,235 @@ const RepositoryRoutes = {
6479
5810
  return undefined;
6480
5811
  }
6481
5812
  },
6482
- };
6483
- Object.freeze(RepositoryRoutes);
6484
- /**
6485
- * Internal repository storage operations.
6486
- */
6487
- const RepositoryStorage = {
5813
+ };
5814
+ Object.freeze(RepositoryRoutes);
5815
+ /**
5816
+ * Internal repository storage operations.
5817
+ */
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
+ },
6488
6042
  /**
6489
6043
  * Opens provider Entity storage for one read scope without a commit handle.
6490
6044
  *
@@ -6501,72 +6055,120 @@ const RepositoryStorage = {
6501
6055
  return candidate.createEntityStorage(input);
6502
6056
  },
6503
6057
  /**
6504
- * Opens Entity storage and an atomic commit port from a storage factory.
6058
+ * Returns the canonical tenant/model/history bucket for Entity storage.
6505
6059
  *
6506
- * @typeParam I Entity identifier type.
6507
- * @typeParam S Generated Entity state type.
6508
- * @param factory Provider storage factory.
6509
- * @param input Entity storage location and schema.
6510
- * @param agent Whether to open mandatory Agent history.
6511
- * @returns Storage ports closed together by one handle.
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.
6512
6065
  */
6513
- openEntityStorage(factory, input, agent = false) {
6514
- const candidate = factory;
6515
- if (candidate.createEntityStorage === undefined)
6516
- throw new Error("StorageFactory does not provide required atomic Entity commit storage.");
6517
- const entity = candidate.createEntityStorage(input);
6518
- const commits = EntityCommitStorageFactories.create(factory, input);
6519
- let ports;
6520
- try {
6521
- if (agent)
6522
- ports = this.openAgentPorts(factory, input);
6523
- }
6524
- catch (error) {
6525
- commits.close();
6526
- entity.close();
6527
- throw error;
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);
6528
6076
  }
6529
- return {
6530
- ...entity,
6531
- commits,
6532
- ...ports,
6533
- close: () => {
6534
- ports?.agentExecution.close();
6535
- ports?.agentHistory.close();
6536
- commits.close();
6537
- entity.close();
6538
- },
6539
- };
6077
+ return bucket;
6540
6078
  },
6541
6079
  /**
6542
- * Opens both mandatory Agent ports and closes the first if the second fails.
6080
+ * Returns the canonical tenant/model bucket for Agent audit history.
6543
6081
  *
6544
6082
  * @typeParam I Typed Agent identifier.
6545
- * @typeParam S Generated Agent state message.
6546
- * @param factory Storage factory registered with the Bounded Context.
6547
- * @param input Original Inbox or storage input.
6548
- * @returns Indexed history and execution handles for the same tenant.
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.
6099
+ *
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.
6549
6106
  */
6550
- openAgentPorts(factory, input) {
6551
- const agentHistory = AgentHistoryStorageFactories.create(factory, {
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;
6118
+ },
6119
+ /**
6120
+ * Returns shared Agent audit history for one tenant and model.
6121
+ *
6122
+ * @typeParam I Typed Agent identifier.
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, {
6552
6137
  context: input.context,
6553
6138
  stateType: input.sourceType.typeName,
6554
6139
  id: { key: input.id.key },
6555
6140
  });
6556
- try {
6557
- const agentExecution = AgentExecutionStorageFactories.create(factory, {
6558
- entity: input,
6559
- stateType: input.sourceType.typeName,
6560
- });
6561
- return { agentHistory, agentExecution };
6562
- }
6563
- catch (error) {
6564
- agentHistory.close();
6565
- throw error;
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
+ }
6566
6163
  }
6164
+ if (errors.length === 1)
6165
+ throw errors[0];
6166
+ if (errors.length > 1)
6167
+ throw new AggregateError(errors, "Entity storage cleanup failed.");
6567
6168
  },
6568
6169
  /**
6569
- * 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.
6570
6172
  *
6571
6173
  * @typeParam I Entity identifier type.
6572
6174
  * @typeParam S Generated Entity state type.
@@ -6576,20 +6178,72 @@ const RepositoryStorage = {
6576
6178
  * @returns Shared Entity storage handle.
6577
6179
  */
6578
6180
  openRepositoryEntityStorage(repository, factory, input) {
6579
- const key = JSON.stringify({ context: input.context, state: input.sourceType.typeName });
6580
- let handles = repositoryEntityHandles.get(repository);
6581
- const handle = RepositoryStorage.openEntityStorage(factory, input, repository.entityFamily === "agent");
6582
- if (handles === undefined) {
6583
- handles = new Map();
6584
- repositoryEntityHandles.set(repository, handles);
6585
- }
6586
- const existing = handles.get(key);
6587
- if (existing !== undefined) {
6588
- handle.close();
6589
- return existing;
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;
6590
6226
  }
6591
- handles.set(key, handle);
6592
- 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);
6593
6247
  },
6594
6248
  /**
6595
6249
  * Builds Entity storage input with configured state and Event history flags.
@@ -6599,12 +6253,15 @@ const RepositoryStorage = {
6599
6253
  * @returns Entity storage description for the provider.
6600
6254
  */
6601
6255
  entityStorageInput(repository, context) {
6602
- 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);
6603
6260
  const history = RepositoryStorage.historyConfiguration(repository);
6604
6261
  return {
6605
6262
  ...descriptor,
6606
6263
  stateHistory: history.stateHistory,
6607
- eventHistory: repository.entityFamily === "aggregate" || history.processManagerEventHistory,
6264
+ eventHistory: snapshot.entityFamily === "aggregate" || history.processManagerEventHistory,
6608
6265
  };
6609
6266
  },
6610
6267
  /**
@@ -6659,23 +6316,129 @@ const RepositoryHistoryInternals = {
6659
6316
  /**
6660
6317
  * Binds state and Event history readers to one restored Entity instance.
6661
6318
  *
6319
+ * @param repository Registered repository admitting history reads.
6662
6320
  * @param entity Instance receiving the history accessors.
6663
6321
  * @param storage Entity storage used for reads and maintenance.
6664
6322
  * @param entityId Identifier used to select history records.
6665
6323
  * @param schema Schema used to unpack state records.
6324
+ * @param tenantId Captured tenant for later live membership checks.
6666
6325
  */
6667
- bindEntityHistory(entity, storage, entityId, schema) {
6668
- const stateCache = RepositoryHistoryInternals.createHistoryCache((depth, startingFromVersion) => storage.states.backward(entityId, depth, startingFromVersion), (record) => BigInt(EntityRecords.unpack(schema, record).versionMessage.number), { requireDescendingVersions: true });
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);
6669
6335
  entityStateHistoryCaches.set(entity, stateCache);
6670
- const eventCache = RepositoryHistoryInternals.createHistoryCache((depth, startingFromVersion) => storage.events.backward(entityId, depth, startingFromVersion), (event) => RepositorySignals.readEventVersion(event), { cacheCompleteVersionGroups: true });
6671
6336
  entityHistoryAccess.bind(entity, {
6672
- stateAt: async (time) => RepositoryHistoryInternals.cloneHistoryState(await storage.states.stateAt(entityId, time)),
6673
- states: async (depth) => RepositoryHistoryInternals.freezeHistoryStates((await stateCache.read(depth)).map((record) => EntityRecords.unpack(schema, record).state)),
6674
- events: async (depth) => RepositoryHistoryInternals.freezeHistoryEvents(await eventCache.read(depth)),
6675
- stateMaintenance: storage.states,
6676
- eventMaintenance: storage.events,
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),
6677
6351
  });
6678
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
+ },
6679
6442
  /**
6680
6443
  * Creates a history cache that serializes reads and extends prior results.
6681
6444
  *
@@ -7063,14 +6826,11 @@ const DispatchGuards = {
7063
6826
  */
7064
6827
  async wasDispatched(repository, runtime, event, entityId, journalEventId, depth) {
7065
6828
  const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForEvent(runtime.context, event));
7066
- const storage = RepositoryStorage.openEntityStorage(runtime.storageFactory, input);
7067
- try {
6829
+ return RepositoryStorage.track(repository, async () => {
6830
+ const storage = RepositoryStorage.repositoryReadEntityStorage(repository, runtime.storageFactory, input);
7068
6831
  const persisted = await storage.events.backward(entityId, depth);
7069
6832
  return persisted.some((candidate) => candidate.id?.value === journalEventId);
7070
- }
7071
- finally {
7072
- storage.close();
7073
- }
6833
+ });
7074
6834
  },
7075
6835
  /**
7076
6836
  * Records a successful delivery and discards the oldest excess identifiers.
@@ -7228,7 +6988,7 @@ const InboxMessages = {
7228
6988
  if (message.label !== "HANDLE_COMMAND") {
7229
6989
  throw new Error(`Entity Inbox replay does not handle "${message.label}" messages.`);
7230
6990
  }
7231
- const command = message.signal === undefined ? undefined : AnyMessages.unpack(message.signal, CommandSchema);
6991
+ const command = message.signal === undefined ? undefined : AnyPacker.tryUnpack(message.signal, CommandSchema);
7232
6992
  if (command === undefined) {
7233
6993
  throw CommandValidationError.invalidPayload();
7234
6994
  }
@@ -7271,7 +7031,7 @@ const InboxMessages = {
7271
7031
  typeUrl: message.signal.typeUrl,
7272
7032
  value: new Uint8Array(message.signal.value),
7273
7033
  });
7274
- const event = signal === undefined ? undefined : AnyMessages.unpack(signal, EventSchema);
7034
+ const event = signal === undefined ? undefined : AnyPacker.tryUnpack(signal, EventSchema);
7275
7035
  if (event === undefined) {
7276
7036
  throw new Error(unreadableMessage);
7277
7037
  }
@@ -7368,7 +7128,7 @@ const InboxReplay = {
7368
7128
  const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
7369
7129
  if (assignee === undefined)
7370
7130
  throw new Error("Agent Command has no selected assignee.");
7371
- await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityId, { kind: "command", value: command }, command.context?.actorContext ?? create(ActorContextSchema), [assignee.registeredHandler]);
7131
+ await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityId, { case: "command", value: command }, [assignee.registeredHandler]);
7372
7132
  },
7373
7133
  /**
7374
7134
  * Admits matching Event handlers from the stored target without rerouting.
@@ -7388,7 +7148,7 @@ const InboxReplay = {
7388
7148
  const handlers = InboxReplay.agentEventHandlers(routing, route.messageFullTypeName, event);
7389
7149
  if (handlers.length === 0)
7390
7150
  return;
7391
- 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);
7392
7152
  },
7393
7153
  /**
7394
7154
  * Restores selected Event and Command reactions from the original payload.
@@ -7417,64 +7177,154 @@ const InboxReplay = {
7417
7177
  * @param tenantId Selected delivery tenant.
7418
7178
  * @param entityId Typed target from the stored route.
7419
7179
  * @param signal Original source envelope.
7420
- * @param actor Original actor metadata.
7421
7180
  * @param handlers Selected handler metadata in execution order.
7422
7181
  * @returns When the fenced repository operation completes.
7423
7182
  */
7424
- async admitAgent(repository, runtime, message, tenantId, entityId, signal, actor, handlers) {
7425
- const revisions = InboxReplay.agentRevisions(repository, runtime, handlers);
7426
- const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
7427
- const execution = InboxReplay.agentExecutionHandle(repository, runtime, input);
7428
- await execution.admit(AgentAdmission.create({
7429
- stateType: repository.stateSchema.typeName,
7430
- agentKey: input.id.key(entityId),
7431
- recipientId: message.inboxId.targetId,
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,
7432
7204
  signal,
7433
- actor,
7434
- inbox: message,
7435
7205
  handlers,
7436
- codeRevision: revisions.codeRevision,
7437
- schemaRevision: revisions.schema,
7438
- policyRevision: revisions.policy,
7439
- }));
7440
- runtime.wakeAcceptedAgent(repository, tenantId);
7206
+ reservation,
7207
+ });
7441
7208
  },
7442
7209
  /**
7443
- * Opens the actual tenant's mandatory execution handle.
7210
+ * Serializes the complete local Agent identity for one capacity slot.
7444
7211
  *
7445
- * @typeParam I Typed Agent identifier.
7446
- * @typeParam S Generated Agent state message.
7447
- * @param repository Registered Entity repository for this operation.
7448
- * @param runtime Bounded Context storage and publication services.
7449
- * @param input Original Inbox or storage input.
7450
- * @returns Fenced execution handle for the accepted tenant.
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.
7451
7216
  */
7452
- agentExecutionHandle(repository, runtime, input) {
7453
- const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
7454
- if (storage.agentExecution === undefined)
7455
- throw new Error("Agent Inbox replay requires durable execution storage.");
7456
- return storage.agentExecution;
7217
+ agentCapacityKey(context, stateType, id) {
7218
+ return JSON.stringify([context.name, String(TenantBoundary.of(context).key), stateType, id]);
7457
7219
  },
7458
7220
  /**
7459
- * Records the registered code, schema, and AI policy before Inbox handoff.
7221
+ * Builds a capacity key for one repository entity in the selected tenant.
7460
7222
  *
7461
- * @param repository Registered Entity repository for this operation.
7462
- * @param runtime Bounded Context storage and publication services.
7463
- * @param handlers Generated handlers in their saved order.
7464
- * @returns Pinned code, schema, and AI policy digests.
7465
- */
7466
- agentRevisions(repository, runtime, handlers) {
7467
- const ai = runtime.ai;
7468
- const configuration = repositoryAccess.agentConfiguration(repository);
7469
- if (ai === undefined ||
7470
- configuration.ai === undefined ||
7471
- configuration.codeRevision === undefined)
7472
- throw new Error("Agent Inbox replay requires registered AI capabilities and revisions.");
7473
- return {
7474
- codeRevision: configuration.codeRevision,
7475
- ...AgentRevisions.atAdmission(repository.stateSchema, handlers, ai, configuration.ai),
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();
7476
7286
  };
7477
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
+ },
7478
7328
  /**
7479
7329
  * Dispatches a stored Process Manager Command after tenant and payload validation.
7480
7330
  *
@@ -7583,7 +7433,7 @@ const InboxReplay = {
7583
7433
  validateReplayedCommandPayload(routing, command) {
7584
7434
  const commandMessage = EntityInvocation.requireSignalMessage(command.message, "command");
7585
7435
  const commandSchema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, commandMessage.typeUrl, "command");
7586
- const payload = AnyMessages.unpack(commandMessage, commandSchema);
7436
+ const payload = AnyPacker.tryUnpack(commandMessage, commandSchema);
7587
7437
  if (payload === undefined) {
7588
7438
  throw CommandValidationError.invalidPayload();
7589
7439
  }
@@ -7611,7 +7461,7 @@ const InboxReplay = {
7611
7461
  validateReplayedEventPayload(routing, event, invalidPayloadMessage = "Projection inbox replay requires a readable event payload.") {
7612
7462
  const eventMessage = EntityInvocation.requireSignalMessage(event.message, "event");
7613
7463
  const eventSchema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, eventMessage.typeUrl, "event");
7614
- const payload = AnyMessages.unpack(eventMessage, eventSchema);
7464
+ const payload = AnyPacker.tryUnpack(eventMessage, eventSchema);
7615
7465
  if (payload === undefined) {
7616
7466
  throw new Error(invalidPayloadMessage);
7617
7467
  }
@@ -7792,18 +7642,14 @@ const InboxHandoff = {
7792
7642
  const commandId = RepositorySignals.requireCommandId(command);
7793
7643
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7794
7644
  const deliveryTenantId = RepositoryTenants.requireCommandTenant(runtime.context, command);
7795
- const delivery = new Delivery({
7796
- context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
7797
- storageFactory: runtime.storageFactory,
7798
- strategy: runtime.entityInbox.strategy(),
7799
- });
7645
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7800
7646
  await runtime.entityInbox.receive(delivery, {
7801
7647
  inboxId: {
7802
7648
  targetId: InboxMessages.inboxTargetId(route.entityId, repository.idField),
7803
7649
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
7804
7650
  },
7805
7651
  signalId: commandId.uuid,
7806
- signal: AnyMessages.pack(CommandSchema, command, { validate: false }),
7652
+ signal: AnyPacker.pack(CommandSchema, command),
7807
7653
  label: "HANDLE_COMMAND",
7808
7654
  status: "TO_DELIVER",
7809
7655
  keepUntil,
@@ -7822,17 +7668,14 @@ const InboxHandoff = {
7822
7668
  const eventId = RepositorySignals.requireEventId(event);
7823
7669
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7824
7670
  const deliveryTenantId = RepositoryTenants.requireProjectionTenant(runtime.context, event);
7825
- const delivery = new Delivery({
7826
- context: RepositoryTenants.projectionDeliveryContext(runtime.context, deliveryTenantId),
7827
- storageFactory: runtime.storageFactory,
7828
- });
7671
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7829
7672
  await runtime.projectionInbox.receive(delivery, {
7830
7673
  inboxId: {
7831
7674
  targetId: InboxMessages.inboxTargetId(entityId, repository.idField),
7832
7675
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
7833
7676
  },
7834
7677
  signalId: eventId.value,
7835
- signal: AnyMessages.pack(EventSchema, event, { validate: false }),
7678
+ signal: AnyPacker.pack(EventSchema, event),
7836
7679
  label: "UPDATE_SUBSCRIBER",
7837
7680
  status: "TO_DELIVER",
7838
7681
  shard: ShardIndex.single(),
@@ -7852,11 +7695,7 @@ const InboxHandoff = {
7852
7695
  const eventId = RepositorySignals.requireEventId(event);
7853
7696
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7854
7697
  const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
7855
- const delivery = new Delivery({
7856
- context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
7857
- storageFactory: runtime.storageFactory,
7858
- strategy: runtime.entityInbox.strategy(),
7859
- });
7698
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7860
7699
  await runtime.entityInbox.receive(delivery, InboxHandoff.pmEventInboxInput(repository, eventId.value, event, entityId, keepUntil), deliveryTenantId);
7861
7700
  },
7862
7701
  /**
@@ -7872,11 +7711,7 @@ const InboxHandoff = {
7872
7711
  const eventId = RepositorySignals.requireEventId(event);
7873
7712
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
7874
7713
  const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
7875
- const delivery = new Delivery({
7876
- context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
7877
- storageFactory: runtime.storageFactory,
7878
- strategy: runtime.entityInbox.strategy(),
7879
- });
7714
+ const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
7880
7715
  for (let start = 0; start < entityIds.length; start += 1_000) {
7881
7716
  const inputs = entityIds
7882
7717
  .slice(start, start + 1_000)
@@ -7901,7 +7736,7 @@ const InboxHandoff = {
7901
7736
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
7902
7737
  },
7903
7738
  signalId,
7904
- signal: AnyMessages.pack(EventSchema, event, { validate: false }),
7739
+ signal: AnyPacker.pack(EventSchema, event),
7905
7740
  label: "REACT_UPON_EVENT",
7906
7741
  status: "TO_DELIVER",
7907
7742
  keepUntil,
@@ -7909,182 +7744,6 @@ const InboxHandoff = {
7909
7744
  },
7910
7745
  };
7911
7746
  Object.freeze(InboxHandoff);
7912
- /**
7913
- * Rebinds generated repository recipients without repeating accepted route queries.
7914
- */
7915
- const SavedRepositoryDispatch = {
7916
- /**
7917
- * Maps a generated repository family to its serialized binding value.
7918
- *
7919
- * @param repository Registered Entity repository for this operation.
7920
- * @returns Repository family encoded in a saved dispatch target.
7921
- */
7922
- family(repository) {
7923
- switch (repository.entityFamily) {
7924
- case "aggregate":
7925
- return AgentSavedRepositoryFamily.AGENT_SAVED_AGGREGATE;
7926
- case "projection":
7927
- return AgentSavedRepositoryFamily.AGENT_SAVED_PROJECTION;
7928
- case "process-manager":
7929
- return AgentSavedRepositoryFamily.AGENT_SAVED_PROCESS_MANAGER;
7930
- case "agent":
7931
- return AgentSavedRepositoryFamily.AGENT_SAVED_AGENT;
7932
- }
7933
- },
7934
- /**
7935
- * Finds the current generated handlers that determine an Event handoff.
7936
- *
7937
- * @param routing Registered generated handler routing metadata.
7938
- * @param event Original typed Event envelope.
7939
- * @returns Generated handlers matching the outgoing Event.
7940
- */
7941
- eventHandlers(routing, event) {
7942
- const packed = EntityInvocation.requireSignalMessage(event.message, "event");
7943
- const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
7944
- const value = EntityInvocation.unpackRequired(packed, schema, "event");
7945
- const external = event.context?.external === true;
7946
- return [
7947
- ...routing.eventReactors(schema.typeName, value, external),
7948
- ...routing.commandReactions(schema.typeName, value, external),
7949
- ...routing
7950
- .eventSubscribers(schema.typeName, value, external)
7951
- .map((entry) => entry.registeredHandler),
7952
- ];
7953
- },
7954
- /**
7955
- * Calculates a digest of ordered generated metadata and message descriptors.
7956
- *
7957
- * @param repository Registered Entity repository for this operation.
7958
- * @param handlers Generated handlers in their saved order.
7959
- * @returns Digest of ordered generated handler metadata.
7960
- */
7961
- fingerprint(repository, handlers) {
7962
- const selected = handlers.map(({ entity, handler }, ordinal) => ({
7963
- ordinal,
7964
- receiver: entity.schema.typeName,
7965
- kind: handler.kind,
7966
- method: handler.methodName,
7967
- signal: handler.messageFullTypeName,
7968
- origin: handler.origin,
7969
- parameters: handler.parameterCount,
7970
- where: handler.where === undefined ? null : [handler.where.eventField, handler.where.equals],
7971
- descriptor: Buffer.from(toBinary(FileDescriptorProtoSchema, handler.schema.file.proto)).toString("base64"),
7972
- }));
7973
- const state = Buffer.from(toBinary(FileDescriptorProtoSchema, repository.stateSchema.file.proto));
7974
- return createHash("sha256").update(state).update(JSON.stringify(selected)).digest("hex");
7975
- },
7976
- /**
7977
- * Restores a typed ID only when its canonical wire representation is unchanged.
7978
- *
7979
- * @param repository Registered Entity repository for this operation.
7980
- * @param packed Original typed Protobuf payload.
7981
- * @returns Typed recipient identifier from the frozen route.
7982
- */
7983
- recipient(repository, packed) {
7984
- const id = InboxMessages.targetEntityId(packed, repository.idField);
7985
- if (!equals(AnySchema, InboxMessages.inboxTargetId(id, repository.idField), packed))
7986
- throw new Error("Saved Agent output recipient ID is not canonical for its repository.");
7987
- return id;
7988
- },
7989
- /**
7990
- * Checks the stable repository and signal identity before saved transport.
7991
- *
7992
- * @param repository Registered Entity repository for this operation.
7993
- * @param target Frozen recipient binding.
7994
- * @param kind Generation or decision model kind.
7995
- * @returns Whether the checked condition holds.
7996
- */
7997
- matches(repository, target, kind) {
7998
- return (target.kind === kind &&
7999
- target.receiverStateType === repository.stateSchema.typeName &&
8000
- target.repositoryFamily === SavedRepositoryDispatch.family(repository));
8001
- },
8002
- /**
8003
- * Prepares one typed Command recipient with its selected generated assignee.
8004
- *
8005
- * @param repository Registered Entity repository for this operation.
8006
- * @param routing Registered generated handler routing metadata.
8007
- * @param command Original typed Command envelope.
8008
- * @returns Validated frozen Command recipient target.
8009
- */
8010
- async prepareCommand(repository, routing, command) {
8011
- const route = await repository.routeCommand(command);
8012
- const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
8013
- if (assignee === undefined)
8014
- throw new Error("Saved Agent Command has no generated assignee.");
8015
- return create(SavedTargetSchema, {
8016
- kind: AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND,
8017
- receiverStateType: repository.stateSchema.typeName,
8018
- repositoryFamily: SavedRepositoryDispatch.family(repository),
8019
- signalType: route.messageFullTypeName,
8020
- bindingFingerprint: SavedRepositoryDispatch.fingerprint(repository, [
8021
- assignee.registeredHandler,
8022
- ]),
8023
- recipients: [InboxMessages.inboxTargetId(route.entityId, repository.idField)],
8024
- });
8025
- },
8026
- /**
8027
- * Resolves the original Command recipient without calling its route again.
8028
- *
8029
- * @param repository Registered Entity repository for this operation.
8030
- * @param routing Registered generated handler routing metadata.
8031
- * @param command Original typed Command envelope.
8032
- * @param target Frozen recipient binding.
8033
- * @returns When the fenced repository operation completes.
8034
- */
8035
- bindCommand(repository, routing, command, target) {
8036
- const { runtime, signalType, recipient } = SavedRepositoryDispatch.requireCommandBinding(repository, routing, command, target);
8037
- const entityId = SavedRepositoryDispatch.recipient(repository, recipient);
8038
- const route = {
8039
- entityId,
8040
- messageFullTypeName: signalType,
8041
- invocation: "deferred",
8042
- };
8043
- return Promise.resolve(() => InboxHandoff.handoffEntityCommand(repository, runtime, command, route));
8044
- },
8045
- /**
8046
- * Checks saved Command metadata, typed target presence, and runtime registration.
8047
- *
8048
- * @param repository Registered Entity repository for this operation.
8049
- * @param routing Registered generated handler routing metadata.
8050
- * @param command Original typed Command envelope.
8051
- * @param target Frozen recipient binding.
8052
- * @returns Registered Command route and original recipient.
8053
- */
8054
- requireCommandBinding(repository, routing, command, target) {
8055
- const packed = EntityInvocation.requireSignalMessage(command.message, "command");
8056
- const schema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, packed.typeUrl, "command");
8057
- const assignee = routing.commandReadiness?.findCommandAssignee(schema.typeName);
8058
- const fingerprint = assignee === undefined
8059
- ? ""
8060
- : SavedRepositoryDispatch.fingerprint(repository, [assignee.registeredHandler]);
8061
- const recipient = target.recipients[0];
8062
- const runtime = repositoryRuntimes.get(repository);
8063
- if (!SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND) ||
8064
- target.signalType !== schema.typeName ||
8065
- target.bindingFingerprint !== fingerprint ||
8066
- target.recipients.length !== 1 ||
8067
- recipient === undefined ||
8068
- runtime === undefined)
8069
- throw new Error("Saved Agent Command repository binding changed before delivery.");
8070
- return { runtime, signalType: schema.typeName, recipient };
8071
- },
8072
- /**
8073
- * Creates one internal Command binding from the repository registration.
8074
- *
8075
- * @param repository Registered Entity repository for this operation.
8076
- * @param routing Registered generated handler routing metadata.
8077
- * @returns Saved Command binding for this registration.
8078
- */
8079
- commandBinding(repository, routing) {
8080
- return {
8081
- prepare: (command) => SavedRepositoryDispatch.prepareCommand(repository, routing, command),
8082
- matches: (target) => SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND),
8083
- bind: (command, target) => SavedRepositoryDispatch.bindCommand(repository, routing, command, target),
8084
- };
8085
- },
8086
- };
8087
- Object.freeze(SavedRepositoryDispatch);
8088
7747
  /**
8089
7748
  * Connects bus dispatchers and Inbox replay to repository execution.
8090
7749
  */
@@ -8119,7 +7778,7 @@ const RepositoryDispatch = {
8119
7778
  messageSchemas: () => routing.commandSchemas,
8120
7779
  dispatch: (command) => RepositoryDispatch.dispatchRepositoryCommand(repository, routing, command),
8121
7780
  });
8122
- return SavedDispatcherBindings.command(dispatcher, SavedRepositoryDispatch.commandBinding(repository, routing));
7781
+ return dispatcher;
8123
7782
  },
8124
7783
  /**
8125
7784
  * Creates an Event dispatcher that preserves routes computed during acceptance.