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

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 (199) hide show
  1. package/README.md +104 -7
  2. package/REFERENCE.md +272 -31
  3. package/dist/agent/agent-admission.d.ts +80 -0
  4. package/dist/agent/agent-admission.d.ts.map +1 -0
  5. package/dist/agent/agent-admission.js +127 -0
  6. package/dist/agent/agent-admission.js.map +1 -0
  7. package/dist/agent/agent-ai-binding.d.ts +24 -0
  8. package/dist/agent/agent-ai-binding.d.ts.map +1 -0
  9. package/dist/agent/agent-ai-binding.js +48 -0
  10. package/dist/agent/agent-ai-binding.js.map +1 -0
  11. package/dist/agent/agent-ai-runtime.d.ts +76 -0
  12. package/dist/agent/agent-ai-runtime.d.ts.map +1 -0
  13. package/dist/agent/agent-ai-runtime.js +1269 -0
  14. package/dist/agent/agent-ai-runtime.js.map +1 -0
  15. package/dist/agent/agent-execution-capacity.d.ts +43 -0
  16. package/dist/agent/agent-execution-capacity.d.ts.map +1 -0
  17. package/dist/agent/agent-execution-capacity.js +147 -0
  18. package/dist/agent/agent-execution-capacity.js.map +1 -0
  19. package/dist/agent/agent-execution-fault.d.ts +18 -0
  20. package/dist/agent/agent-execution-fault.d.ts.map +1 -0
  21. package/dist/agent/agent-execution-fault.js +31 -0
  22. package/dist/agent/agent-execution-fault.js.map +1 -0
  23. package/dist/agent/agent-execution-session.d.ts +103 -0
  24. package/dist/agent/agent-execution-session.d.ts.map +1 -0
  25. package/dist/agent/agent-execution-session.js +238 -0
  26. package/dist/agent/agent-execution-session.js.map +1 -0
  27. package/dist/agent/agent-history.d.ts +148 -0
  28. package/dist/agent/agent-history.d.ts.map +1 -0
  29. package/dist/agent/agent-history.js +340 -0
  30. package/dist/agent/agent-history.js.map +1 -0
  31. package/dist/agent/agent-interaction-audit.d.ts +58 -0
  32. package/dist/agent/agent-interaction-audit.d.ts.map +1 -0
  33. package/dist/agent/agent-interaction-audit.js +114 -0
  34. package/dist/agent/agent-interaction-audit.js.map +1 -0
  35. package/dist/agent/agent-interaction-events.d.ts +40 -0
  36. package/dist/agent/agent-interaction-events.d.ts.map +1 -0
  37. package/dist/agent/agent-interaction-events.js +129 -0
  38. package/dist/agent/agent-interaction-events.js.map +1 -0
  39. package/dist/agent/agent-mcp-budget.d.ts +125 -0
  40. package/dist/agent/agent-mcp-budget.d.ts.map +1 -0
  41. package/dist/agent/agent-mcp-budget.js +566 -0
  42. package/dist/agent/agent-mcp-budget.js.map +1 -0
  43. package/dist/agent/agent-mcp-host.d.ts +125 -0
  44. package/dist/agent/agent-mcp-host.d.ts.map +1 -0
  45. package/dist/agent/agent-mcp-host.js +15 -0
  46. package/dist/agent/agent-mcp-host.js.map +1 -0
  47. package/dist/agent/agent-mcp-runtime.d.ts +199 -0
  48. package/dist/agent/agent-mcp-runtime.d.ts.map +1 -0
  49. package/dist/agent/agent-mcp-runtime.js +621 -0
  50. package/dist/agent/agent-mcp-runtime.js.map +1 -0
  51. package/dist/agent/agent-model-selection.d.ts +129 -0
  52. package/dist/agent/agent-model-selection.d.ts.map +1 -0
  53. package/dist/agent/agent-model-selection.js +342 -0
  54. package/dist/agent/agent-model-selection.js.map +1 -0
  55. package/dist/agent/agent-read-runtime.d.ts +70 -0
  56. package/dist/agent/agent-read-runtime.d.ts.map +1 -0
  57. package/dist/agent/agent-read-runtime.js +320 -0
  58. package/dist/agent/agent-read-runtime.js.map +1 -0
  59. package/dist/agent/agent-revisions.d.ts +35 -0
  60. package/dist/agent/agent-revisions.d.ts.map +1 -0
  61. package/dist/agent/agent-revisions.js +133 -0
  62. package/dist/agent/agent-revisions.js.map +1 -0
  63. package/dist/agent/agent-scheduler.d.ts +99 -0
  64. package/dist/agent/agent-scheduler.d.ts.map +1 -0
  65. package/dist/agent/agent-scheduler.js +398 -0
  66. package/dist/agent/agent-scheduler.js.map +1 -0
  67. package/dist/bus/command-bus.d.ts +62 -0
  68. package/dist/bus/command-bus.d.ts.map +1 -1
  69. package/dist/bus/command-bus.js +51 -2
  70. package/dist/bus/command-bus.js.map +1 -1
  71. package/dist/bus/event-bus.d.ts +101 -2
  72. package/dist/bus/event-bus.d.ts.map +1 -1
  73. package/dist/bus/event-bus.js +74 -13
  74. package/dist/bus/event-bus.js.map +1 -1
  75. package/dist/bus/saved-dispatcher-binding.d.ts +53 -0
  76. package/dist/bus/saved-dispatcher-binding.d.ts.map +1 -0
  77. package/dist/bus/saved-dispatcher-binding.js +27 -0
  78. package/dist/bus/saved-dispatcher-binding.js.map +1 -0
  79. package/dist/context/bounded-context.d.ts +45 -12
  80. package/dist/context/bounded-context.d.ts.map +1 -1
  81. package/dist/context/bounded-context.js +306 -86
  82. package/dist/context/bounded-context.js.map +1 -1
  83. package/dist/context/entity-inbox.d.ts.map +1 -1
  84. package/dist/context/entity-inbox.js +21 -7
  85. package/dist/context/entity-inbox.js.map +1 -1
  86. package/dist/context/local-inbox-handoff.d.ts +1 -0
  87. package/dist/context/local-inbox-handoff.d.ts.map +1 -1
  88. package/dist/context/local-inbox-handoff.js +31 -1
  89. package/dist/context/local-inbox-handoff.js.map +1 -1
  90. package/dist/context/projection-handoff.d.ts.map +1 -1
  91. package/dist/context/projection-handoff.js +2 -0
  92. package/dist/context/projection-handoff.js.map +1 -1
  93. package/dist/context/tenant-index.d.ts +25 -0
  94. package/dist/context/tenant-index.d.ts.map +1 -1
  95. package/dist/context/tenant-index.js +45 -1
  96. package/dist/context/tenant-index.js.map +1 -1
  97. package/dist/delivery/delivery-monitor.d.ts +12 -9
  98. package/dist/delivery/delivery-monitor.d.ts.map +1 -1
  99. package/dist/delivery/delivery-monitor.js +15 -9
  100. package/dist/delivery/delivery-monitor.js.map +1 -1
  101. package/dist/delivery/delivery-ports.d.ts +6 -13
  102. package/dist/delivery/delivery-ports.d.ts.map +1 -1
  103. package/dist/delivery/delivery-run-control.d.ts +15 -0
  104. package/dist/delivery/delivery-run-control.d.ts.map +1 -1
  105. package/dist/delivery/delivery-run-control.js.map +1 -1
  106. package/dist/delivery/delivery-supervisor.d.ts +33 -5
  107. package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
  108. package/dist/delivery/delivery-supervisor.js +150 -22
  109. package/dist/delivery/delivery-supervisor.js.map +1 -1
  110. package/dist/delivery/delivery.d.ts.map +1 -1
  111. package/dist/delivery/delivery.js +128 -54
  112. package/dist/delivery/delivery.js.map +1 -1
  113. package/dist/delivery/inbox-storage.d.ts +8 -11
  114. package/dist/delivery/inbox-storage.d.ts.map +1 -1
  115. package/dist/delivery/inbox-storage.js +33 -76
  116. package/dist/delivery/inbox-storage.js.map +1 -1
  117. package/dist/delivery/inbox.d.ts +9 -12
  118. package/dist/delivery/inbox.d.ts.map +1 -1
  119. package/dist/delivery/inbox.js +10 -13
  120. package/dist/delivery/inbox.js.map +1 -1
  121. package/dist/delivery/reception-observation.d.ts +56 -0
  122. package/dist/delivery/reception-observation.d.ts.map +1 -0
  123. package/dist/delivery/reception-observation.js +67 -0
  124. package/dist/delivery/reception-observation.js.map +1 -0
  125. package/dist/entity/entity.d.ts +82 -3
  126. package/dist/entity/entity.d.ts.map +1 -1
  127. package/dist/entity/entity.js +121 -22
  128. package/dist/entity/entity.js.map +1 -1
  129. package/dist/handler/generated-handler-registry.js +8 -3
  130. package/dist/handler/generated-handler-registry.js.map +1 -1
  131. package/dist/handler/handler-metadata.d.ts +17 -1
  132. package/dist/handler/handler-metadata.d.ts.map +1 -1
  133. package/dist/handler/handler-metadata.js +10 -4
  134. package/dist/handler/handler-metadata.js.map +1 -1
  135. package/dist/index.d.ts +2 -2
  136. package/dist/index.d.ts.map +1 -1
  137. package/dist/index.js +1 -1
  138. package/dist/index.js.map +1 -1
  139. package/dist/repository/commit-fence.d.ts +11 -0
  140. package/dist/repository/commit-fence.d.ts.map +1 -1
  141. package/dist/repository/commit-fence.js +13 -0
  142. package/dist/repository/commit-fence.js.map +1 -1
  143. package/dist/repository/repository.d.ts +221 -13
  144. package/dist/repository/repository.d.ts.map +1 -1
  145. package/dist/repository/repository.js +2323 -134
  146. package/dist/repository/repository.js.map +1 -1
  147. package/dist/runtime/runtime.d.ts +11 -0
  148. package/dist/runtime/runtime.d.ts.map +1 -1
  149. package/dist/runtime/runtime.js +13 -0
  150. package/dist/runtime/runtime.js.map +1 -1
  151. package/dist/runtime/signal-metadata.d.ts.map +1 -1
  152. package/dist/runtime/signal-metadata.js +3 -10
  153. package/dist/runtime/signal-metadata.js.map +1 -1
  154. package/dist/runtime/signal-publisher.d.ts +14 -30
  155. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  156. package/dist/runtime/signal-publisher.js +75 -47
  157. package/dist/runtime/signal-publisher.js.map +1 -1
  158. package/dist/runtime/standalone-handler-runtime.d.ts +7 -0
  159. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  160. package/dist/runtime/standalone-handler-runtime.js +118 -11
  161. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  162. package/dist/server/environment-delivery-worker.d.ts.map +1 -1
  163. package/dist/server/environment-delivery-worker.js +305 -5
  164. package/dist/server/environment-delivery-worker.js.map +1 -1
  165. package/dist/server/managed-server-application.d.ts +2 -2
  166. package/dist/server/managed-server-application.d.ts.map +1 -1
  167. package/dist/server/managed-server-application.js +467 -177
  168. package/dist/server/managed-server-application.js.map +1 -1
  169. package/dist/server/node-coordinator.d.ts +14 -0
  170. package/dist/server/node-coordinator.d.ts.map +1 -1
  171. package/dist/server/node-coordinator.js +266 -45
  172. package/dist/server/node-coordinator.js.map +1 -1
  173. package/dist/server/server-log.d.ts.map +1 -1
  174. package/dist/server/server-log.js +4 -0
  175. package/dist/server/server-log.js.map +1 -1
  176. package/dist/server/server.d.ts +9 -0
  177. package/dist/server/server.d.ts.map +1 -1
  178. package/dist/server/server.js +33 -8
  179. package/dist/server/server.js.map +1 -1
  180. package/dist/services/spine-services.d.ts.map +1 -1
  181. package/dist/services/spine-services.js +200 -72
  182. package/dist/services/spine-services.js.map +1 -1
  183. package/dist/stand/event-origin-filter.d.ts +16 -0
  184. package/dist/stand/event-origin-filter.d.ts.map +1 -0
  185. package/dist/stand/event-origin-filter.js +124 -0
  186. package/dist/stand/event-origin-filter.js.map +1 -0
  187. package/dist/stand/subscription-observer.d.ts.map +1 -1
  188. package/dist/stand/subscription-observer.js +7 -0
  189. package/dist/stand/subscription-observer.js.map +1 -1
  190. package/dist/stand/subscription-runtime.d.ts +18 -1
  191. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  192. package/dist/stand/subscription-runtime.js +32 -4
  193. package/dist/stand/subscription-runtime.js.map +1 -1
  194. package/dist/testing/index.d.ts +57 -1
  195. package/dist/testing/index.d.ts.map +1 -1
  196. package/dist/testing/index.js +88 -1
  197. package/dist/testing/index.js.map +1 -1
  198. package/dist/tsconfig.tsbuildinfo +1 -1
  199. package/package.json +15 -13
@@ -11,16 +11,22 @@
11
11
  * or implied. See the License for the specific language governing permissions and limitations under
12
12
  * the License.
13
13
  */
14
- import { clone, create, ScalarType, toBinary, } from "@bufbuild/protobuf";
15
- import { AnySchema, Int32ValueSchema, Int64ValueSchema, StringValueSchema, } from "@bufbuild/protobuf/wkt";
14
+ import { createHash, randomUUID } from "node:crypto";
15
+ import { clone, create, equals, ScalarType, toBinary, } from "@bufbuild/protobuf";
16
+ import { AnySchema, FileDescriptorProtoSchema, Int32ValueSchema, Int64ValueSchema, StringValueSchema, } from "@bufbuild/protobuf/wkt";
16
17
  import { ValidationException, RejectionThrowable, Validate, TypeUrls, AnyMessages, Identifiers, StringifierRegistry, Time, } from "@spine-event-engine/core";
17
18
  import { CommandContextSchema, ActorContextSchema, CommandIdSchema, CommandSchema, EventContextSchema, EventIdSchema, EventSchema, MessageIdSchema, TenantIdSchema, RejectionEventContextSchema, VersionSchema, EntityOption_Kind, } from "@spine-event-engine/proto";
18
19
  import * as EntityLog from "@spine-event-engine/proto/generated/spine/system/server/entity_log_events_pb.js";
19
20
  import { EntityTypeNameSchema } from "@spine-event-engine/proto/generated/spine/system/server/entity_type_pb.js";
20
21
  import { EntityRecordSchema, } from "@spine-event-engine/proto/generated/spine/server/entity/entity_pb.js";
21
- import { StorageQueryPolicy, } from "@spine-event-engine/storage";
22
+ import { EventStore, StorageQueryPolicy, } from "@spine-event-engine/storage";
22
23
  import { TenantBoundary } from "@spine-event-engine/storage/provider";
23
24
  import { EntityCommitStorageFactories } from "@spine-event-engine/storage/provider";
25
+ 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";
29
+ import { registryOptions } from "@spine-event-engine/ai/spi/runtime";
24
30
  import { CommandValidationError } from "../bus/command-errors.js";
25
31
  import { SignalPublisher } from "../runtime/signal-publisher.js";
26
32
  import { ServerEnvironment, serverEnvironmentAccess } from "../server/server-environment.js";
@@ -30,11 +36,24 @@ import { EventRoutingInternals } from "./event-routing.js";
30
36
  import { StateUpdateRoutingInternals, } from "./state-update-routing.js";
31
37
  import { RoutingDeclarations } from "./routing-declarations.js";
32
38
  import { EventDispatcherOriginSchemas } from "../bus/event-dispatcher-origin-schemas.js";
39
+ import { SavedDispatcherBindings, } from "../bus/saved-dispatcher-binding.js";
33
40
  import { Delivery } from "../delivery/delivery.js";
41
+ import { receptionObservation } from "../delivery/reception-observation.js";
34
42
  import { commitFenced } from "./commit-fence.js";
43
+ import { AgentHistoryReads } from "../agent/agent-history.js";
44
+ import { AgentReadRuntime } from "../agent/agent-read-runtime.js";
45
+ import { AgentAdmission } from "../agent/agent-admission.js";
46
+ import { AgentRevisions } from "../agent/agent-revisions.js";
47
+ import { AgentExecutionSession } from "../agent/agent-execution-session.js";
48
+ import { AgentExecutionFault } from "../agent/agent-execution-fault.js";
49
+ 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
+ import { AgentAiBindings } from "../agent/agent-ai-binding.js";
35
54
  import { InboxTargets } from "../delivery/inbox.js";
36
55
  import { ShardIndex } from "../delivery/shard-index.js";
37
- import { Aggregate, ProcessManager, Projection, entityHistoryAccess, processManagerQueryAccess, transactionalEntityAccess, } from "../entity/entity.js";
56
+ import { Aggregate, Agent, ProcessManager, Projection, entityHistoryAccess, processManagerQueryAccess, transactionalEntityAccess, } from "../entity/entity.js";
38
57
  import { describeEntityMetadata, attachEntitySchema, } from "../entity/entity-metadata.js";
39
58
  import { EntityIds, EntityRecords, entityStorageDescriptor, } from "../entity/entity-storage-descriptor.js";
40
59
  import { repositorySpecScanner } from "../entity/spec-scanner.js";
@@ -185,6 +204,31 @@ export class Repository {
185
204
  get snapshot() {
186
205
  return RepositoryIdentity.cloneRepositorySnapshot(RepositoryIdentity.createRepositorySnapshot(this.#entityType, this.#entityFamily, this.#metadata));
187
206
  }
207
+ /**
208
+ * Opens retained history for one Agent without restoring an Entity.
209
+ * The trusted application must authorize the caller before using this read.
210
+ *
211
+ * @param this Registered Agent repository.
212
+ * @param id Typed Agent identifier.
213
+ * @param scope Explicit tenant boundary; pass `{}` in a single-tenant Bounded Context.
214
+ * @returns Reader for indexed history categories.
215
+ */
216
+ agentHistory(id, scope) {
217
+ return RepositoryAgentReads.history(this, id, scope);
218
+ }
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
+ }
188
232
  /**
189
233
  * Routes a command to one entity ID without invoking a handler.
190
234
  *
@@ -213,6 +257,199 @@ export class Repository {
213
257
  });
214
258
  }
215
259
  }
260
+ const RepositoryAgentReads = {
261
+ /**
262
+ * Captures a validated Agent and tenant boundary without opening Entity storage.
263
+ *
264
+ * @param repository Registered Agent repository.
265
+ * @param id Typed Agent identifier.
266
+ * @param scope Explicit tenant selection.
267
+ * @returns Detached read identity and active runtime.
268
+ */
269
+ capture(repository, id, scope) {
270
+ if (repository.entityFamily !== "agent")
271
+ throw new TypeError("Agent read requires an Agent repository.");
272
+ const scopeValue = scope;
273
+ if (scopeValue === null || typeof scopeValue !== "object")
274
+ throw new TypeError("Agent read requires an explicit scope.");
275
+ const runtime = repositoryRuntimes.get(repository);
276
+ if (runtime === undefined)
277
+ throw new Error("Agent read requires an active repository.");
278
+ const validated = RepositoryRoutes.readRouteId(id, repository.idField, "command");
279
+ const field = repository.idField.descriptor;
280
+ const copiedId = field.fieldKind === "message"
281
+ ? clone(field.message, validated)
282
+ : validated;
283
+ const tenant = scope.tenantId === undefined ? undefined : RepositoryTenants.require(scope.tenantId);
284
+ const context = RepositoryTenants.storageContextForTenant(runtime.context, tenant);
285
+ return { runtime, context, id: copiedId };
286
+ },
287
+ /**
288
+ * Returns a detached public history request before provider access.
289
+ *
290
+ * @param request Page request and opaque cursor.
291
+ * @returns Independent request values.
292
+ */
293
+ request(request) {
294
+ return {
295
+ pageSize: request.pageSize,
296
+ ...(request.cursor === undefined ? {} : { cursor: request.cursor }),
297
+ };
298
+ },
299
+ /**
300
+ * Creates the four scoped history views for one captured Agent identity.
301
+ *
302
+ * @param repository Registered Agent repository.
303
+ * @param id Typed Agent identifier.
304
+ * @param scope Explicit tenant selection.
305
+ * @returns Reader using indexed provider history.
306
+ */
307
+ history(repository, id, scope) {
308
+ const captured = this.capture(repository, id, scope);
309
+ const read = (view, request) => this.historyPage(repository, captured, view, this.request(request));
310
+ return Object.freeze({
311
+ fullHistory: (request) => read({ kind: "full" }, request),
312
+ conversationHistory: async (request) => {
313
+ const conversationValue = request.conversation;
314
+ if (conversationValue === null ||
315
+ typeof conversationValue !== "object" ||
316
+ !("$typeName" in conversationValue) ||
317
+ conversationValue.$typeName !== ConversationIdSchema.typeName ||
318
+ !("value" in conversationValue) ||
319
+ typeof conversationValue.value !== "string" ||
320
+ conversationValue.value.length === 0)
321
+ throw new TypeError("Agent conversation history requires a ConversationId.");
322
+ const conversation = clone(ConversationIdSchema, request.conversation);
323
+ const page = await read({ kind: "conversation", conversation }, request);
324
+ return this.project(page, "conversationRecord");
325
+ },
326
+ systemEventHistory: async (request) => this.project(await read({ kind: "system" }, request), "systemEvent"),
327
+ domainEventHistory: async (request) => this.project(await read({ kind: "domain" }, request), "domainEvent"),
328
+ });
329
+ },
330
+ /**
331
+ * Reads one category from the existing indexed history provider.
332
+ *
333
+ * @param repository Registered Agent repository.
334
+ * @param captured Detached Agent and tenant identity.
335
+ * @param view Indexed category and optional conversation.
336
+ * @param request Detached page request.
337
+ * @returns Detached history entries and opaque continuation.
338
+ */
339
+ async historyPage(repository, captured, view, request) {
340
+ if (repositoryRuntimes.get(repository) !== captured.runtime)
341
+ 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 {
349
+ const page = await AgentHistoryReads.readBound({
350
+ storage: history,
351
+ entityId: captured.id,
352
+ scope: repositoryAccess.agentHistoryScope(captured.context, input, captured.id),
353
+ }, view, request);
354
+ return {
355
+ items: page.items,
356
+ ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
357
+ };
358
+ }
359
+ finally {
360
+ history.close();
361
+ }
362
+ },
363
+ /**
364
+ * Returns one typed category from the full Proto history oneof.
365
+ *
366
+ * @typeParam K Selected history oneof category.
367
+ * @param page Complete indexed history page.
368
+ * @param kind Expected oneof category.
369
+ * @returns Category values and the original continuation.
370
+ */
371
+ project(page, kind) {
372
+ const items = page.items.map((entry) => {
373
+ if (entry.item.case !== kind)
374
+ throw new Error("Agent history provider returned the wrong category.");
375
+ return entry.item.value;
376
+ });
377
+ return {
378
+ items,
379
+ ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
380
+ };
381
+ },
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
+ };
216
453
  const repositorySnapshots = new WeakMap();
217
454
  const repositoryCreators = new WeakMap();
218
455
  const repositoryRoutings = new WeakMap();
@@ -225,6 +462,8 @@ const repositoryRuntimes = new WeakMap();
225
462
  const repositoryEntityHandles = new WeakMap();
226
463
  const entityStateHistoryCaches = new WeakMap();
227
464
  const repositoryHistoryConfigurations = new WeakMap();
465
+ const repositoryAiConfigurations = new WeakMap();
466
+ const repositoryAgentCodeRevisions = new WeakMap();
228
467
  const repositoryDispatchGuards = new WeakMap();
229
468
  /**
230
469
  * Installs repository routing, history, and callback state after identity validation.
@@ -248,10 +487,27 @@ const RepositoryRegistration = {
248
487
  repositoryRoutings.set(repository, routing);
249
488
  repositoryProducedEventSchemas.set(repository, Object.freeze([...routing.producedEventSchemas]));
250
489
  repositoryHistoryConfigurations.set(repository, RepositoryStorage.readHistoryConfiguration(options, entityFamily));
490
+ this.installAgentOptions(repository, options, entityFamily);
251
491
  repositorySnapshots.set(repository, RepositoryIdentity.createRepositorySnapshot(entityType, entityFamily, metadata));
252
492
  repositoryDispatchers.set(repository, RepositoryDispatch.createRepositoryDispatchers(repository, routing));
253
493
  RepositoryRegistration.installTargets(repository, routing);
254
494
  },
495
+ /**
496
+ * Captures Agent capability and code revisions during repository registration.
497
+ *
498
+ * @typeParam EntityType Concrete generated Entity constructor.
499
+ * @param repository Registered Entity repository for this operation.
500
+ * @param options Repository or execution options for this operation.
501
+ * @param family Registered Entity family.
502
+ */
503
+ installAgentOptions(repository, options, family) {
504
+ if (family !== "agent")
505
+ return;
506
+ if (options.ai !== undefined)
507
+ repositoryAiConfigurations.set(repository, options.ai);
508
+ if (options.agentCodeRevision !== undefined)
509
+ repositoryAgentCodeRevisions.set(repository, options.agentCodeRevision);
510
+ },
255
511
  /**
256
512
  * Registers durable Entity and direct Projection delivery targets.
257
513
  *
@@ -312,6 +568,98 @@ export const repositoryAccess = Object.freeze({
312
568
  }
313
569
  return schemas;
314
570
  },
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
+ agentConfiguration(repository) {
578
+ 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 }) });
597
+ },
598
+ /**
599
+ * Opens a scoped provider read without restoring an application Entity.
600
+ *
601
+ * @param repository Registered Agent repository.
602
+ * @param entityId Typed Agent identifier.
603
+ * @param view Indexed category.
604
+ * @param read Requested count, byte budget, and complete continuation key.
605
+ * @param tenantId Complete tenant identity when required.
606
+ * @returns Retained entries and continuation status.
607
+ */
608
+ async agentHistory(repository, entityId, view, read, tenantId) {
609
+ if (repository.entityFamily !== "agent")
610
+ throw new TypeError("Agent history requires an Agent repository.");
611
+ const runtime = repositoryRuntimes.get(repository);
612
+ if (runtime === undefined)
613
+ throw new Error("Agent history requires an active repository.");
614
+ const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
615
+ 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
+ }
627
+ },
628
+ async agentHistoryPage(repository, entityId, request, tenantId) {
629
+ if (repository.entityFamily !== "agent")
630
+ throw new TypeError("Agent history requires an Agent repository.");
631
+ const runtime = repositoryRuntimes.get(repository);
632
+ if (runtime === undefined)
633
+ throw new Error("Agent history requires an active repository.");
634
+ const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
635
+ 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({
643
+ storage: history,
644
+ entityId,
645
+ scope: this.agentHistoryScope(context, input, entityId),
646
+ }, { kind: "full" }, request);
647
+ }
648
+ finally {
649
+ history.close();
650
+ }
651
+ },
652
+ /**
653
+ * Computes the opaque cursor scope for a registered Agent history read.
654
+ */
655
+ agentHistoryScope(context, input, entityId) {
656
+ return {
657
+ context: context.name,
658
+ tenant: JSON.stringify(context.tenantId ?? null),
659
+ repository: input.sourceType.typeName,
660
+ entity: input.id.key(entityId),
661
+ };
662
+ },
315
663
  /**
316
664
  * Looks up command dispatch for the repository.
317
665
  *
@@ -1558,13 +1906,10 @@ class ProjectionEventExecution {
1558
1906
  async #loadProjection(entityId, options, mode) {
1559
1907
  const stored = await standAccess.readCurrent(this.#runtime.stand, this.#repository.stateSchema, entityId, options);
1560
1908
  const entity = RepositoryEntities.instantiate(this.#repository, entityId, stored, mode === "rebuild" && stored?.deleted === true);
1561
- const { commits, events, storageInput } = RepositoryEntities.bindStorage(this.#repository, this.#runtime, options.tenantId, entityId, entity);
1562
- return RepositoryEntities.loaded(entity, stored, commits, events, storageInput, this.#repository.stateSchema, entityId);
1909
+ const { commits, events, agentHistory, storageInput } = RepositoryEntities.bindStorage(this.#repository, this.#runtime, options.tenantId, entityId, entity);
1910
+ return RepositoryEntities.loaded(entity, stored, commits, events, agentHistory, storageInput, this.#repository.stateSchema, entityId);
1563
1911
  }
1564
1912
  }
1565
- /**
1566
- * Binds Process Manager query access to Stand for one handler invocation.
1567
- */
1568
1913
  const ProcessManagerQueries = Object.freeze({
1569
1914
  /**
1570
1915
  * Binds tenant-aware query reads and returns a release callback.
@@ -1573,15 +1918,16 @@ const ProcessManagerQueries = Object.freeze({
1573
1918
  * @param runtime Context Stand and query services.
1574
1919
  * @param actorContext Actor metadata from the source signal.
1575
1920
  * @param tenantId Tenant for query reads, when present.
1921
+ * @param intercept Durable Agent snapshot interception, when active.
1576
1922
  * @returns Callback that removes the query binding.
1577
1923
  */
1578
- bind(entity, runtime, actorContext, tenantId) {
1924
+ bind(entity, runtime, actorContext, tenantId, intercept) {
1579
1925
  const context = actorContext === undefined
1580
1926
  ? create(ActorContextSchema)
1581
1927
  : clone(ActorContextSchema, actorContext);
1582
1928
  const effectiveTenant = EffectiveTenants.current(runtime.context.multitenant, tenantId);
1583
1929
  context.tenantId = effectiveTenant;
1584
- return processManagerQueryAccess.bind(entity, (plan, schema, query) => ProcessManagerQueries.read(runtime, effectiveTenant, plan, schema, query), context);
1930
+ return processManagerQueryAccess.bind(entity, (plan, schema, query) => ProcessManagerQueries.read(runtime, effectiveTenant, plan, schema, query, intercept), context);
1585
1931
  },
1586
1932
  /**
1587
1933
  * Routes a handler read to the registered state target before reaching storage.
@@ -1592,9 +1938,10 @@ const ProcessManagerQueries = Object.freeze({
1592
1938
  * @param plan Compiled read criteria.
1593
1939
  * @param schema State schema supplied by the handler.
1594
1940
  * @param query Wire query carrying the actor and requested fields.
1941
+ * @param intercept Durable Agent snapshot interception, when active.
1595
1942
  * @returns Detached state snapshots decoded with the caller's schema.
1596
1943
  */
1597
- async read(runtime, tenantId, plan, schema, query) {
1944
+ async read(runtime, tenantId, plan, schema, query, intercept) {
1598
1945
  const routes = RegisteredTargets.forStand(runtime.stand);
1599
1946
  const typeUrl = TypeUrls.derive(schema);
1600
1947
  const target = routes?.find(typeUrl);
@@ -1609,7 +1956,23 @@ const ProcessManagerQueries = Object.freeze({
1609
1956
  if (target !== undefined)
1610
1957
  routes?.validate(target, plan);
1611
1958
  const destination = EffectiveTenants.destination(tenantId, target?.context.isMultitenant ?? runtime.context.multitenant);
1612
- const results = await QueryReader.read(target?.context.stand() ?? runtime.stand, registeredSchema, plan, destination, 10_000, query);
1959
+ const live = () => this.load(target?.context.stand() ?? runtime.stand, registeredSchema, plan, destination, query, schema);
1960
+ return intercept === undefined ? live() : intercept(schema, query, live);
1961
+ },
1962
+ /**
1963
+ * Reads the authorized target after optional Agent snapshot reuse is checked.
1964
+ *
1965
+ * @typeParam Schema Generated Projection state descriptor.
1966
+ * @param stand Stand selected by the authorized query route.
1967
+ * @param registeredSchema Descriptor registered for the target.
1968
+ * @param plan Compiled query criteria.
1969
+ * @param destination Effective tenant selected for the target.
1970
+ * @param query Original typed Query envelope.
1971
+ * @param schema Descriptor expected by the caller.
1972
+ * @returns Detached Projection state snapshots.
1973
+ */
1974
+ async load(stand, registeredSchema, plan, destination, query, schema) {
1975
+ const results = await QueryReader.read(stand, registeredSchema, plan, destination, 10_000, query);
1613
1976
  return Object.freeze(results.map((result) => clone(schema, result.state)));
1614
1977
  },
1615
1978
  });
@@ -1654,32 +2017,65 @@ class ProcessManagerExecutionSupport {
1654
2017
  async load(entityId, options) {
1655
2018
  const stored = await standAccess.readCurrent(this.#runtime.stand, this.#repository.stateSchema, entityId, options);
1656
2019
  const entity = RepositoryEntities.instantiate(this.#repository, entityId, stored, false);
1657
- const { commits, events, storageInput } = RepositoryEntities.bindStorage(this.#repository, this.#runtime, options.tenantId, entityId, entity);
1658
- return RepositoryEntities.loaded(entity, stored, commits, events, storageInput, this.#repository.stateSchema, entityId);
2020
+ const { commits, events, agentHistory, storageInput } = RepositoryEntities.bindStorage(this.#repository, this.#runtime, options.tenantId, entityId, entity);
2021
+ return RepositoryEntities.loaded(entity, stored, commits, events, agentHistory, storageInput, this.#repository.stateSchema, entityId);
1659
2022
  }
1660
2023
  /**
1661
2024
  * Stores changed Process Manager state or produced Events under one Version.
1662
2025
  *
1663
2026
  * @param loaded Process Manager and commit storage.
1664
2027
  * @param options Stand tenant selection.
1665
- * @param events Produced or diagnostic Events to retain when configured.
2028
+ * @param diagnostics Input and produced Events to retain when configured.
2029
+ * @param producedEvents Delivery Events to save before current state.
1666
2030
  * @returns `true` after a successful commit or unchanged no-op.
1667
2031
  */
1668
- async commit(loaded, options, events) {
2032
+ async commit(loaded, options, diagnostics, producedEvents) {
1669
2033
  const version = RepositoryEntities.repositoryVersion(loaded.entity);
1670
2034
  const priorNumber = loaded.current?.version?.number ?? 0;
1671
2035
  if (version.number === priorNumber) {
1672
- await this.#appendUnchangedDiagnostics(loaded, events);
2036
+ 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
+ }
2044
+ await this.#appendAgentEvents(loaded, producedEvents);
1673
2045
  return true;
1674
2046
  }
1675
2047
  const entityId = loaded.entity.id;
1676
2048
  const state = RepositoryEntities.repositoryState(loaded.entity);
1677
2049
  const lifecycle = RepositoryEntities.repositoryLifecycle(loaded.entity);
1678
2050
  const deferred = await standAccess.deferUpdate(this.#runtime.stand, this.#repository.stateSchema, state, RepositoryStand.standUpdateOptions(options.tenantId, version, lifecycle), this.#repository.metadata);
1679
- await this.#storeProcessManagerRecord(loaded, entityId, events, deferred);
1680
- this.#notifyProcessManager(deferred, events);
2051
+ await this.#storeProcessManagerRecord(loaded, entityId, diagnostics, producedEvents, deferred);
2052
+ this.#notifyProcessManager(deferred, diagnostics);
2053
+ await this.#appendAgentEvents(loaded, producedEvents);
1681
2054
  return true;
1682
2055
  }
2056
+ /**
2057
+ * Stores original envelopes emitted by an Agent under its repository.
2058
+ *
2059
+ * @param loaded Restored Agent with history storage.
2060
+ * @param events Emitted domain Event envelopes.
2061
+ * @returns Completion after every append.
2062
+ */
2063
+ async #appendAgentEvents(loaded, events) {
2064
+ if (this.#repository.entityFamily !== "agent")
2065
+ return;
2066
+ const history = loaded.agentHistory;
2067
+ if (history === undefined)
2068
+ throw new Error("Agent history storage is required.");
2069
+ const id = loaded.entity.id;
2070
+ for (const event of events) {
2071
+ if (event.context?.timestamp === undefined)
2072
+ throw new Error("Agent emitted Event requires its original timestamp.");
2073
+ await history.append(id, create(AgentHistoryEntrySchema, {
2074
+ occurredAt: event.context.timestamp,
2075
+ item: { case: "domainEvent", value: event },
2076
+ }));
2077
+ }
2078
+ }
1683
2079
  /**
1684
2080
  * Writes configured Process Manager diagnostics without storing unchanged state.
1685
2081
  *
@@ -1698,13 +2094,14 @@ class ProcessManagerExecutionSupport {
1698
2094
  *
1699
2095
  * @param loaded Process Manager and commit storage.
1700
2096
  * @param entityId Process Manager identifier.
1701
- * @param events Diagnostic Events to retain when configured.
2097
+ * @param diagnostics Diagnostic Events to retain when configured.
2098
+ * @param producedEvents Delivery Events to save before current state.
1702
2099
  * @param deferred Stand update to notify or cancel.
1703
2100
  * @returns Completion after durable persistence.
1704
2101
  */
1705
- async #storeProcessManagerRecord(loaded, entityId, events, deferred) {
2102
+ async #storeProcessManagerRecord(loaded, entityId, diagnostics, producedEvents, deferred) {
1706
2103
  try {
1707
- await this.#commitProcessManagerRecord(loaded, entityId, events, deferred);
2104
+ await this.#commitProcessManagerRecord(loaded, entityId, diagnostics, producedEvents, deferred);
1708
2105
  entityStateHistoryCaches.get(loaded.entity)?.clear();
1709
2106
  }
1710
2107
  catch (error) {
@@ -1717,11 +2114,12 @@ class ProcessManagerExecutionSupport {
1717
2114
  *
1718
2115
  * @param loaded Loaded Process Manager and commit storage.
1719
2116
  * @param entityId Process Manager identifier.
1720
- * @param events Diagnostic Events to retain when configured.
2117
+ * @param diagnostics Diagnostic Events to retain when configured.
2118
+ * @param producedEvents Delivery Events to save before current state.
1721
2119
  * @param deferred Stand update containing the prepared record.
1722
2120
  * @returns Completion after the storage commit.
1723
2121
  */
1724
- #commitProcessManagerRecord(loaded, entityId, events, deferred) {
2122
+ #commitProcessManagerRecord(loaded, entityId, diagnostics, producedEvents, deferred) {
1725
2123
  const history = RepositoryStorage.historyConfiguration(this.#repository);
1726
2124
  const record = RepositoryStand.preparedRecord(this.#repository, deferred, entityId);
1727
2125
  return loaded.commits.commit({
@@ -1731,8 +2129,9 @@ class ProcessManagerExecutionSupport {
1731
2129
  next: record,
1732
2130
  ...(history.stateHistory ? { states: [record] } : {}),
1733
2131
  ...(history.processManagerEventHistory
1734
- ? { diagnostics: events.map((event) => clone(EventSchema, event)) }
2132
+ ? { diagnostics: diagnostics.map((event) => clone(EventSchema, event)) }
1735
2133
  : {}),
2134
+ events: producedEvents.map((event) => clone(EventSchema, event)),
1736
2135
  });
1737
2136
  }
1738
2137
  /**
@@ -1751,7 +2150,1271 @@ class ProcessManagerExecutionSupport {
1751
2150
  }
1752
2151
  }
1753
2152
  /**
1754
- * Executes a routed Process Manager Command and publishes its results.
2153
+ * Advances admitted Agent work only under a provider claim and exact record fence.
2154
+ */
2155
+ const AgentExecutionRunner = {
2156
+ /**
2157
+ * Processes one accepted Agent transition under its provider claim.
2158
+ *
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.
2164
+ */
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);
2182
+ },
2183
+ /**
2184
+ * Updates and releases one exact claim while its saved work advances.
2185
+ *
2186
+ * @param repository Registered Entity repository for this operation.
2187
+ * @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.
2192
+ */
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
+ }
2219
+ },
2220
+ /**
2221
+ * Handles transient failures while giving expired work a final audit.
2222
+ *
2223
+ * @param repository Registered Entity repository for this operation.
2224
+ * @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.
2229
+ */
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;
2249
+ },
2250
+ /**
2251
+ * Processes one claimed record according to its saved completion or request state.
2252
+ *
2253
+ * @param repository Registered Entity repository for this operation.
2254
+ * @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.
2275
+ */
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));
2281
+ },
2282
+ /**
2283
+ * Checks for a dispatched model reservation whose response was never saved.
2284
+ *
2285
+ * @param record Durable Agent execution record.
2286
+ * @returns Whether the checked condition holds.
2287
+ */
2288
+ hasUnresolvedPhysicalRequest(record) {
2289
+ const model = record.journal.some((entry) => {
2290
+ if (entry.evidence.case !== "attempt")
2291
+ return false;
2292
+ const attempt = entry.evidence.value;
2293
+ return (attempt.transportReserved &&
2294
+ attempt.response.case === undefined &&
2295
+ !this.hasSavedOperationResult(record, attempt.operation?.value));
2296
+ });
2297
+ return model || this.hasUnresolvedToolWrite(record);
2298
+ },
2299
+ /**
2300
+ * Checks for a dispatched write with no recorded tool result.
2301
+ *
2302
+ * @param record Durable Agent execution record.
2303
+ * @returns Whether the checked condition holds.
2304
+ */
2305
+ hasUnresolvedToolWrite(record) {
2306
+ return record.journal.some((entry) => {
2307
+ if (entry.evidence.case !== "tool")
2308
+ return false;
2309
+ const tool = entry.evidence.value;
2310
+ return (tool.request?.effect === ToolEffect.WRITE &&
2311
+ tool.response === undefined &&
2312
+ (tool.dispatched || tool.outcomeUnknown) &&
2313
+ !this.hasSavedOperationResult(record, tool.operation?.value));
2314
+ });
2315
+ },
2316
+ /**
2317
+ * Checks for a settled named result even when physical evidence is incomplete.
2318
+ *
2319
+ * @param record Durable Agent execution record.
2320
+ * @param id Saved signal, operation, or Entity identifier.
2321
+ * @returns Whether the checked condition holds.
2322
+ */
2323
+ hasSavedOperationResult(record, id) {
2324
+ return record.journal.some((entry) => entry.evidence.case === "operation" &&
2325
+ entry.evidence.value.operation?.value === id &&
2326
+ entry.evidence.value.result.case !== undefined);
2327
+ },
2328
+ /**
2329
+ * Records termination of an unreplayable physical request under the provider fence.
2330
+ *
2331
+ * @param repository Registered Entity repository for this operation.
2332
+ * @param runtime Bounded Context storage and publication services.
2333
+ * @param tenantId Tenant selected for this accepted signal.
2334
+ * @param session Fenced Agent execution session.
2335
+ * @param reason Safe terminal reason retained in Agent audit.
2336
+ * @returns When the fenced repository operation completes.
2337
+ */
2338
+ async terminate(repository, runtime, tenantId, session, reason) {
2339
+ const record = session.record();
2340
+ const event = this.terminationEvent(repository, runtime, tenantId, record, reason);
2341
+ const occurredAt = event.context?.timestamp;
2342
+ if (occurredAt === undefined)
2343
+ throw new Error("Agent termination Event requires Time.");
2344
+ runtime.registerSystemEventSchema(AgentInvocationTerminatedSchema);
2345
+ await session.update((next) => {
2346
+ next.status = AgentInvocationStatus.AGENT_INVOCATION_TERMINATED;
2347
+ next.terminalFailure = create(AiDiagnosticIdSchema, { value: randomUUID() });
2348
+ return next;
2349
+ }, [
2350
+ create(AgentHistoryEntrySchema, {
2351
+ occurredAt,
2352
+ item: { case: "systemEvent", value: event },
2353
+ }),
2354
+ ]);
2355
+ await runtime.publishAgentSystemEvent(event);
2356
+ },
2357
+ /**
2358
+ * Builds one original termination envelope for a saved terminal reason.
2359
+ *
2360
+ * @param repository Registered Entity repository for this operation.
2361
+ * @param runtime Bounded Context storage and publication services.
2362
+ * @param tenantId Tenant selected for this accepted signal.
2363
+ * @param record Durable Agent execution record.
2364
+ * @param reason Safe terminal reason retained in Agent audit.
2365
+ * @returns Original AgentInvocationTerminated System Event envelope.
2366
+ */
2367
+ terminationEvent(repository, runtime, tenantId, record, reason) {
2368
+ const accepted = record.accepted;
2369
+ if (accepted?.recipientId === undefined)
2370
+ throw new Error("Agent termination requires the original typed recipient.");
2371
+ const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2372
+ const unresolvedToolCalls = this.unresolvedToolCalls(record);
2373
+ const payload = create(AgentInvocationTerminatedSchema, {
2374
+ agent: scope.agent,
2375
+ sourceSignal: scope.source,
2376
+ reason,
2377
+ unresolvedAttempts: this.unresolvedAttempts(record),
2378
+ unresolvedToolCalls,
2379
+ terminatedAt: Time.currentTime(),
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))
2583
+ 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;
2922
+ }
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
+ },
2972
+ /**
2973
+ * Returns the typed original ID of one completed outgoing Command.
2974
+ *
2975
+ * @param output Prepared outgoing Command.
2976
+ * @returns Original durable Command ID.
2977
+ */
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
+ },
2983
+ /**
2984
+ * Validates every stored binding against the current generated selectors.
2985
+ *
2986
+ * @param repository Agent registration.
2987
+ * @param accepted Immutable source and ordered bindings.
2988
+ * @returns Current matching handler metadata.
2989
+ */
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),
3027
+ ];
3028
+ },
3029
+ /**
3030
+ * Records start facts and the original dispatch System Event before handler execution.
3031
+ *
3032
+ * @param repository Agent registration.
3033
+ * @param runtime Bound services.
3034
+ * @param session Fenced provider claim and current record.
3035
+ * @param loaded Restored Entity and initial Version.
3036
+ * @param entityId Original typed recipient.
3037
+ * @param tenantId Accepted delivery tenant when applicable.
3038
+ * @returns Updated record image for exact subsequent CAS.
3039
+ */
3040
+ async start(repository, runtime, session, loaded, entityId, tenantId) {
3041
+ const accepted = session.record().accepted;
3042
+ if (accepted === undefined)
3043
+ throw new Error("Agent start requires accepted work.");
3044
+ const started = this.startFacts(repository, runtime, accepted, loaded);
3045
+ const audit = this.startAudit(runtime, repository, accepted, entityId);
3046
+ await session.update((current) => {
3047
+ current.started = started;
3048
+ return current;
3049
+ }, [this.startHistory(audit)]);
3050
+ if (accepted.signal.case === "command")
3051
+ HandlerDispatchPublisher.publishCommand(runtime, audit);
3052
+ else
3053
+ HandlerDispatchPublisher.publishReactor(runtime, audit);
3054
+ return this.selectMissingModels(repository, runtime, session, accepted, tenantId);
3055
+ },
3056
+ /**
3057
+ * Stores the original dispatch audit envelope in mandatory Agent history.
3058
+ *
3059
+ * @param audit Original Agent System event envelope.
3060
+ * @returns Typed Agent history row for the original dispatch Event.
3061
+ */
3062
+ startHistory(audit) {
3063
+ const occurredAt = audit.context?.timestamp;
3064
+ if (occurredAt === undefined)
3065
+ throw new Error("Agent dispatch audit requires its timestamp.");
3066
+ return create(AgentHistoryEntrySchema, {
3067
+ occurredAt,
3068
+ item: { case: "systemEvent", value: audit },
3069
+ });
3070
+ },
3071
+ /**
3072
+ * Verifies admission revisions and fixes whole-invocation limits before execution.
3073
+ *
3074
+ * @param repository Registered Entity repository for this operation.
3075
+ * @param runtime Bounded Context storage and publication services.
3076
+ * @param accepted Durable original signal and saved handler bindings.
3077
+ * @param loaded Loaded Entity and its repository storage state.
3078
+ * @returns Durable deadline, initial version, and whole-invocation bounds.
3079
+ */
3080
+ startFacts(repository, runtime, accepted, loaded) {
3081
+ const configuration = repositoryAccess.agentConfiguration(repository);
3082
+ const ai = runtime.ai;
3083
+ if (accepted.key === undefined || ai === undefined || configuration.ai === undefined)
3084
+ throw new Error("Agent execution start requires accepted work and AI policy.");
3085
+ const limits = registryOptions(ai).invocationLimits ?? {};
3086
+ return create(AgentExecutionStartSchema, {
3087
+ ...(limits.deadlineMs === undefined
3088
+ ? {}
3089
+ : { deadline: addMillis(Time.currentTime(), limits.deadlineMs) }),
3090
+ initialVersion: RepositoryEntities.priorVersion(loaded.current),
3091
+ bounds: this.invocationBounds(limits),
3092
+ });
3093
+ },
3094
+ /**
3095
+ * Persists each model kind without a saved connection identity.
3096
+ *
3097
+ * @param repository Agent registration and permitted capabilities.
3098
+ * @param runtime Bound AI registry.
3099
+ * @param session Current fenced execution record.
3100
+ * @param accepted Original accepted signal and actor.
3101
+ * @param tenantId Accepted tenant when applicable.
3102
+ * @returns Record after each missing identity has been saved.
3103
+ */
3104
+ async selectMissingModels(repository, runtime, session, accepted, tenantId) {
3105
+ const ai = runtime.ai;
3106
+ const configuration = repositoryAccess.agentConfiguration(repository).ai;
3107
+ if (ai === undefined || configuration === undefined)
3108
+ throw new Error("Agent model selection requires saved start facts and AI policy.");
3109
+ const deadlineMs = this.savedModelDeadline(session, "selection");
3110
+ const source = accepted.signal.value?.message;
3111
+ for (const kind of ["generation", "decision"]) {
3112
+ if (!configuration.models.some((model) => model.definition.kind === kind))
3113
+ continue;
3114
+ const modelKind = AgentModelSelection.kind(kind);
3115
+ if (session.record().started?.models.some((model) => model.kind === modelKind))
3116
+ continue;
3117
+ const selected = await AgentModelSelection.selectKind(ai, configuration, AgentModelSelection.scope(accepted, repository.stateSchema, tenantId), session.initialPreferences(), session.signal, deadlineMs, kind, source);
3118
+ await this.saveSelectedModel(session, selected);
3119
+ }
3120
+ return session.record();
3121
+ },
3122
+ /**
3123
+ * Writes one selected model under the current Agent claim.
3124
+ *
3125
+ * @param session Fenced Agent execution session.
3126
+ * @param selected Authorized deployment and connection identity.
3127
+ * @returns When the selection is durable.
3128
+ */
3129
+ async saveSelectedModel(session, selected) {
3130
+ await session.update((record) => {
3131
+ if (record.started === undefined)
3132
+ throw new Error("Agent start facts disappeared.");
3133
+ record.started.models.push(selected);
3134
+ return record;
3135
+ });
3136
+ },
3137
+ /**
3138
+ * Rejects accepted work when its exact handler and AI policy changed.
3139
+ *
3140
+ * @param repository Registered Entity repository for this operation.
3141
+ * @param accepted Durable original signal and saved handler bindings.
3142
+ * @param handlers Generated handlers in their saved order.
3143
+ * @param ai Configured AI registry for this Bounded Context.
3144
+ * @param configuration Repository and AI policy used for revision checks.
3145
+ */
3146
+ assertRevisions(repository, accepted, handlers, ai, configuration) {
3147
+ if (configuration.ai === undefined)
3148
+ throw new Error("Agent AI policy is required.");
3149
+ const revisions = AgentRevisions.atAdmission(repository.stateSchema, handlers, ai, configuration.ai);
3150
+ if (accepted.codeRevision?.value !== configuration.codeRevision ||
3151
+ accepted.schemaRevision?.value !== revisions.schema ||
3152
+ accepted.policyRevision?.value !== revisions.policy)
3153
+ throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent code, schema, or policy changed before execution.");
3154
+ },
3155
+ /**
3156
+ * Copies the whole-invocation budget into its durable integer fields.
3157
+ *
3158
+ * @param limits Configured whole-invocation limits.
3159
+ * @returns Whole-transition limits persisted at invocation start.
3160
+ */
3161
+ invocationBounds(limits) {
3162
+ return create(AgentInvocationBoundsSchema, {
3163
+ ...(limits.operations === undefined ? {} : { operations: BigInt(limits.operations) }),
3164
+ ...(limits.modelRequests === undefined
3165
+ ? {}
3166
+ : { modelRequests: BigInt(limits.modelRequests) }),
3167
+ ...(limits.toolCalls === undefined ? {} : { toolCalls: BigInt(limits.toolCalls) }),
3168
+ ...(limits.recordedReads === undefined
3169
+ ? {}
3170
+ : { recordedReads: BigInt(limits.recordedReads) }),
3171
+ ...(limits.deadlineMs === undefined ? {} : { deadlineMillis: BigInt(limits.deadlineMs) }),
3172
+ ...(limits.totalInputBytes === undefined
3173
+ ? {}
3174
+ : { totalInputBytes: BigInt(limits.totalInputBytes) }),
3175
+ ...(limits.totalOutputBytes === undefined
3176
+ ? {}
3177
+ : { totalOutputBytes: BigInt(limits.totalOutputBytes) }),
3178
+ ...(limits.maxRecoveryBytes === undefined
3179
+ ? {}
3180
+ : { maxRecoveryBytes: BigInt(limits.maxRecoveryBytes) }),
3181
+ });
3182
+ },
3183
+ /**
3184
+ * Creates a genuine source-specific dispatch System Event before the handler.
3185
+ *
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.
3191
+ */
3192
+ startAudit(runtime, repository, accepted, entityId) {
3193
+ const source = accepted.signal;
3194
+ if (source.case === "command")
3195
+ return HandlerDispatchPublisher.commandEvent(runtime, repository, source.value, entityId);
3196
+ if (source.case === "event")
3197
+ return HandlerDispatchPublisher.reactorEvent(runtime, repository, source.value, entityId);
3198
+ throw new Error("Agent start requires its accepted envelope.");
3199
+ },
3200
+ /**
3201
+ * Invokes saved Command or Event bindings inside one Entity transaction.
3202
+ *
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.
3211
+ */
3212
+ async invoke(repository, runtime, tenantId, session, entity, accepted, handlers) {
3213
+ transactionalEntityAccess.start(entity);
3214
+ 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;
3221
+ }
3222
+ catch (error) {
3223
+ transactionalEntityAccess.rollback(entity);
3224
+ throw error;
3225
+ }
3226
+ },
3227
+ /**
3228
+ * Validates the transaction result before Agent output completion.
3229
+ *
3230
+ * @param entity Loaded recipient Entity instance.
3231
+ * @returns When the fenced repository operation completes.
3232
+ */
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);
3255
+ try {
3256
+ const deadlineEpochMs = this.handlerDeadline(session);
3257
+ const signals = await this.awaitHandler(this.invokeHandler(entity, accepted, handler), session.signal, deadlineEpochMs);
3258
+ reads.finish();
3259
+ facade.finish();
3260
+ return signals;
3261
+ }
3262
+ finally {
3263
+ facade.close();
3264
+ release();
3265
+ }
3266
+ },
3267
+ /**
3268
+ * Binds Agent AI, indexed history and routed projection queries for one handler.
3269
+ *
3270
+ * @param entity Loaded recipient Entity instance.
3271
+ * @param runtime Bounded Context storage and publication services.
3272
+ * @param tenantId Tenant selected for this accepted signal.
3273
+ * @param accepted Durable original signal and saved handler bindings.
3274
+ * @param reads Handler-scoped durable read facade.
3275
+ * @param facade Handler-scoped AI facade.
3276
+ * @returns Release callback for handler-scoped AI and read facades.
3277
+ */
3278
+ bindHandlerCapabilities(entity, runtime, tenantId, accepted, reads, facade) {
3279
+ const releaseHistory = AgentHistoryReads.withJournal(entity, (scope, view, request, live, maxBytes) => reads.history(scope, view, request, live, maxBytes));
3280
+ let releaseQuery;
3281
+ try {
3282
+ releaseQuery = ProcessManagerQueries.bind(entity, runtime, accepted.actor, tenantId, (schema, query, live) => reads.query(schema, query, live));
3283
+ const releaseAi = AgentAiBindings.bind(entity, facade);
3284
+ return () => {
3285
+ releaseQuery?.();
3286
+ releaseHistory();
3287
+ releaseAi();
3288
+ };
3289
+ }
3290
+ catch (error) {
3291
+ releaseQuery?.();
3292
+ releaseHistory();
3293
+ throw error;
3294
+ }
3295
+ },
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
+ },
3314
+ /**
3315
+ * Invokes one selected generated handler and validates its declared results.
3316
+ *
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.
3321
+ */
3322
+ async invokeHandler(entity, accepted, handler) {
3323
+ const source = accepted.signal;
3324
+ if (source.case !== "command" && source.case !== "event")
3325
+ throw new Error("Agent source envelope is missing.");
3326
+ const kind = source.case;
3327
+ const message = EntityInvocation.unpackRequired(EntityInvocation.requireSignalMessage(source.value.message, kind), handler.handler.schema, kind);
3328
+ const context = source.case === "command"
3329
+ ? EntityInvocation.commandHandlerContext(source.value)
3330
+ : EntityInvocation.eventHandlerContext(source.value);
3331
+ const produced = await EntityInvocation.invokeEntityMethod(entity, handler.handler.methodName, message, handler.handler.parameterCount, context);
3332
+ const signals = produced === undefined
3333
+ ? []
3334
+ : Array.isArray(produced)
3335
+ ? Array.from(produced)
3336
+ : [produced];
3337
+ RepositoryHandlers.requireDeclaredOutputs(handler.handler, signals);
3338
+ return signals;
3339
+ },
3340
+ /**
3341
+ * Reads the original invocation deadline before starting an application callback.
3342
+ *
3343
+ * @param session Current fenced execution session.
3344
+ * @returns Absolute saved deadline in milliseconds.
3345
+ */
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
+ },
3357
+ /**
3358
+ * Awaits an application handler within its saved deadline and current claim.
3359
+ *
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.
3365
+ */
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
+ });
3388
+ try {
3389
+ const result = await Promise.race([work, cutoff]);
3390
+ if (signal.aborted ||
3391
+ (deadlineEpochMs !== undefined && Time.currentTimeMillis() >= deadlineEpochMs))
3392
+ throw new Error("Agent handler deadline or claim ended.");
3393
+ return result;
3394
+ }
3395
+ finally {
3396
+ clearTimeout(timer);
3397
+ signal.removeEventListener("abort", abort);
3398
+ }
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
+ };
3415
+ }
3416
+ /**
3417
+ * Executes one Process Manager Command against its loaded Entity state.
1755
3418
  */
1756
3419
  class ProcessManagerCommandExecution {
1757
3420
  #repository;
@@ -1764,7 +3427,7 @@ class ProcessManagerCommandExecution {
1764
3427
  *
1765
3428
  * @param repository Process Manager repository receiving the Command.
1766
3429
  * @param routing Registered Command routes and schemas.
1767
- * @param runtime Built context services.
3430
+ * @param runtime Built Bounded Context services.
1768
3431
  * @param command Source Command to execute.
1769
3432
  */
1770
3433
  constructor(repository, routing, runtime, command) {
@@ -1787,7 +3450,7 @@ class ProcessManagerCommandExecution {
1787
3450
  return undefined;
1788
3451
  const tenantOptions = RepositoryTenants.commandStandOptions(this.#runtime.context, this.#command);
1789
3452
  const loaded = await this.#support.load(intake.route.entityId, tenantOptions);
1790
- this.#publishDispatch(intake.route.entityId);
3453
+ await this.#publishDispatch(loaded, intake.route.entityId);
1791
3454
  try {
1792
3455
  const produced = await this.#invoke(loaded.entity, intake.assignee, intake.message, tenantOptions.tenantId);
1793
3456
  return await this.#commitAndPublish(loaded, tenantOptions, intake, produced);
@@ -1800,12 +3463,29 @@ class ProcessManagerCommandExecution {
1800
3463
  }
1801
3464
  }
1802
3465
  /**
1803
- * Publishes a best-effort Command dispatch diagnostic.
3466
+ * Persists required Agent dispatch history before the handler runs.
3467
+ * Process Manager diagnostic publication remains best effort.
1804
3468
  *
1805
- * @param entityId Target Process Manager identifier.
3469
+ * @param loaded Entity with mandatory Agent history when applicable.
3470
+ * @param entityId Target Process Manager or Agent identifier.
3471
+ * @returns Completion after the Agent audit append and publication.
1806
3472
  */
1807
- #publishDispatch(entityId) {
1808
- HandlerDispatchPublisher.command(this.#runtime, this.#repository, this.#command, entityId);
3473
+ async #publishDispatch(loaded, entityId) {
3474
+ if (this.#repository.entityFamily !== "agent") {
3475
+ HandlerDispatchPublisher.command(this.#runtime, this.#repository, this.#command, entityId);
3476
+ return;
3477
+ }
3478
+ const history = loaded.agentHistory;
3479
+ if (history === undefined)
3480
+ throw new Error("Agent history storage is required.");
3481
+ const event = HandlerDispatchPublisher.commandEvent(this.#runtime, this.#repository, this.#command, entityId);
3482
+ if (event.context?.timestamp === undefined)
3483
+ throw new Error("Agent System Event requires its original timestamp.");
3484
+ await history.append(entityId, create(AgentHistoryEntrySchema, {
3485
+ occurredAt: event.context.timestamp,
3486
+ item: { case: "systemEvent", value: event },
3487
+ }));
3488
+ HandlerDispatchPublisher.publishCommand(this.#runtime, event);
1809
3489
  }
1810
3490
  /**
1811
3491
  * Decodes a registered Command and finds its assignee.
@@ -1833,7 +3513,7 @@ class ProcessManagerCommandExecution {
1833
3513
  async #commitAndPublish(loaded, tenantOptions, intake, producedSignals) {
1834
3514
  const commands = this.#commandOutputs(intake.assignee, producedSignals);
1835
3515
  const events = this.#eventOutputs(intake.assignee, producedSignals, intake.route.entityId, RepositoryEntities.priorVersion(loaded.current));
1836
- const committed = await this.#support.commit(loaded, tenantOptions, events);
3516
+ const committed = await this.#support.commit(loaded, tenantOptions, events, events);
1837
3517
  if (!committed)
1838
3518
  return undefined;
1839
3519
  this.#publishChangedState(loaded, intake.route.entityId);
@@ -1895,7 +3575,7 @@ class ProcessManagerCommandExecution {
1895
3575
  /**
1896
3576
  * Binds query access for one Command handler invocation.
1897
3577
  *
1898
- * @param entity Process Manager instance.
3578
+ * @param entity Entity instance.
1899
3579
  * @param assignee Registered Command handler.
1900
3580
  * @param message Decoded Command message.
1901
3581
  * @param tenantId Tenant for query reads, when present.
@@ -1913,7 +3593,7 @@ class ProcessManagerCommandExecution {
1913
3593
  /**
1914
3594
  * Invokes a Command handler and checks delivery before the Entity transaction commits.
1915
3595
  *
1916
- * @param entity Process Manager instance to mutate.
3596
+ * @param entity Entity instance to mutate.
1917
3597
  * @param assignee Registered Command handler.
1918
3598
  * @param message Decoded Command message.
1919
3599
  * @returns Validated handler signals after commit.
@@ -1924,7 +3604,7 @@ class ProcessManagerCommandExecution {
1924
3604
  const produced = await EntityInvocation.invokeEntityMethod(entity, assignee.handler.methodName, message, assignee.handler.parameterCount, EntityInvocation.commandHandlerContext(this.#command));
1925
3605
  const signals = this.#support.normalizeProducedSignals(produced);
1926
3606
  if (signals.length === 0)
1927
- throw new Error("Repository Process Manager command handlers must return a signal.");
3607
+ throw new Error("Repository command handlers must return a signal.");
1928
3608
  RepositoryHandlers.requireDeclaredOutputs(assignee.handler, signals);
1929
3609
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, assignee.handler.kind === "command-assignment" && signals.length > 0));
1930
3610
  if (commit.status === "rejected") {
@@ -1938,11 +3618,11 @@ class ProcessManagerCommandExecution {
1938
3618
  }
1939
3619
  }
1940
3620
  /**
1941
- * Binds assignment results as Process Manager Events.
3621
+ * Binds assignment results as Entity Events.
1942
3622
  *
1943
3623
  * @param produced Domain Event messages from the handler.
1944
- * @param entityId Target Process Manager identifier.
1945
- * @param version Process Manager Version before this dispatch.
3624
+ * @param entityId Target Entity identifier.
3625
+ * @param version Entity Version before this dispatch.
1946
3626
  * @returns Frozen Event envelope list.
1947
3627
  */
1948
3628
  #bindProducedEvents(produced, entityId, version) {
@@ -1952,15 +3632,15 @@ class ProcessManagerCommandExecution {
1952
3632
  * Packs one declared Event with source and producer context.
1953
3633
  *
1954
3634
  * @param signal Domain Event message to pack.
1955
- * @param entityId Target Process Manager identifier.
1956
- * @param version Process Manager Version before this dispatch.
3635
+ * @param entityId Target Entity identifier.
3636
+ * @param version Entity Version before this dispatch.
1957
3637
  * @returns Bound Event envelope.
1958
3638
  */
1959
3639
  #bindProducedEvent(signal, entityId, version) {
1960
3640
  const typeName = EntityInvocation.messageTypeName(signal);
1961
3641
  const schema = this.#routing.producedEventSchemas.find((candidate) => candidate.typeName === typeName);
1962
3642
  if (schema === undefined) {
1963
- throw new Error(`Repository process-manager execution cannot pack event message "${typeName}".`);
3643
+ throw new Error(`Repository Entity handler cannot pack Event message "${typeName}".`);
1964
3644
  }
1965
3645
  const metadata = this.#runtime.signalMetadata.eventFromCommand(this.#command, {
1966
3646
  version: version.number,
@@ -1982,7 +3662,7 @@ class ProcessManagerCommandExecution {
1982
3662
  const typeName = EntityInvocation.messageTypeName(signal);
1983
3663
  const schema = this.#routing.producedCommandSchemas.find((candidate) => candidate.typeName === typeName);
1984
3664
  if (schema === undefined) {
1985
- throw new Error(`Repository process-manager execution cannot pack command message "${typeName}".`);
3665
+ throw new Error(`Repository Entity handler cannot pack Command message "${typeName}".`);
1986
3666
  }
1987
3667
  const metadata = this.#runtime.signalMetadata.commandFromCommand(this.#command);
1988
3668
  return create(CommandSchema, {
@@ -2013,12 +3693,12 @@ class ProcessManagerCommandExecution {
2013
3693
  #postEvents(events) {
2014
3694
  for (const event of events) {
2015
3695
  // spine-log-boundary: server.repository_event_follow_up
2016
- void this.#runtime.publisher.publishEvent(event);
3696
+ void this.#runtime.publisher.publishReactorEvent(event);
2017
3697
  }
2018
3698
  }
2019
3699
  }
2020
3700
  /**
2021
- * Runs Process Manager Event handlers and persists their results.
3701
+ * Runs Process Manager or Agent Event handlers and persists their results.
2022
3702
  */
2023
3703
  class ProcessManagerEventExecution {
2024
3704
  #repository;
@@ -2027,9 +3707,9 @@ class ProcessManagerEventExecution {
2027
3707
  #event;
2028
3708
  #support;
2029
3709
  /**
2030
- * Captures Process Manager routing, runtime, and source Event.
3710
+ * Captures Entity routing, runtime, and source Event.
2031
3711
  *
2032
- * @param repository Process Manager repository receiving the Event.
3712
+ * @param repository Entity repository receiving the Event.
2033
3713
  * @param routing Registered Event routes and schemas.
2034
3714
  * @param runtime Built context services.
2035
3715
  * @param event Source Event to execute.
@@ -2042,7 +3722,7 @@ class ProcessManagerEventExecution {
2042
3722
  this.#support = new ProcessManagerExecutionSupport(repository, runtime);
2043
3723
  }
2044
3724
  /**
2045
- * Delivers a routed Event to the durable Process Manager inbox.
3725
+ * Delivers a routed Event to the durable Entity inbox.
2046
3726
  *
2047
3727
  * @param acceptedRoute Route accepted before dispatch.
2048
3728
  * @returns Completion after inbox handoff.
@@ -2062,7 +3742,7 @@ class ProcessManagerEventExecution {
2062
3742
  /**
2063
3743
  * Delivers a routed Event for one target under the duplicate-dispatch guard.
2064
3744
  *
2065
- * @param entityId Target Process Manager identifier.
3745
+ * @param entityId Target Entity identifier.
2066
3746
  * @param acceptedRoute Route accepted before replay.
2067
3747
  * @returns Completion after guarded target execution.
2068
3748
  */
@@ -2099,7 +3779,7 @@ class ProcessManagerEventExecution {
2099
3779
  /**
2100
3780
  * Invokes handlers, commits state, and publishes resulting Events and Commands.
2101
3781
  *
2102
- * @param entityId Target Process Manager identifier.
3782
+ * @param entityId Target Entity identifier.
2103
3783
  * @param intake Decoded Event and matching handlers.
2104
3784
  * @returns Completion after accepted results are published.
2105
3785
  */
@@ -2113,7 +3793,7 @@ class ProcessManagerEventExecution {
2113
3793
  DispatchGuards.guardedJournalEvent(this.#repository, this.#event, entityId),
2114
3794
  ...events,
2115
3795
  ];
2116
- const committed = await this.#support.commit(loaded, tenantOptions, diagnostics);
3796
+ const committed = await this.#support.commit(loaded, tenantOptions, diagnostics, events);
2117
3797
  if (!committed)
2118
3798
  return;
2119
3799
  this.#publishChangedState(loaded, entityId);
@@ -2121,10 +3801,10 @@ class ProcessManagerEventExecution {
2121
3801
  await this.#postCommands(commands);
2122
3802
  }
2123
3803
  /**
2124
- * Publishes a state-change System Event when the Process Manager changed.
3804
+ * Publishes a state-change System Event when the Entity changed.
2125
3805
  *
2126
- * @param loaded Process Manager before and after handling.
2127
- * @param entityId Target Process Manager identifier.
3806
+ * @param loaded Entity before and after handling.
3807
+ * @param entityId Target Entity identifier.
2128
3808
  */
2129
3809
  #publishChangedState(loaded, entityId) {
2130
3810
  if (!RepositoryEntities.repositoryChanged(loaded.entity))
@@ -2153,8 +3833,8 @@ class ProcessManagerEventExecution {
2153
3833
  /**
2154
3834
  * Invokes Event handlers in one transaction with temporary query access.
2155
3835
  *
2156
- * @param entityId Target Process Manager identifier.
2157
- * @param entity Process Manager instance.
3836
+ * @param entityId Target Entity identifier.
3837
+ * @param entity Entity instance.
2158
3838
  * @param intake Decoded Event and matching handlers.
2159
3839
  * @param tenantId Tenant for query reads, when present.
2160
3840
  * @returns Validated Command and Event message lists.
@@ -2184,8 +3864,8 @@ class ProcessManagerEventExecution {
2184
3864
  /**
2185
3865
  * Invokes reactors and Command producers, collecting their separate outputs.
2186
3866
  *
2187
- * @param entityId Target Process Manager identifier.
2188
- * @param entity Process Manager instance.
3867
+ * @param entityId Target Entity identifier.
3868
+ * @param entity Entity instance.
2189
3869
  * @param intake Decoded Event and matching handlers.
2190
3870
  * @param context Copied source Event context.
2191
3871
  * @param events Output list for Event messages.
@@ -2193,15 +3873,23 @@ class ProcessManagerEventExecution {
2193
3873
  * @returns Completion after matching handlers run.
2194
3874
  */
2195
3875
  async #invokeEventHandlers(entityId, entity, intake, context, events, commands) {
2196
- if (intake.reactors.length > 0)
2197
- HandlerDispatchPublisher.reactor(this.#runtime, this.#repository, this.#event, entityId);
3876
+ if (intake.reactors.length > 0) {
3877
+ if (this.#repository.entityFamily === "agent") {
3878
+ const diagnostic = HandlerDispatchPublisher.reactorEvent(this.#runtime, this.#repository, this.#event, entityId);
3879
+ await AgentHistoryReads.appendSystem(entity, diagnostic);
3880
+ HandlerDispatchPublisher.publishReactor(this.#runtime, diagnostic);
3881
+ }
3882
+ else {
3883
+ HandlerDispatchPublisher.reactor(this.#runtime, this.#repository, this.#event, entityId);
3884
+ }
3885
+ }
2198
3886
  await this.#invokeHandlersInto(entity, intake.reactors, intake.message, context, events);
2199
3887
  await this.#invokeHandlersInto(entity, intake.commanders, intake.message, context, commands);
2200
3888
  }
2201
3889
  /**
2202
3890
  * Invokes each selected handler and appends its declared results.
2203
3891
  *
2204
- * @param entity Process Manager instance.
3892
+ * @param entity Entity instance.
2205
3893
  * @param handlers Handler registrations to invoke.
2206
3894
  * @param message Decoded source Event message.
2207
3895
  * @param context Copied source Event context.
@@ -2219,7 +3907,7 @@ class ProcessManagerEventExecution {
2219
3907
  /**
2220
3908
  * Checks delivery immediately before the Entity transaction commits and raises transition rejection.
2221
3909
  *
2222
- * @param entity Process Manager instance to commit.
3910
+ * @param entity Entity instance to commit.
2223
3911
  * @param producedEvents Whether handlers produced Events.
2224
3912
  * @returns Completion after a successful commit.
2225
3913
  */
@@ -2229,11 +3917,11 @@ class ProcessManagerEventExecution {
2229
3917
  throw new TransitionValidationError(commit.validation.error);
2230
3918
  }
2231
3919
  /**
2232
- * Binds reactor results as Process Manager Events.
3920
+ * Binds reactor results as Entity Events.
2233
3921
  *
2234
3922
  * @param produced Domain Event messages from handlers.
2235
- * @param entityId Target Process Manager identifier.
2236
- * @param version Process Manager Version before this dispatch.
3923
+ * @param entityId Target Entity identifier.
3924
+ * @param version Entity Version before this dispatch.
2237
3925
  * @returns Frozen Event envelope list.
2238
3926
  */
2239
3927
  #bindProducedEvents(produced, entityId, version) {
@@ -2243,15 +3931,15 @@ class ProcessManagerEventExecution {
2243
3931
  * Packs one declared Event with source and producer context.
2244
3932
  *
2245
3933
  * @param signal Domain Event message to pack.
2246
- * @param entityId Target Process Manager identifier.
2247
- * @param version Process Manager Version before this dispatch.
3934
+ * @param entityId Target Entity identifier.
3935
+ * @param version Entity Version before this dispatch.
2248
3936
  * @returns Bound Event envelope.
2249
3937
  */
2250
3938
  #bindProducedEvent(signal, entityId, version) {
2251
3939
  const typeName = EntityInvocation.messageTypeName(signal);
2252
3940
  const schema = this.#routing.producedEventSchemas.find((candidate) => candidate.typeName === typeName);
2253
3941
  if (schema === undefined) {
2254
- throw new Error(`Repository process-manager execution cannot pack event message "${typeName}".`);
3942
+ throw new Error(`Repository Entity handler cannot pack Event message "${typeName}".`);
2255
3943
  }
2256
3944
  const metadata = this.#runtime.signalMetadata.eventFromEvent(this.#event, {
2257
3945
  version: version.number,
@@ -2273,7 +3961,7 @@ class ProcessManagerEventExecution {
2273
3961
  const typeName = EntityInvocation.messageTypeName(signal);
2274
3962
  const schema = this.#routing.producedCommandSchemas.find((candidate) => candidate.typeName === typeName);
2275
3963
  if (schema === undefined) {
2276
- throw new Error(`Repository process-manager execution cannot pack command message "${typeName}".`);
3964
+ throw new Error(`Repository Entity handler cannot pack Command message "${typeName}".`);
2277
3965
  }
2278
3966
  const metadata = this.#runtime.signalMetadata.commandFromEvent(this.#event);
2279
3967
  return create(CommandSchema, {
@@ -2302,7 +3990,7 @@ class ProcessManagerEventExecution {
2302
3990
  #postEvents(events) {
2303
3991
  for (const event of events) {
2304
3992
  // spine-log-boundary: server.process_manager_event_follow_up
2305
- void this.#runtime.publisher.publishEvent(event);
3993
+ void this.#runtime.publisher.publishReactorEvent(event);
2306
3994
  }
2307
3995
  }
2308
3996
  }
@@ -2323,7 +4011,7 @@ const RepositoryIdentity = {
2323
4011
  const displayName = RepositoryIdentity.entityTypeName(entityType);
2324
4012
  const schema = RepositoryIdentity.readRepositorySchemaOption(options, displayName, entityFamily);
2325
4013
  const metadata = RepositoryIdentity.describeRepositoryEntityMetadata(displayName, entityFamily, schema);
2326
- if (metadata.kind !== entityFamily) {
4014
+ if (metadata.kind !== (entityFamily === "agent" ? "entity" : entityFamily)) {
2327
4015
  throw new RepositoryIdentityError("ENTITY_SCHEMA_KIND_MISMATCH", `Repository entity type "${displayName}" does not match the supplied state schema.`);
2328
4016
  }
2329
4017
  if (options.stateUpdateRouting !== undefined && entityFamily !== "projection") {
@@ -2340,34 +4028,34 @@ const RepositoryIdentity = {
2340
4028
  requireEntityType(options) {
2341
4029
  if (!RepositoryIdentity.isRepositoryOptionsObject(options)) {
2342
4030
  throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "Repository options must be a non-null object with an entity type class constructor " +
2343
- "extending Aggregate, Projection, or ProcessManager.");
4031
+ "extending Aggregate, Projection, ProcessManager, or Agent.");
2344
4032
  }
2345
4033
  const entityType = RepositoryIdentity.readEntityTypeOption(options);
2346
4034
  if (typeof entityType !== "function" || !RepositoryIdentity.isClassConstructor(entityType)) {
2347
4035
  throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", `Repository entity type "${RepositoryIdentity.entityTypeName(entityType)}" must be a class constructor ` +
2348
- "extending Aggregate, Projection, or ProcessManager.");
4036
+ "extending Aggregate, Projection, ProcessManager, or Agent.");
2349
4037
  }
2350
4038
  return entityType;
2351
4039
  },
2352
4040
  /**
2353
- * Validates and returns Aggregate, Projection, or Process Manager inheritance.
4041
+ * Validates and returns Aggregate, Projection, Process Manager, or Agent inheritance.
2354
4042
  *
2355
4043
  * @param entityType Validated class constructor.
2356
- * @returns Aggregate, Projection, or Process Manager.
4044
+ * @returns Aggregate, Projection, Process Manager, or Agent.
2357
4045
  */
2358
4046
  requireEntityFamily(entityType) {
2359
4047
  const family = RepositoryIdentity.resolveRepositoryEntityFamily(entityType);
2360
4048
  if (family !== undefined)
2361
4049
  return family;
2362
4050
  throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", `Repository entity type "${RepositoryIdentity.entityTypeName(entityType)}" must extend ` +
2363
- "Aggregate, Projection, or ProcessManager.");
4051
+ "Aggregate, Projection, ProcessManager, or Agent.");
2364
4052
  },
2365
4053
  /**
2366
4054
  * Creates a frozen identity snapshot from validated Entity metadata.
2367
4055
  *
2368
4056
  * @typeParam EntityType Concrete Entity constructor represented by the snapshot.
2369
4057
  * @param entityType Entity constructor.
2370
- * @param entityFamily Aggregate, Projection, or Process Manager family.
4058
+ * @param entityFamily Aggregate, Projection, Process Manager, or Agent family.
2371
4059
  * @param metadata Descriptor-derived state metadata.
2372
4060
  * @returns Copy-safe repository identity snapshot.
2373
4061
  */
@@ -2421,7 +4109,7 @@ const RepositoryIdentity = {
2421
4109
  }
2422
4110
  catch {
2423
4111
  throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "Repository options entityType must be readable and resolve to a class constructor " +
2424
- "extending Aggregate, Projection, or ProcessManager.");
4112
+ "extending Aggregate, Projection, ProcessManager, or Agent.");
2425
4113
  }
2426
4114
  },
2427
4115
  /**
@@ -2471,22 +4159,21 @@ const RepositoryIdentity = {
2471
4159
  return undefined;
2472
4160
  }
2473
4161
  const runtimeEntityType = entityType;
2474
- if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Aggregate, Aggregate.prototype)) {
4162
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Aggregate, Aggregate.prototype))
2475
4163
  return "aggregate";
2476
- }
2477
- if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Projection, Projection.prototype)) {
4164
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Projection, Projection.prototype))
2478
4165
  return "projection";
2479
- }
2480
- if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, ProcessManager, ProcessManager.prototype)) {
4166
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, ProcessManager, ProcessManager.prototype))
2481
4167
  return "process-manager";
2482
- }
4168
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Agent, Agent.prototype))
4169
+ return "agent";
2483
4170
  return undefined;
2484
4171
  },
2485
4172
  /**
2486
4173
  * Checks constructor and prototype inheritance against an Entity family.
2487
4174
  *
2488
4175
  * @param entityType Candidate constructor.
2489
- * @param familyConstructor Aggregate, Projection, or Process Manager constructor.
4176
+ * @param familyConstructor Entity family constructor.
2490
4177
  * @param familyPrototype Prototype for the same family.
2491
4178
  * @returns Whether both inheritance paths match.
2492
4179
  */
@@ -2625,7 +4312,23 @@ const EntityInvocation = {
2625
4312
  if (typeof method !== "function") {
2626
4313
  throw new TypeError(`Repository entity execution requires method "${methodName}".`);
2627
4314
  }
2628
- return Reflect.apply(method, entity, parameterCount === 2 ? [message, context] : [message]);
4315
+ let result;
4316
+ try {
4317
+ result = Reflect.apply(method, entity, parameterCount === 2 ? [message, context] : [message]);
4318
+ }
4319
+ catch (error) {
4320
+ receptionObservation.recordHandlerFailure(error);
4321
+ throw error;
4322
+ }
4323
+ if (result !== null &&
4324
+ (typeof result === "object" || typeof result === "function") &&
4325
+ typeof result.then === "function") {
4326
+ return Promise.resolve(result).catch((error) => {
4327
+ receptionObservation.recordHandlerFailure(error);
4328
+ throw error;
4329
+ });
4330
+ }
4331
+ return result;
2629
4332
  },
2630
4333
  /**
2631
4334
  * Copies a Command context or creates an empty context when absent.
@@ -2697,7 +4400,36 @@ const RepositoryEntities = {
2697
4400
  const storageInput = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
2698
4401
  const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, storageInput);
2699
4402
  RepositoryHistoryInternals.bindEntityHistory(entity, storage, entityId, repository.stateSchema);
2700
- return { commits: storage.commits, events: storage.events, storageInput };
4403
+ if (repository.entityFamily === "agent")
4404
+ this.bindAgentHistory(entity, entityId, storageInput, storage.agentHistory);
4405
+ return {
4406
+ commits: storage.commits,
4407
+ events: storage.events,
4408
+ ...(storage.agentHistory === undefined ? {} : { agentHistory: storage.agentHistory }),
4409
+ storageInput,
4410
+ };
4411
+ },
4412
+ /**
4413
+ * Attaches the indexed history port to one restored Agent.
4414
+ *
4415
+ * @param entity Restored Agent instance.
4416
+ * @param entityId Typed Agent identifier.
4417
+ * @param input Entity storage scope.
4418
+ * @param history Mandatory Agent history port.
4419
+ */
4420
+ bindAgentHistory(entity, entityId, input, history) {
4421
+ if (history === undefined)
4422
+ throw new Error("Agent history storage is required.");
4423
+ AgentHistoryReads.bind(entity, {
4424
+ storage: history,
4425
+ entityId,
4426
+ scope: {
4427
+ context: input.context.name,
4428
+ tenant: JSON.stringify(input.context.tenantId ?? null),
4429
+ repository: input.sourceType.typeName,
4430
+ entity: input.id.key(entityId),
4431
+ },
4432
+ });
2701
4433
  },
2702
4434
  /**
2703
4435
  * Restores an Entity from Stand or creates a fresh instance at Version zero.
@@ -2729,15 +4461,17 @@ const RepositoryEntities = {
2729
4461
  * @param stored Prior Stand snapshot, when present.
2730
4462
  * @param commits Durable commit storage.
2731
4463
  * @param events Durable diagnostic Event history.
4464
+ * @param agentHistory Mandatory Agent history when this is an Agent.
2732
4465
  * @param storageInput Entity storage specification.
2733
4466
  * @param schema Entity state schema.
2734
4467
  * @param entityId Entity identifier.
2735
4468
  * @returns The loaded Entity and prior record.
2736
4469
  */
2737
- loaded(entity, stored, commits, events, storageInput, schema, entityId) {
4470
+ loaded(entity, stored, commits, events, agentHistory, storageInput, schema, entityId) {
2738
4471
  return Object.freeze({
2739
4472
  commits,
2740
4473
  events,
4474
+ ...(agentHistory === undefined ? {} : { agentHistory }),
2741
4475
  current: stored === undefined
2742
4476
  ? undefined
2743
4477
  : EntityRecords.pack(schema, entityId, stored.state, stored.versionMessage, {
@@ -2863,8 +4597,30 @@ const RepositorySignals = {
2863
4597
  * @returns Deferred rejection Event publication.
2864
4598
  */
2865
4599
  postRejectionEvent(runtime, repository, command, entityId, rejection) {
4600
+ const event = this.rejectionEvent(runtime, repository, command, entityId, rejection);
4601
+ return async () => {
4602
+ try {
4603
+ // spine-log-boundary: server.repository_rejection_follow_up
4604
+ await runtime.publisher.publishRejectionEvent(event);
4605
+ }
4606
+ catch (error) {
4607
+ runtime.publisher.reportFailure("event", event, error);
4608
+ }
4609
+ };
4610
+ },
4611
+ /**
4612
+ * Creates the original declared rejection envelope without publishing it.
4613
+ *
4614
+ * @param runtime Bounded Context storage and publication services.
4615
+ * @param repository Registered Entity repository for this operation.
4616
+ * @param command Original typed Command envelope.
4617
+ * @param entityId Typed recipient Entity identifier.
4618
+ * @param rejection Declared rejection from the selected handler.
4619
+ * @returns Original declared-rejection Event envelope.
4620
+ */
4621
+ rejectionEvent(runtime, repository, command, entityId, rejection) {
2866
4622
  const metadata = runtime.signalMetadata.eventFromCommand(command, {});
2867
- const event = create(EventSchema, {
4623
+ return create(EventSchema, {
2868
4624
  id: metadata.id,
2869
4625
  message: AnyMessages.pack(rejection.schema, rejection.messageThrown()),
2870
4626
  context: create(EventContextSchema, {
@@ -2875,15 +4631,6 @@ const RepositorySignals = {
2875
4631
  }),
2876
4632
  }),
2877
4633
  });
2878
- return async () => {
2879
- try {
2880
- // spine-log-boundary: server.repository_rejection_follow_up
2881
- await runtime.publisher.publishRejectionEvent(event);
2882
- }
2883
- catch (error) {
2884
- runtime.publisher.reportFailure("event", event, error);
2885
- }
2886
- };
2887
4634
  },
2888
4635
  /**
2889
4636
  * Validates a nonempty Command ID for Event origin metadata.
@@ -3200,20 +4947,40 @@ class HandlerDispatchPublishing {
3200
4947
  */
3201
4948
  command(runtime, repository, command, entityId) {
3202
4949
  try {
3203
- const context = runtime.signalMetadata.eventContext({
3204
- origin: runtime.signalMetadata.originFromCommand(command),
3205
- });
3206
- const event = create(EventSchema, {
3207
- id: runtime.signalMetadata.eventId(),
3208
- message: AnyMessages.pack(EntityLog.CommandDispatchedToHandlerSchema, this.#message(repository, command, entityId, context.timestamp)),
3209
- context,
3210
- });
3211
- this.#post(runtime, EntityLog.CommandDispatchedToHandlerSchema, event);
4950
+ this.publishCommand(runtime, this.commandEvent(runtime, repository, command, entityId));
3212
4951
  }
3213
4952
  catch (error) {
3214
4953
  runtime.publisher.reportFailure("event", create(EventSchema), error);
3215
4954
  }
3216
4955
  }
4956
+ /**
4957
+ * Builds the genuine Command dispatch diagnostic before its publication.
4958
+ *
4959
+ * @param runtime Bounded Context signal metadata.
4960
+ * @param repository Receiving repository.
4961
+ * @param command Source Command.
4962
+ * @param entityId Target Entity identifier.
4963
+ * @returns Original System Event envelope.
4964
+ */
4965
+ commandEvent(runtime, repository, command, entityId) {
4966
+ const context = runtime.signalMetadata.eventContext({
4967
+ origin: runtime.signalMetadata.originFromCommand(command),
4968
+ });
4969
+ return create(EventSchema, {
4970
+ id: runtime.signalMetadata.eventId(),
4971
+ message: AnyMessages.pack(EntityLog.CommandDispatchedToHandlerSchema, this.#message(repository, command, entityId, context.timestamp)),
4972
+ context,
4973
+ });
4974
+ }
4975
+ /**
4976
+ * Sends the exact System envelope already retained for an Agent.
4977
+ *
4978
+ * @param runtime Bounded Context publication services.
4979
+ * @param event Original System Event envelope.
4980
+ */
4981
+ publishCommand(runtime, event) {
4982
+ this.#post(runtime, EntityLog.CommandDispatchedToHandlerSchema, event);
4983
+ }
3217
4984
  /**
3218
4985
  * Publishes a best-effort EventDispatchedToSubscriber diagnostic.
3219
4986
  *
@@ -3236,6 +5003,34 @@ class HandlerDispatchPublishing {
3236
5003
  reactor(runtime, repository, event, entityId) {
3237
5004
  this.#publishEvent(runtime, repository, event, entityId, EntityLog.EventDispatchedToReactorSchema);
3238
5005
  }
5006
+ /**
5007
+ * Builds the genuine Event-to-reactor diagnostic with its original envelope.
5008
+ *
5009
+ * @param runtime Bounded Context signal metadata.
5010
+ * @param repository Receiving repository.
5011
+ * @param source Source Event.
5012
+ * @param entityId Target Entity identifier.
5013
+ * @returns Original System Event envelope.
5014
+ */
5015
+ reactorEvent(runtime, repository, source, entityId) {
5016
+ const context = runtime.signalMetadata.eventContext({
5017
+ origin: runtime.signalMetadata.originFromEvent(source),
5018
+ });
5019
+ return create(EventSchema, {
5020
+ id: runtime.signalMetadata.eventId(),
5021
+ message: AnyMessages.pack(EntityLog.EventDispatchedToReactorSchema, this.#eventMessage(EntityLog.EventDispatchedToReactorSchema, repository, source, entityId, context.timestamp)),
5022
+ context,
5023
+ });
5024
+ }
5025
+ /**
5026
+ * Publishes the same Event-to-reactor diagnostic retained by Agent history.
5027
+ *
5028
+ * @param runtime Bounded Context publication services.
5029
+ * @param event Original System Event envelope.
5030
+ */
5031
+ publishReactor(runtime, event) {
5032
+ this.#post(runtime, EntityLog.EventDispatchedToReactorSchema, event);
5033
+ }
3239
5034
  /**
3240
5035
  * Builds and publishes one subscriber or reactor dispatch diagnostic.
3241
5036
  *
@@ -3751,9 +5546,10 @@ const RepositoryHandlers = {
3751
5546
  throw new RepositoryIdentityError("ENTITY_SCHEMA_KIND_MISMATCH", `Repository entity type "${entityType.name}" does not match the supplied handler metadata.`);
3752
5547
  }
3753
5548
  if (metadata.kind !== "process-manager" &&
5549
+ metadata.kind !== "entity" &&
3754
5550
  (handlersMetadata.commandSubstitutions.length > 0 ||
3755
5551
  handlersMetadata.commandReactions.length > 0)) {
3756
- throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "Only Process Manager repositories support @Command handlers.");
5552
+ throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "Only Process Manager and Agent repositories support @Command handlers.");
3757
5553
  }
3758
5554
  }
3759
5555
  },
@@ -4711,24 +6507,64 @@ const RepositoryStorage = {
4711
6507
  * @typeParam S Generated Entity state type.
4712
6508
  * @param factory Provider storage factory.
4713
6509
  * @param input Entity storage location and schema.
6510
+ * @param agent Whether to open mandatory Agent history.
4714
6511
  * @returns Storage ports closed together by one handle.
4715
6512
  */
4716
- openEntityStorage(factory, input) {
6513
+ openEntityStorage(factory, input, agent = false) {
4717
6514
  const candidate = factory;
4718
- if (candidate.createEntityStorage === undefined) {
4719
- throw new Error("StorageFactory does not provide the required atomic Entity commit storage seam.");
4720
- }
6515
+ if (candidate.createEntityStorage === undefined)
6516
+ throw new Error("StorageFactory does not provide required atomic Entity commit storage.");
4721
6517
  const entity = candidate.createEntityStorage(input);
4722
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;
6528
+ }
4723
6529
  return {
4724
6530
  ...entity,
4725
6531
  commits,
6532
+ ...ports,
4726
6533
  close: () => {
6534
+ ports?.agentExecution.close();
6535
+ ports?.agentHistory.close();
4727
6536
  commits.close();
4728
6537
  entity.close();
4729
6538
  },
4730
6539
  };
4731
6540
  },
6541
+ /**
6542
+ * Opens both mandatory Agent ports and closes the first if the second fails.
6543
+ *
6544
+ * @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.
6549
+ */
6550
+ openAgentPorts(factory, input) {
6551
+ const agentHistory = AgentHistoryStorageFactories.create(factory, {
6552
+ context: input.context,
6553
+ stateType: input.sourceType.typeName,
6554
+ id: { key: input.id.key },
6555
+ });
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;
6566
+ }
6567
+ },
4732
6568
  /**
4733
6569
  * Returns one shared Entity storage handle per context and state type.
4734
6570
  *
@@ -4742,7 +6578,7 @@ const RepositoryStorage = {
4742
6578
  openRepositoryEntityStorage(repository, factory, input) {
4743
6579
  const key = JSON.stringify({ context: input.context, state: input.sourceType.typeName });
4744
6580
  let handles = repositoryEntityHandles.get(repository);
4745
- const handle = RepositoryStorage.openEntityStorage(factory, input);
6581
+ const handle = RepositoryStorage.openEntityStorage(factory, input, repository.entityFamily === "agent");
4746
6582
  if (handles === undefined) {
4747
6583
  handles = new Map();
4748
6584
  repositoryEntityHandles.set(repository, handles);
@@ -5202,21 +7038,40 @@ const DispatchGuards = {
5202
7038
  async dispatchOnce(repository, runtime, event, entityId, eventId, journalEventId, depth, guard, dispatch) {
5203
7039
  if (guard.completed.has(eventId))
5204
7040
  return;
5205
- const storage = RepositoryStorage.openEntityStorage(runtime.storageFactory, RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForEvent(runtime.context, event)));
5206
- let persisted;
5207
- try {
5208
- persisted = await storage.events.backward(entityId, depth);
5209
- }
5210
- finally {
5211
- storage.close();
7041
+ if (repository.entityFamily === "agent") {
7042
+ await dispatch();
7043
+ DispatchGuards.rememberGuardCompletion(guard, eventId, depth);
7044
+ return;
5212
7045
  }
5213
- if (persisted.some((candidate) => candidate.id?.value === journalEventId)) {
7046
+ if (await this.wasDispatched(repository, runtime, event, entityId, journalEventId, depth)) {
5214
7047
  DispatchGuards.rememberGuardCompletion(guard, eventId, depth);
5215
7048
  return;
5216
7049
  }
5217
7050
  await dispatch();
5218
7051
  DispatchGuards.rememberGuardCompletion(guard, eventId, depth);
5219
7052
  },
7053
+ /**
7054
+ * Finds an already dispatched Process Manager Event in retained diagnostic history.
7055
+ *
7056
+ * @param repository Process Manager repository.
7057
+ * @param runtime Repository storage runtime.
7058
+ * @param event Incoming Event envelope.
7059
+ * @param entityId Target Process Manager identifier.
7060
+ * @param journalEventId Target-specific diagnostic identifier.
7061
+ * @param depth Maximum retained history read.
7062
+ * @returns Whether the Event was already dispatched.
7063
+ */
7064
+ async wasDispatched(repository, runtime, event, entityId, journalEventId, depth) {
7065
+ const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForEvent(runtime.context, event));
7066
+ const storage = RepositoryStorage.openEntityStorage(runtime.storageFactory, input);
7067
+ try {
7068
+ const persisted = await storage.events.backward(entityId, depth);
7069
+ return persisted.some((candidate) => candidate.id?.value === journalEventId);
7070
+ }
7071
+ finally {
7072
+ storage.close();
7073
+ }
7074
+ },
5220
7075
  /**
5221
7076
  * Records a successful delivery and discards the oldest excess identifiers.
5222
7077
  *
@@ -5461,6 +7316,10 @@ const InboxReplay = {
5461
7316
  * @returns Any Command follow-up work that must wait for Inbox completion.
5462
7317
  */
5463
7318
  async replayPmInbox(repository, routing, message, deliveryTenantId) {
7319
+ if (repository.entityFamily === "agent") {
7320
+ await InboxReplay.replayAgentInbox(repository, routing, message, deliveryTenantId);
7321
+ return undefined;
7322
+ }
5464
7323
  if (message.label === "HANDLE_COMMAND") {
5465
7324
  return await InboxReplay.replayProcessManagerCommand(repository, routing, message, deliveryTenantId);
5466
7325
  }
@@ -5470,6 +7329,152 @@ const InboxReplay = {
5470
7329
  }
5471
7330
  throw new Error(`Entity Inbox replay does not handle "${message.label}" messages.`);
5472
7331
  },
7332
+ /**
7333
+ * Persists selected Agent work before the normal Inbox acknowledges delivery.
7334
+ *
7335
+ * @param repository Agent repository receiving the original signal.
7336
+ * @param routing Registered handler routes and schemas.
7337
+ * @param message Original durable Inbox message.
7338
+ * @param deliveryTenantId Tenant selected by delivery.
7339
+ * @returns When the fenced repository operation completes.
7340
+ */
7341
+ async replayAgentInbox(repository, routing, message, deliveryTenantId) {
7342
+ const runtime = repositoryRuntimes.get(repository);
7343
+ if (runtime === undefined)
7344
+ throw new Error("Agent Inbox replay requires a bound runtime.");
7345
+ if (message.label === "HANDLE_COMMAND") {
7346
+ await InboxReplay.replayAgentCommand(repository, routing, runtime, message, deliveryTenantId);
7347
+ return;
7348
+ }
7349
+ if (message.label !== "REACT_UPON_EVENT")
7350
+ throw new Error(`Agent Inbox replay does not handle "${message.label}" messages.`);
7351
+ await InboxReplay.replayAgentEvent(repository, routing, runtime, message, deliveryTenantId);
7352
+ },
7353
+ /**
7354
+ * Admits a Command using its stored target and registered assignee.
7355
+ *
7356
+ * @param repository Agent repository.
7357
+ * @param routing Registered Command routes.
7358
+ * @param runtime Bounded Context services.
7359
+ * @param message Original Inbox row.
7360
+ * @param tenantId Delivery tenant.
7361
+ * @returns When the fenced repository operation completes.
7362
+ */
7363
+ async replayAgentCommand(repository, routing, runtime, message, tenantId) {
7364
+ const command = InboxMessages.readInboxCommand(message);
7365
+ InboxReplay.validateReplayTenant(runtime.context, tenantId, command);
7366
+ InboxReplay.validateReplayedCommandPayload(routing, command);
7367
+ const route = InboxReplay.replayCommandRoute(repository, routing, message, command);
7368
+ const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
7369
+ if (assignee === undefined)
7370
+ 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]);
7372
+ },
7373
+ /**
7374
+ * Admits matching Event handlers from the stored target without rerouting.
7375
+ *
7376
+ * @param repository Agent repository.
7377
+ * @param routing Registered Event routes.
7378
+ * @param runtime Bounded Context services.
7379
+ * @param message Original Inbox row.
7380
+ * @param tenantId Delivery tenant.
7381
+ * @returns When the fenced repository operation completes.
7382
+ */
7383
+ async replayAgentEvent(repository, routing, runtime, message, tenantId) {
7384
+ const event = InboxMessages.readPmInboxEvent(message);
7385
+ InboxReplay.validatePmReplayTenant(runtime.context, tenantId, event);
7386
+ InboxReplay.validateReplayedEventPayload(routing, event, "Agent Inbox replay requires an Event payload.");
7387
+ const route = InboxReplay.replayEventRoute(repository, routing, message, event, "Agent Inbox replay");
7388
+ const handlers = InboxReplay.agentEventHandlers(routing, route.messageFullTypeName, event);
7389
+ if (handlers.length === 0)
7390
+ return;
7391
+ await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityIds[0], { kind: "event", value: event }, runtime.signalMetadata.originFromEvent(event).actorContext ?? create(ActorContextSchema), handlers);
7392
+ },
7393
+ /**
7394
+ * Restores selected Event and Command reactions from the original payload.
7395
+ *
7396
+ * @param routing Registered generated handler routing metadata.
7397
+ * @param typeName Generated message type name.
7398
+ * @param event Original typed Event envelope.
7399
+ * @returns Generated reactions selected for the accepted Event.
7400
+ */
7401
+ agentEventHandlers(routing, typeName, event) {
7402
+ const packed = EntityInvocation.requireSignalMessage(event.message, "event");
7403
+ const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
7404
+ const value = EntityInvocation.unpackRequired(packed, schema, "event");
7405
+ const external = event.context?.external === true;
7406
+ return [
7407
+ ...routing.eventReactors(typeName, value, external),
7408
+ ...routing.commandReactions(typeName, value, external),
7409
+ ];
7410
+ },
7411
+ /**
7412
+ * Opens the actual tenant handle and records a selected invocation before handoff.
7413
+ *
7414
+ * @param repository Registered Agent repository.
7415
+ * @param runtime Bounded Context services including the effective registry.
7416
+ * @param message Durable Inbox receipt.
7417
+ * @param tenantId Selected delivery tenant.
7418
+ * @param entityId Typed target from the stored route.
7419
+ * @param signal Original source envelope.
7420
+ * @param actor Original actor metadata.
7421
+ * @param handlers Selected handler metadata in execution order.
7422
+ * @returns When the fenced repository operation completes.
7423
+ */
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,
7432
+ signal,
7433
+ actor,
7434
+ inbox: message,
7435
+ handlers,
7436
+ codeRevision: revisions.codeRevision,
7437
+ schemaRevision: revisions.schema,
7438
+ policyRevision: revisions.policy,
7439
+ }));
7440
+ runtime.wakeAcceptedAgent(repository, tenantId);
7441
+ },
7442
+ /**
7443
+ * Opens the actual tenant's mandatory execution handle.
7444
+ *
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.
7451
+ */
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;
7457
+ },
7458
+ /**
7459
+ * Records the registered code, schema, and AI policy before Inbox handoff.
7460
+ *
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),
7476
+ };
7477
+ },
5473
7478
  /**
5474
7479
  * Dispatches a stored Process Manager Command after tenant and payload validation.
5475
7480
  *
@@ -5780,9 +7785,10 @@ const InboxHandoff = {
5780
7785
  * @param runtime Inbox, storage, tenant mode, and delivery strategy.
5781
7786
  * @param command Command envelope to retain and deliver.
5782
7787
  * @returns Completion of Inbox receipt and its selected delivery operation.
7788
+ * @param acceptedRoute Recipient route saved when the signal was accepted.
5783
7789
  */
5784
- async handoffEntityCommand(repository, runtime, command) {
5785
- const route = await repository.routeCommand(command);
7790
+ async handoffEntityCommand(repository, runtime, command, acceptedRoute) {
7791
+ const route = acceptedRoute ?? (await repository.routeCommand(command));
5786
7792
  const commandId = RepositorySignals.requireCommandId(command);
5787
7793
  const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
5788
7794
  const deliveryTenantId = RepositoryTenants.requireCommandTenant(runtime.context, command);
@@ -5903,6 +7909,182 @@ const InboxHandoff = {
5903
7909
  },
5904
7910
  };
5905
7911
  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);
5906
8088
  /**
5907
8089
  * Connects bus dispatchers and Inbox replay to repository execution.
5908
8090
  */
@@ -5931,12 +8113,13 @@ const RepositoryDispatch = {
5931
8113
  * @returns The dispatcher, or undefined when no Command schema is registered.
5932
8114
  */
5933
8115
  commandDispatcher(repository, routing) {
5934
- return routing.commandSchemas.length === 0
5935
- ? undefined
5936
- : Object.freeze({
5937
- messageSchemas: () => routing.commandSchemas,
5938
- dispatch: (command) => RepositoryDispatch.dispatchRepositoryCommand(repository, routing, command),
5939
- });
8116
+ if (routing.commandSchemas.length === 0)
8117
+ return undefined;
8118
+ const dispatcher = Object.freeze({
8119
+ messageSchemas: () => routing.commandSchemas,
8120
+ dispatch: (command) => RepositoryDispatch.dispatchRepositoryCommand(repository, routing, command),
8121
+ });
8122
+ return SavedDispatcherBindings.command(dispatcher, SavedRepositoryDispatch.commandBinding(repository, routing));
5940
8123
  },
5941
8124
  /**
5942
8125
  * Creates an Event dispatcher that preserves routes computed during acceptance.
@@ -5961,7 +8144,8 @@ const RepositoryDispatch = {
5961
8144
  return RepositoryDispatch.dispatchRepositoryEvent(repository, routing, event, route);
5962
8145
  },
5963
8146
  });
5964
- return EventDispatcherOriginSchemas.define(dispatcher, routing.domesticEventSchemas, routing.externalEventSchemas);
8147
+ const originAware = EventDispatcherOriginSchemas.define(dispatcher, routing.domesticEventSchemas, routing.externalEventSchemas);
8148
+ return originAware;
5965
8149
  },
5966
8150
  /**
5967
8151
  * Creates a system-Event dispatcher for registered Entity-state subscriptions.
@@ -6010,7 +8194,9 @@ const RepositoryDispatch = {
6010
8194
  * @returns The Inbox target and supported operation labels, when applicable.
6011
8195
  */
6012
8196
  createEntityInboxTarget(repository, routing) {
6013
- if ((repository.entityFamily !== "aggregate" && repository.entityFamily !== "process-manager") ||
8197
+ if ((repository.entityFamily !== "aggregate" &&
8198
+ repository.entityFamily !== "process-manager" &&
8199
+ repository.entityFamily !== "agent") ||
6014
8200
  (routing.commandSchemas.length === 0 && routing.eventSchemas.length === 0)) {
6015
8201
  return undefined;
6016
8202
  }
@@ -6018,7 +8204,9 @@ const RepositoryDispatch = {
6018
8204
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
6019
8205
  labels: Object.freeze([
6020
8206
  ...(routing.commandSchemas.length === 0 ? [] : ["HANDLE_COMMAND"]),
6021
- ...(repository.entityFamily === "process-manager" && routing.eventSchemas.length > 0
8207
+ ...((repository.entityFamily === "process-manager" ||
8208
+ repository.entityFamily === "agent") &&
8209
+ routing.eventSchemas.length > 0
6022
8210
  ? ["REACT_UPON_EVENT"]
6023
8211
  : []),
6024
8212
  ]),
@@ -6082,6 +8270,7 @@ const RepositoryDispatch = {
6082
8270
  await RepositoryDispatch.dispatchAggregateEvent(repository, routing, runtime, event, route);
6083
8271
  return;
6084
8272
  case "process-manager":
8273
+ case "agent":
6085
8274
  await new ProcessManagerEventExecution(repository, routing, runtime, event).run(route);
6086
8275
  return;
6087
8276
  case "projection":
@@ -6146,7 +8335,7 @@ const RepositoryDispatch = {
6146
8335
  await InboxHandoff.handoffEntityCommand(repository, runtime, command);
6147
8336
  return;
6148
8337
  }
6149
- if (repository.entityFamily === "process-manager") {
8338
+ if (repository.entityFamily === "process-manager" || repository.entityFamily === "agent") {
6150
8339
  await InboxHandoff.handoffEntityCommand(repository, runtime, command);
6151
8340
  return;
6152
8341
  }