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

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 (168) hide show
  1. package/README.md +104 -7
  2. package/REFERENCE.md +199 -19
  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 +137 -3
  72. package/dist/bus/event-bus.d.ts.map +1 -1
  73. package/dist/bus/event-bus.js +133 -2
  74. package/dist/bus/event-bus.js.map +1 -1
  75. package/dist/bus/saved-dispatcher-binding.d.ts +96 -0
  76. package/dist/bus/saved-dispatcher-binding.d.ts.map +1 -0
  77. package/dist/bus/saved-dispatcher-binding.js +35 -0
  78. package/dist/bus/saved-dispatcher-binding.js.map +1 -0
  79. package/dist/context/bounded-context.d.ts +36 -2
  80. package/dist/context/bounded-context.d.ts.map +1 -1
  81. package/dist/context/bounded-context.js +253 -54
  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 +5 -5
  85. package/dist/context/entity-inbox.js.map +1 -1
  86. package/dist/context/projection-handoff.d.ts.map +1 -1
  87. package/dist/context/projection-handoff.js +2 -1
  88. package/dist/context/projection-handoff.js.map +1 -1
  89. package/dist/context/tenant-index.d.ts +25 -0
  90. package/dist/context/tenant-index.d.ts.map +1 -1
  91. package/dist/context/tenant-index.js +45 -1
  92. package/dist/context/tenant-index.js.map +1 -1
  93. package/dist/delivery/delivery.d.ts.map +1 -1
  94. package/dist/delivery/delivery.js +4 -3
  95. package/dist/delivery/delivery.js.map +1 -1
  96. package/dist/delivery/inbox-records.d.ts +13 -0
  97. package/dist/delivery/inbox-records.d.ts.map +1 -1
  98. package/dist/delivery/inbox-records.js +204 -30
  99. package/dist/delivery/inbox-records.js.map +1 -1
  100. package/dist/delivery/inbox-storage.d.ts.map +1 -1
  101. package/dist/delivery/inbox-storage.js +26 -5
  102. package/dist/delivery/inbox-storage.js.map +1 -1
  103. package/dist/delivery/inbox.d.ts +7 -7
  104. package/dist/delivery/inbox.d.ts.map +1 -1
  105. package/dist/delivery/inbox.js +1 -2
  106. package/dist/delivery/inbox.js.map +1 -1
  107. package/dist/delivery/sharded-work-registry.d.ts +1 -0
  108. package/dist/delivery/sharded-work-registry.d.ts.map +1 -1
  109. package/dist/delivery/sharded-work-registry.js +3 -1
  110. package/dist/delivery/sharded-work-registry.js.map +1 -1
  111. package/dist/entity/entity-transaction.d.ts.map +1 -1
  112. package/dist/entity/entity-transaction.js +2 -6
  113. package/dist/entity/entity-transaction.js.map +1 -1
  114. package/dist/entity/entity.d.ts +82 -3
  115. package/dist/entity/entity.d.ts.map +1 -1
  116. package/dist/entity/entity.js +121 -22
  117. package/dist/entity/entity.js.map +1 -1
  118. package/dist/handler/generated-handler-registry.js +8 -3
  119. package/dist/handler/generated-handler-registry.js.map +1 -1
  120. package/dist/handler/handler-metadata.d.ts +17 -1
  121. package/dist/handler/handler-metadata.d.ts.map +1 -1
  122. package/dist/handler/handler-metadata.js +10 -4
  123. package/dist/handler/handler-metadata.js.map +1 -1
  124. package/dist/index.d.ts +3 -3
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +2 -2
  127. package/dist/index.js.map +1 -1
  128. package/dist/integration/third-party-context.d.ts +5 -0
  129. package/dist/integration/third-party-context.d.ts.map +1 -1
  130. package/dist/integration/third-party-context.js +7 -6
  131. package/dist/integration/third-party-context.js.map +1 -1
  132. package/dist/repository/commit-fence.d.ts +11 -0
  133. package/dist/repository/commit-fence.d.ts.map +1 -1
  134. package/dist/repository/commit-fence.js +13 -0
  135. package/dist/repository/commit-fence.js.map +1 -1
  136. package/dist/repository/repository.d.ts +222 -13
  137. package/dist/repository/repository.d.ts.map +1 -1
  138. package/dist/repository/repository.js +2329 -137
  139. package/dist/repository/repository.js.map +1 -1
  140. package/dist/runtime/signal-metadata.d.ts +7 -47
  141. package/dist/runtime/signal-metadata.d.ts.map +1 -1
  142. package/dist/runtime/signal-metadata.js +8 -62
  143. package/dist/runtime/signal-metadata.js.map +1 -1
  144. package/dist/runtime/signal-publisher.d.ts +6 -0
  145. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  146. package/dist/runtime/signal-publisher.js +13 -0
  147. package/dist/runtime/signal-publisher.js.map +1 -1
  148. package/dist/runtime/standalone-handler-runtime.d.ts +7 -0
  149. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  150. package/dist/runtime/standalone-handler-runtime.js +148 -11
  151. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  152. package/dist/server/managed-server-application.d.ts +36 -6
  153. package/dist/server/managed-server-application.d.ts.map +1 -1
  154. package/dist/server/managed-server-application.js +257 -4
  155. package/dist/server/managed-server-application.js.map +1 -1
  156. package/dist/server/server.d.ts +9 -0
  157. package/dist/server/server.d.ts.map +1 -1
  158. package/dist/server/server.js +13 -1
  159. package/dist/server/server.js.map +1 -1
  160. package/dist/stand/subscription-registry.d.ts.map +1 -1
  161. package/dist/stand/subscription-registry.js +9 -8
  162. package/dist/stand/subscription-registry.js.map +1 -1
  163. package/dist/testing/index.d.ts +57 -1
  164. package/dist/testing/index.d.ts.map +1 -1
  165. package/dist/testing/index.js +87 -0
  166. package/dist/testing/index.js.map +1 -1
  167. package/dist/tsconfig.tsbuildinfo +1 -1
  168. package/package.json +14 -12
@@ -11,9 +11,10 @@
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, TimestampSchema, } from "@bufbuild/protobuf/wkt";
16
- import { ValidationException, RejectionThrowable, Validate, TypeUrls, AnyMessages, Identifiers, StringifierRegistry, } from "@spine-event-engine/core";
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";
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";
@@ -21,6 +22,11 @@ import { EntityRecordSchema, } from "@spine-event-engine/proto/generated/spine/s
21
22
  import { 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, AgentOutgoingSignalSchema, 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,23 @@ 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";
34
41
  import { commitFenced } from "./commit-fence.js";
42
+ import { AgentHistoryReads } from "../agent/agent-history.js";
43
+ import { AgentReadRuntime } from "../agent/agent-read-runtime.js";
44
+ import { AgentAdmission } from "../agent/agent-admission.js";
45
+ import { AgentRevisions } from "../agent/agent-revisions.js";
46
+ import { AgentExecutionSession } from "../agent/agent-execution-session.js";
47
+ import { AgentExecutionFault } from "../agent/agent-execution-fault.js";
48
+ import { AgentModelSelection } from "../agent/agent-model-selection.js";
49
+ import { AgentAiRuntime, AgentUncertainAttemptError } from "../agent/agent-ai-runtime.js";
50
+ import { AgentInteractionEvents } from "../agent/agent-interaction-events.js";
51
+ import { AgentInteractionAudit } from "../agent/agent-interaction-audit.js";
52
+ import { AgentAiBindings } from "../agent/agent-ai-binding.js";
35
53
  import { InboxTargets } from "../delivery/inbox.js";
36
54
  import { ShardIndex } from "../delivery/shard-index.js";
37
- import { Aggregate, ProcessManager, Projection, entityHistoryAccess, processManagerQueryAccess, transactionalEntityAccess, } from "../entity/entity.js";
55
+ import { Aggregate, Agent, ProcessManager, Projection, entityHistoryAccess, processManagerQueryAccess, transactionalEntityAccess, } from "../entity/entity.js";
38
56
  import { describeEntityMetadata, attachEntitySchema, } from "../entity/entity-metadata.js";
39
57
  import { EntityIds, EntityRecords, entityStorageDescriptor, } from "../entity/entity-storage-descriptor.js";
40
58
  import { repositorySpecScanner } from "../entity/spec-scanner.js";
@@ -185,6 +203,31 @@ export class Repository {
185
203
  get snapshot() {
186
204
  return RepositoryIdentity.cloneRepositorySnapshot(RepositoryIdentity.createRepositorySnapshot(this.#entityType, this.#entityFamily, this.#metadata));
187
205
  }
206
+ /**
207
+ * Opens retained history for one Agent without restoring an Entity.
208
+ * The trusted application must authorize the caller before using this read.
209
+ *
210
+ * @param this Registered Agent repository.
211
+ * @param id Typed Agent identifier.
212
+ * @param scope Explicit tenant boundary; pass `{}` in a single-tenant Bounded Context.
213
+ * @returns Reader for indexed history categories.
214
+ */
215
+ agentHistory(id, scope) {
216
+ return RepositoryAgentReads.history(this, id, scope);
217
+ }
218
+ /**
219
+ * Reads the exact recorded phase for one accepted Command or Event.
220
+ * The trusted application must authorize the caller before using this read.
221
+ *
222
+ * @param this Registered Agent repository.
223
+ * @param id Typed Agent identifier.
224
+ * @param source Original Command or Event ID.
225
+ * @param scope Explicit tenant boundary; pass `{}` in a single-tenant Bounded Context.
226
+ * @returns Recorded phase, or `undefined` when no invocation was recorded.
227
+ */
228
+ agentExecution(id, source, scope) {
229
+ return RepositoryAgentReads.execution(this, id, source, scope);
230
+ }
188
231
  /**
189
232
  * Routes a command to one entity ID without invoking a handler.
190
233
  *
@@ -213,6 +256,199 @@ export class Repository {
213
256
  });
214
257
  }
215
258
  }
259
+ const RepositoryAgentReads = {
260
+ /**
261
+ * Captures a validated Agent and tenant boundary without opening Entity storage.
262
+ *
263
+ * @param repository Registered Agent repository.
264
+ * @param id Typed Agent identifier.
265
+ * @param scope Explicit tenant selection.
266
+ * @returns Detached read identity and active runtime.
267
+ */
268
+ capture(repository, id, scope) {
269
+ if (repository.entityFamily !== "agent")
270
+ throw new TypeError("Agent read requires an Agent repository.");
271
+ const scopeValue = scope;
272
+ if (scopeValue === null || typeof scopeValue !== "object")
273
+ throw new TypeError("Agent read requires an explicit scope.");
274
+ const runtime = repositoryRuntimes.get(repository);
275
+ if (runtime === undefined)
276
+ throw new Error("Agent read requires an active repository.");
277
+ const validated = RepositoryRoutes.readRouteId(id, repository.idField, "command");
278
+ const field = repository.idField.descriptor;
279
+ const copiedId = field.fieldKind === "message"
280
+ ? clone(field.message, validated)
281
+ : validated;
282
+ const tenant = scope.tenantId === undefined ? undefined : RepositoryTenants.require(scope.tenantId);
283
+ const context = RepositoryTenants.storageContextForTenant(runtime.context, tenant);
284
+ return { runtime, context, id: copiedId };
285
+ },
286
+ /**
287
+ * Returns a detached public history request before provider access.
288
+ *
289
+ * @param request Page request and opaque cursor.
290
+ * @returns Independent request values.
291
+ */
292
+ request(request) {
293
+ return {
294
+ pageSize: request.pageSize,
295
+ ...(request.cursor === undefined ? {} : { cursor: request.cursor }),
296
+ };
297
+ },
298
+ /**
299
+ * Creates the four scoped history views for one captured Agent identity.
300
+ *
301
+ * @param repository Registered Agent repository.
302
+ * @param id Typed Agent identifier.
303
+ * @param scope Explicit tenant selection.
304
+ * @returns Reader using indexed provider history.
305
+ */
306
+ history(repository, id, scope) {
307
+ const captured = this.capture(repository, id, scope);
308
+ const read = (view, request) => this.historyPage(repository, captured, view, this.request(request));
309
+ return Object.freeze({
310
+ fullHistory: (request) => read({ kind: "full" }, request),
311
+ conversationHistory: async (request) => {
312
+ const conversationValue = request.conversation;
313
+ if (conversationValue === null ||
314
+ typeof conversationValue !== "object" ||
315
+ !("$typeName" in conversationValue) ||
316
+ conversationValue.$typeName !== ConversationIdSchema.typeName ||
317
+ !("value" in conversationValue) ||
318
+ typeof conversationValue.value !== "string" ||
319
+ conversationValue.value.length === 0)
320
+ throw new TypeError("Agent conversation history requires a ConversationId.");
321
+ const conversation = clone(ConversationIdSchema, request.conversation);
322
+ const page = await read({ kind: "conversation", conversation }, request);
323
+ return this.project(page, "conversationRecord");
324
+ },
325
+ systemEventHistory: async (request) => this.project(await read({ kind: "system" }, request), "systemEvent"),
326
+ domainEventHistory: async (request) => this.project(await read({ kind: "domain" }, request), "domainEvent"),
327
+ });
328
+ },
329
+ /**
330
+ * Reads one category from the existing indexed history provider.
331
+ *
332
+ * @param repository Registered Agent repository.
333
+ * @param captured Detached Agent and tenant identity.
334
+ * @param view Indexed category and optional conversation.
335
+ * @param request Detached page request.
336
+ * @returns Detached history entries and opaque continuation.
337
+ */
338
+ async historyPage(repository, captured, view, request) {
339
+ if (repositoryRuntimes.get(repository) !== captured.runtime)
340
+ throw new Error("Agent read requires an active repository.");
341
+ const input = RepositoryStorage.entityStorageInput(repository, captured.context);
342
+ const history = AgentHistoryStorageFactories.create(captured.runtime.storageFactory, {
343
+ context: captured.context,
344
+ stateType: repository.stateSchema.typeName,
345
+ id: { key: input.id.key },
346
+ });
347
+ try {
348
+ const page = await AgentHistoryReads.readBound({
349
+ storage: history,
350
+ entityId: captured.id,
351
+ scope: repositoryAccess.agentHistoryScope(captured.context, input, captured.id),
352
+ }, view, request);
353
+ return {
354
+ items: page.items,
355
+ ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
356
+ };
357
+ }
358
+ finally {
359
+ history.close();
360
+ }
361
+ },
362
+ /**
363
+ * Returns one typed category from the full Proto history oneof.
364
+ *
365
+ * @typeParam K Selected history oneof category.
366
+ * @param page Complete indexed history page.
367
+ * @param kind Expected oneof category.
368
+ * @returns Category values and the original continuation.
369
+ */
370
+ project(page, kind) {
371
+ const items = page.items.map((entry) => {
372
+ if (entry.item.case !== kind)
373
+ throw new Error("Agent history provider returned the wrong category.");
374
+ return entry.item.value;
375
+ });
376
+ return {
377
+ items,
378
+ ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
379
+ };
380
+ },
381
+ /**
382
+ * Reads the exact saved invocation and exposes only its phase.
383
+ *
384
+ * @param repository Registered Agent repository.
385
+ * @param id Typed Agent identifier.
386
+ * @param source Original Command or Event ID.
387
+ * @param scope Explicit tenant selection.
388
+ * @returns Current phase, or `undefined` for no saved invocation.
389
+ */
390
+ async execution(repository, id, source, scope) {
391
+ const captured = this.capture(repository, id, scope);
392
+ const sourceSignal = this.source(source);
393
+ const input = RepositoryStorage.entityStorageInput(repository, captured.context);
394
+ const key = create(AgentInvocationKeySchema, {
395
+ scope: { stateType: repository.stateSchema.typeName, agentKey: input.id.key(captured.id) },
396
+ sourceSignal,
397
+ });
398
+ if (repositoryRuntimes.get(repository) !== captured.runtime)
399
+ throw new Error("Agent read requires an active repository.");
400
+ const storage = AgentExecutionStorageFactories.create(captured.runtime.storageFactory, {
401
+ entity: input,
402
+ stateType: repository.stateSchema.typeName,
403
+ });
404
+ try {
405
+ const record = await storage.read(key);
406
+ return record === undefined ? undefined : this.status(record.status);
407
+ }
408
+ finally {
409
+ storage.close();
410
+ }
411
+ },
412
+ /**
413
+ * Copies an original signal identifier into the private execution key.
414
+ *
415
+ * @param source Original Command or Event ID.
416
+ * @returns Typed private source key.
417
+ */
418
+ source(source) {
419
+ if (source.$typeName === CommandIdSchema.typeName && source.uuid.length > 0)
420
+ return create(AgentSignalKeySchema, {
421
+ id: { case: "command", value: clone(CommandIdSchema, source) },
422
+ });
423
+ if (source.$typeName === EventIdSchema.typeName && source.value.length > 0)
424
+ return create(AgentSignalKeySchema, {
425
+ id: { case: "event", value: clone(EventIdSchema, source) },
426
+ });
427
+ throw new TypeError("Agent execution requires an original CommandId or EventId.");
428
+ },
429
+ /**
430
+ * Returns the public status for a private execution phase.
431
+ *
432
+ * @param status Saved private phase.
433
+ * @returns Public phase string.
434
+ */
435
+ status(status) {
436
+ switch (status) {
437
+ case AgentInvocationStatus.AGENT_INVOCATION_ACCEPTED:
438
+ return "accepted";
439
+ case AgentInvocationStatus.AGENT_INVOCATION_ACTIVE:
440
+ return "active";
441
+ case AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_DELIVERY:
442
+ return "completed-pending-delivery";
443
+ case AgentInvocationStatus.AGENT_INVOCATION_COMPLETED:
444
+ return "completed";
445
+ case AgentInvocationStatus.AGENT_INVOCATION_TERMINATED:
446
+ return "terminated";
447
+ default:
448
+ throw new Error("Agent execution provider returned an unsupported phase.");
449
+ }
450
+ },
451
+ };
216
452
  const repositorySnapshots = new WeakMap();
217
453
  const repositoryCreators = new WeakMap();
218
454
  const repositoryRoutings = new WeakMap();
@@ -225,6 +461,8 @@ const repositoryRuntimes = new WeakMap();
225
461
  const repositoryEntityHandles = new WeakMap();
226
462
  const entityStateHistoryCaches = new WeakMap();
227
463
  const repositoryHistoryConfigurations = new WeakMap();
464
+ const repositoryAiConfigurations = new WeakMap();
465
+ const repositoryAgentCodeRevisions = new WeakMap();
228
466
  const repositoryDispatchGuards = new WeakMap();
229
467
  /**
230
468
  * Installs repository routing, history, and callback state after identity validation.
@@ -248,10 +486,27 @@ const RepositoryRegistration = {
248
486
  repositoryRoutings.set(repository, routing);
249
487
  repositoryProducedEventSchemas.set(repository, Object.freeze([...routing.producedEventSchemas]));
250
488
  repositoryHistoryConfigurations.set(repository, RepositoryStorage.readHistoryConfiguration(options, entityFamily));
489
+ this.installAgentOptions(repository, options, entityFamily);
251
490
  repositorySnapshots.set(repository, RepositoryIdentity.createRepositorySnapshot(entityType, entityFamily, metadata));
252
491
  repositoryDispatchers.set(repository, RepositoryDispatch.createRepositoryDispatchers(repository, routing));
253
492
  RepositoryRegistration.installTargets(repository, routing);
254
493
  },
494
+ /**
495
+ * Captures Agent capability and code revisions during repository registration.
496
+ *
497
+ * @typeParam EntityType Concrete generated Entity constructor.
498
+ * @param repository Registered Entity repository for this operation.
499
+ * @param options Repository or execution options for this operation.
500
+ * @param family Registered Entity family.
501
+ */
502
+ installAgentOptions(repository, options, family) {
503
+ if (family !== "agent")
504
+ return;
505
+ if (options.ai !== undefined)
506
+ repositoryAiConfigurations.set(repository, options.ai);
507
+ if (options.agentCodeRevision !== undefined)
508
+ repositoryAgentCodeRevisions.set(repository, options.agentCodeRevision);
509
+ },
255
510
  /**
256
511
  * Registers durable Entity and direct Projection delivery targets.
257
512
  *
@@ -312,6 +567,98 @@ export const repositoryAccess = Object.freeze({
312
567
  }
313
568
  return schemas;
314
569
  },
570
+ producedCommandSchemas(repository) {
571
+ const routing = repositoryRoutings.get(repository);
572
+ if (routing === undefined)
573
+ throw new TypeError("Produced Command schemas require a Repository instance.");
574
+ return routing.producedCommandSchemas;
575
+ },
576
+ agentConfiguration(repository) {
577
+ const ai = repositoryAiConfigurations.get(repository);
578
+ const codeRevision = repositoryAgentCodeRevisions.get(repository);
579
+ return {
580
+ ...(ai === undefined ? {} : { ai }),
581
+ ...(codeRevision === undefined ? {} : { codeRevision }),
582
+ };
583
+ },
584
+ runAcceptedAgent(repository, tenantId, key, signal) {
585
+ return AgentExecutionRunner.run(repository, tenantId, key, signal);
586
+ },
587
+ pendingAcceptedAgents(repository, tenantId, after, count) {
588
+ const runtime = repositoryRuntimes.get(repository);
589
+ if (runtime === undefined || repository.entityFamily !== "agent")
590
+ throw new Error("Agent pending discovery requires a registered Agent repository.");
591
+ const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
592
+ const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
593
+ if (storage.agentExecution === undefined)
594
+ throw new Error("Agent pending discovery requires execution storage.");
595
+ return storage.agentExecution.pending({ count, ...(after === undefined ? {} : { after }) });
596
+ },
597
+ /**
598
+ * Opens a scoped provider read without restoring an application Entity.
599
+ *
600
+ * @param repository Registered Agent repository.
601
+ * @param entityId Typed Agent identifier.
602
+ * @param view Indexed category.
603
+ * @param read Requested count, byte budget, and complete continuation key.
604
+ * @param tenantId Complete tenant identity when required.
605
+ * @returns Retained entries and continuation status.
606
+ */
607
+ async agentHistory(repository, entityId, view, read, tenantId) {
608
+ if (repository.entityFamily !== "agent")
609
+ throw new TypeError("Agent history requires an Agent repository.");
610
+ const runtime = repositoryRuntimes.get(repository);
611
+ if (runtime === undefined)
612
+ throw new Error("Agent history requires an active repository.");
613
+ const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
614
+ const input = RepositoryStorage.entityStorageInput(repository, context);
615
+ const history = AgentHistoryStorageFactories.create(runtime.storageFactory, {
616
+ context,
617
+ stateType: repository.stateSchema.typeName,
618
+ id: { key: input.id.key },
619
+ });
620
+ try {
621
+ return await history.read({ entityId, view, ...read });
622
+ }
623
+ finally {
624
+ history.close();
625
+ }
626
+ },
627
+ async agentHistoryPage(repository, entityId, request, tenantId) {
628
+ if (repository.entityFamily !== "agent")
629
+ throw new TypeError("Agent history requires an Agent repository.");
630
+ const runtime = repositoryRuntimes.get(repository);
631
+ if (runtime === undefined)
632
+ throw new Error("Agent history requires an active repository.");
633
+ const context = RepositoryTenants.storageContextForTenant(runtime.context, tenantId);
634
+ const input = RepositoryStorage.entityStorageInput(repository, context);
635
+ const history = AgentHistoryStorageFactories.create(runtime.storageFactory, {
636
+ context,
637
+ stateType: repository.stateSchema.typeName,
638
+ id: { key: input.id.key },
639
+ });
640
+ try {
641
+ return await AgentHistoryReads.readBound({
642
+ storage: history,
643
+ entityId,
644
+ scope: this.agentHistoryScope(context, input, entityId),
645
+ }, { kind: "full" }, request);
646
+ }
647
+ finally {
648
+ history.close();
649
+ }
650
+ },
651
+ /**
652
+ * Computes the opaque cursor scope for a registered Agent history read.
653
+ */
654
+ agentHistoryScope(context, input, entityId) {
655
+ return {
656
+ context: context.name,
657
+ tenant: JSON.stringify(context.tenantId ?? null),
658
+ repository: input.sourceType.typeName,
659
+ entity: input.id.key(entityId),
660
+ };
661
+ },
315
662
  /**
316
663
  * Looks up command dispatch for the repository.
317
664
  *
@@ -1558,13 +1905,10 @@ class ProjectionEventExecution {
1558
1905
  async #loadProjection(entityId, options, mode) {
1559
1906
  const stored = await standAccess.readCurrent(this.#runtime.stand, this.#repository.stateSchema, entityId, options);
1560
1907
  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);
1908
+ const { commits, events, agentHistory, storageInput } = RepositoryEntities.bindStorage(this.#repository, this.#runtime, options.tenantId, entityId, entity);
1909
+ return RepositoryEntities.loaded(entity, stored, commits, events, agentHistory, storageInput, this.#repository.stateSchema, entityId);
1563
1910
  }
1564
1911
  }
1565
- /**
1566
- * Binds Process Manager query access to Stand for one handler invocation.
1567
- */
1568
1912
  const ProcessManagerQueries = Object.freeze({
1569
1913
  /**
1570
1914
  * Binds tenant-aware query reads and returns a release callback.
@@ -1573,15 +1917,16 @@ const ProcessManagerQueries = Object.freeze({
1573
1917
  * @param runtime Context Stand and query services.
1574
1918
  * @param actorContext Actor metadata from the source signal.
1575
1919
  * @param tenantId Tenant for query reads, when present.
1920
+ * @param intercept Durable Agent snapshot interception, when active.
1576
1921
  * @returns Callback that removes the query binding.
1577
1922
  */
1578
- bind(entity, runtime, actorContext, tenantId) {
1923
+ bind(entity, runtime, actorContext, tenantId, intercept) {
1579
1924
  const context = actorContext === undefined
1580
1925
  ? create(ActorContextSchema)
1581
1926
  : clone(ActorContextSchema, actorContext);
1582
1927
  const effectiveTenant = EffectiveTenants.current(runtime.context.multitenant, tenantId);
1583
1928
  context.tenantId = effectiveTenant;
1584
- return processManagerQueryAccess.bind(entity, (plan, schema, query) => ProcessManagerQueries.read(runtime, effectiveTenant, plan, schema, query), context);
1929
+ return processManagerQueryAccess.bind(entity, (plan, schema, query) => ProcessManagerQueries.read(runtime, effectiveTenant, plan, schema, query, intercept), context);
1585
1930
  },
1586
1931
  /**
1587
1932
  * Routes a handler read to the registered state target before reaching storage.
@@ -1592,9 +1937,10 @@ const ProcessManagerQueries = Object.freeze({
1592
1937
  * @param plan Compiled read criteria.
1593
1938
  * @param schema State schema supplied by the handler.
1594
1939
  * @param query Wire query carrying the actor and requested fields.
1940
+ * @param intercept Durable Agent snapshot interception, when active.
1595
1941
  * @returns Detached state snapshots decoded with the caller's schema.
1596
1942
  */
1597
- async read(runtime, tenantId, plan, schema, query) {
1943
+ async read(runtime, tenantId, plan, schema, query, intercept) {
1598
1944
  const routes = RegisteredTargets.forStand(runtime.stand);
1599
1945
  const typeUrl = TypeUrls.derive(schema);
1600
1946
  const target = routes?.find(typeUrl);
@@ -1609,7 +1955,23 @@ const ProcessManagerQueries = Object.freeze({
1609
1955
  if (target !== undefined)
1610
1956
  routes?.validate(target, plan);
1611
1957
  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);
1958
+ const live = () => this.load(target?.context.stand() ?? runtime.stand, registeredSchema, plan, destination, query, schema);
1959
+ return intercept === undefined ? live() : intercept(schema, query, live);
1960
+ },
1961
+ /**
1962
+ * Reads the authorized target after optional Agent snapshot reuse is checked.
1963
+ *
1964
+ * @typeParam Schema Generated Projection state descriptor.
1965
+ * @param stand Stand selected by the authorized query route.
1966
+ * @param registeredSchema Descriptor registered for the target.
1967
+ * @param plan Compiled query criteria.
1968
+ * @param destination Effective tenant selected for the target.
1969
+ * @param query Original typed Query envelope.
1970
+ * @param schema Descriptor expected by the caller.
1971
+ * @returns Detached Projection state snapshots.
1972
+ */
1973
+ async load(stand, registeredSchema, plan, destination, query, schema) {
1974
+ const results = await QueryReader.read(stand, registeredSchema, plan, destination, 10_000, query);
1613
1975
  return Object.freeze(results.map((result) => clone(schema, result.state)));
1614
1976
  },
1615
1977
  });
@@ -1654,8 +2016,8 @@ class ProcessManagerExecutionSupport {
1654
2016
  async load(entityId, options) {
1655
2017
  const stored = await standAccess.readCurrent(this.#runtime.stand, this.#repository.stateSchema, entityId, options);
1656
2018
  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);
2019
+ const { commits, events, agentHistory, storageInput } = RepositoryEntities.bindStorage(this.#repository, this.#runtime, options.tenantId, entityId, entity);
2020
+ return RepositoryEntities.loaded(entity, stored, commits, events, agentHistory, storageInput, this.#repository.stateSchema, entityId);
1659
2021
  }
1660
2022
  /**
1661
2023
  * Stores changed Process Manager state or produced Events under one Version.
@@ -1663,13 +2025,15 @@ class ProcessManagerExecutionSupport {
1663
2025
  * @param loaded Process Manager and commit storage.
1664
2026
  * @param options Stand tenant selection.
1665
2027
  * @param events Produced or diagnostic Events to retain when configured.
2028
+ * @param emitted Domain Events emitted by an Agent, excluding the source Event.
1666
2029
  * @returns `true` after a successful commit or unchanged no-op.
1667
2030
  */
1668
- async commit(loaded, options, events) {
2031
+ async commit(loaded, options, events, emitted = events) {
1669
2032
  const version = RepositoryEntities.repositoryVersion(loaded.entity);
1670
2033
  const priorNumber = loaded.current?.version?.number ?? 0;
1671
2034
  if (version.number === priorNumber) {
1672
2035
  await this.#appendUnchangedDiagnostics(loaded, events);
2036
+ await this.#appendAgentEvents(loaded, emitted);
1673
2037
  return true;
1674
2038
  }
1675
2039
  const entityId = loaded.entity.id;
@@ -1677,9 +2041,33 @@ class ProcessManagerExecutionSupport {
1677
2041
  const lifecycle = RepositoryEntities.repositoryLifecycle(loaded.entity);
1678
2042
  const deferred = await standAccess.deferUpdate(this.#runtime.stand, this.#repository.stateSchema, state, RepositoryStand.standUpdateOptions(options.tenantId, version, lifecycle), this.#repository.metadata);
1679
2043
  await this.#storeProcessManagerRecord(loaded, entityId, events, deferred);
2044
+ await this.#appendAgentEvents(loaded, emitted);
1680
2045
  this.#notifyProcessManager(deferred, events);
1681
2046
  return true;
1682
2047
  }
2048
+ /**
2049
+ * Stores original envelopes emitted by an Agent under its repository.
2050
+ *
2051
+ * @param loaded Restored Agent with history storage.
2052
+ * @param events Emitted domain Event envelopes.
2053
+ * @returns Completion after every append.
2054
+ */
2055
+ async #appendAgentEvents(loaded, events) {
2056
+ if (this.#repository.entityFamily !== "agent")
2057
+ return;
2058
+ const history = loaded.agentHistory;
2059
+ if (history === undefined)
2060
+ throw new Error("Agent history storage is required.");
2061
+ const id = loaded.entity.id;
2062
+ for (const event of events) {
2063
+ if (event.context?.timestamp === undefined)
2064
+ throw new Error("Agent emitted Event requires its original timestamp.");
2065
+ await history.append(id, create(AgentHistoryEntrySchema, {
2066
+ occurredAt: event.context.timestamp,
2067
+ item: { case: "domainEvent", value: event },
2068
+ }));
2069
+ }
2070
+ }
1683
2071
  /**
1684
2072
  * Writes configured Process Manager diagnostics without storing unchanged state.
1685
2073
  *
@@ -1751,7 +2139,1237 @@ class ProcessManagerExecutionSupport {
1751
2139
  }
1752
2140
  }
1753
2141
  /**
1754
- * Executes a routed Process Manager Command and publishes its results.
2142
+ * Advances admitted Agent work only under a provider claim and exact record fence.
2143
+ */
2144
+ const AgentExecutionRunner = {
2145
+ /**
2146
+ * Processes one accepted Agent transition under its provider claim.
2147
+ *
2148
+ * @param repository Registered Agent repository.
2149
+ * @param tenantId Tenant selected for this invocation.
2150
+ * @param key Exact accepted invocation key.
2151
+ * @param signal Cancellation signal for the current accepted execution.
2152
+ * @returns When the fenced repository operation completes.
2153
+ */
2154
+ async run(repository, tenantId, key, signal) {
2155
+ if (signal?.aborted)
2156
+ return;
2157
+ const runtime = repositoryRuntimes.get(repository);
2158
+ if (runtime?.ai === undefined || repository.entityFamily !== "agent")
2159
+ throw new Error("Agent execution requires a bound Agent repository and AI registry.");
2160
+ const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
2161
+ const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
2162
+ const executions = storage.agentExecution;
2163
+ if (executions === undefined)
2164
+ throw new Error("Agent execution storage is required.");
2165
+ const token = randomUUID();
2166
+ const claimed = await executions.claim(key, token, addMillis(Time.currentTime(), 30_000));
2167
+ if (claimed === undefined)
2168
+ return;
2169
+ const session = new AgentExecutionSession(executions, claimed.record, token, claimed.preferences);
2170
+ await this.runClaimed(repository, runtime, tenantId, session, signal);
2171
+ },
2172
+ /**
2173
+ * Updates and releases one exact claim while its saved work advances.
2174
+ *
2175
+ * @param repository Registered Entity repository for this operation.
2176
+ * @param runtime Bounded Context storage and publication services.
2177
+ * @param tenantId Tenant selected for this accepted signal.
2178
+ * @param session Fenced Agent execution session.
2179
+ * @param signal Cancellation signal for the current accepted execution.
2180
+ * @returns When the fenced repository operation completes.
2181
+ */
2182
+ async runClaimed(repository, runtime, tenantId, session, signal) {
2183
+ const abort = () => {
2184
+ session.stop();
2185
+ };
2186
+ signal?.addEventListener("abort", abort, { once: true });
2187
+ if (signal?.aborted)
2188
+ abort();
2189
+ const renewal = setInterval(() => {
2190
+ // spine-log-boundary: server.agent_lease_renewal
2191
+ void session.renew(addMillis(Time.currentTime(), 30_000)).catch(() => undefined);
2192
+ }, 10_000);
2193
+ try {
2194
+ if (session.signal.aborted)
2195
+ return;
2196
+ await this.advanceClaimed(repository, runtime, tenantId, session);
2197
+ }
2198
+ catch (error) {
2199
+ if (signal?.aborted)
2200
+ return;
2201
+ await this.handleRunFailure(repository, runtime, tenantId, session, error);
2202
+ }
2203
+ finally {
2204
+ clearInterval(renewal);
2205
+ signal?.removeEventListener("abort", abort);
2206
+ session.stop();
2207
+ }
2208
+ },
2209
+ /**
2210
+ * Handles transient failures while giving expired work a final audit.
2211
+ *
2212
+ * @param repository Registered Entity repository for this operation.
2213
+ * @param runtime Bounded Context storage and publication services.
2214
+ * @param tenantId Tenant selected for this accepted signal.
2215
+ * @param session Fenced Agent execution session.
2216
+ * @param error Failure raised while processing this accepted signal.
2217
+ * @returns When the fenced repository operation completes.
2218
+ */
2219
+ async handleRunFailure(repository, runtime, tenantId, session, error) {
2220
+ if (session.record().status === AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_DELIVERY)
2221
+ throw error;
2222
+ if (error instanceof AgentUncertainAttemptError) {
2223
+ const reason = this.hasUnresolvedToolWrite(session.record())
2224
+ ? "TOOL_OUTCOME_UNKNOWN"
2225
+ : "MODEL_OUTCOME_UNKNOWN";
2226
+ return this.terminate(repository, runtime, tenantId, session, reason);
2227
+ }
2228
+ if (error instanceof AgentExecutionFault) {
2229
+ if (session.signal.aborted)
2230
+ throw error;
2231
+ return this.terminate(repository, runtime, tenantId, session, error.reason);
2232
+ }
2233
+ if (this.deadlineExpired(session.record()))
2234
+ return this.terminate(repository, runtime, tenantId, session, "DEADLINE_EXCEEDED");
2235
+ throw error;
2236
+ },
2237
+ /**
2238
+ * Processes one claimed record according to its saved completion or request state.
2239
+ *
2240
+ * @param repository Registered Entity repository for this operation.
2241
+ * @param runtime Bounded Context storage and publication services.
2242
+ * @param tenantId Tenant selected for this accepted signal.
2243
+ * @param session Fenced Agent execution session.
2244
+ * @returns When the fenced repository operation completes.
2245
+ */
2246
+ async advanceClaimed(repository, runtime, tenantId, session) {
2247
+ if (this.hasUnresolvedPhysicalRequest(session.record()))
2248
+ return this.terminate(repository, runtime, tenantId, session, this.hasUnresolvedToolWrite(session.record())
2249
+ ? "TOOL_OUTCOME_UNKNOWN"
2250
+ : "MODEL_OUTCOME_UNKNOWN");
2251
+ if (session.record().status === AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_DELIVERY)
2252
+ return this.deliver(runtime, session);
2253
+ if (this.deadlineExpired(session.record()))
2254
+ return this.terminate(repository, runtime, tenantId, session, "DEADLINE_EXCEEDED");
2255
+ return this.execute(repository, runtime, tenantId, session);
2256
+ },
2257
+ /**
2258
+ * Tests the original saved deadline without granting recovery fresh time.
2259
+ *
2260
+ * @param record Durable Agent execution record.
2261
+ * @returns Whether the checked condition holds.
2262
+ */
2263
+ deadlineExpired(record) {
2264
+ const deadline = record.started?.deadline;
2265
+ return (deadline !== undefined &&
2266
+ Time.currentTimeMillis() >=
2267
+ Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000));
2268
+ },
2269
+ /**
2270
+ * Checks for a dispatched model reservation whose response was never saved.
2271
+ *
2272
+ * @param record Durable Agent execution record.
2273
+ * @returns Whether the checked condition holds.
2274
+ */
2275
+ hasUnresolvedPhysicalRequest(record) {
2276
+ const model = record.journal.some((entry) => {
2277
+ if (entry.evidence.case !== "attempt")
2278
+ return false;
2279
+ const attempt = entry.evidence.value;
2280
+ return (attempt.transportReserved &&
2281
+ attempt.response.case === undefined &&
2282
+ !this.hasSavedOperationResult(record, attempt.operation?.value));
2283
+ });
2284
+ return model || this.hasUnresolvedToolWrite(record);
2285
+ },
2286
+ /**
2287
+ * Checks for a dispatched write with no recorded tool result.
2288
+ *
2289
+ * @param record Durable Agent execution record.
2290
+ * @returns Whether the checked condition holds.
2291
+ */
2292
+ hasUnresolvedToolWrite(record) {
2293
+ return record.journal.some((entry) => {
2294
+ if (entry.evidence.case !== "tool")
2295
+ return false;
2296
+ const tool = entry.evidence.value;
2297
+ return (tool.request?.effect === ToolEffect.WRITE &&
2298
+ tool.response === undefined &&
2299
+ (tool.dispatched || tool.outcomeUnknown) &&
2300
+ !this.hasSavedOperationResult(record, tool.operation?.value));
2301
+ });
2302
+ },
2303
+ /**
2304
+ * Checks for a settled named result even when physical evidence is incomplete.
2305
+ *
2306
+ * @param record Durable Agent execution record.
2307
+ * @param id Saved signal, operation, or Entity identifier.
2308
+ * @returns Whether the checked condition holds.
2309
+ */
2310
+ hasSavedOperationResult(record, id) {
2311
+ return record.journal.some((entry) => entry.evidence.case === "operation" &&
2312
+ entry.evidence.value.operation?.value === id &&
2313
+ entry.evidence.value.result.case !== undefined);
2314
+ },
2315
+ /**
2316
+ * Records termination of an unreplayable physical request under the provider fence.
2317
+ *
2318
+ * @param repository Registered Entity repository for this operation.
2319
+ * @param runtime Bounded Context storage and publication services.
2320
+ * @param tenantId Tenant selected for this accepted signal.
2321
+ * @param session Fenced Agent execution session.
2322
+ * @param reason Safe terminal reason retained in Agent audit.
2323
+ * @returns When the fenced repository operation completes.
2324
+ */
2325
+ async terminate(repository, runtime, tenantId, session, reason) {
2326
+ const record = session.record();
2327
+ const event = this.terminationEvent(repository, runtime, tenantId, record, reason);
2328
+ const occurredAt = event.context?.timestamp;
2329
+ if (occurredAt === undefined)
2330
+ throw new Error("Agent termination Event requires Time.");
2331
+ runtime.registerSystemEventSchema(AgentInvocationTerminatedSchema);
2332
+ await session.update((next) => {
2333
+ next.status = AgentInvocationStatus.AGENT_INVOCATION_TERMINATED;
2334
+ next.terminalFailure = create(AiDiagnosticIdSchema, { value: randomUUID() });
2335
+ return next;
2336
+ }, [
2337
+ create(AgentHistoryEntrySchema, {
2338
+ occurredAt,
2339
+ item: { case: "systemEvent", value: event },
2340
+ }),
2341
+ ]);
2342
+ await runtime.publishAgentSystemEvent(event);
2343
+ },
2344
+ /**
2345
+ * Builds one original termination envelope for a saved terminal reason.
2346
+ *
2347
+ * @param repository Registered Entity repository for this operation.
2348
+ * @param runtime Bounded Context storage and publication services.
2349
+ * @param tenantId Tenant selected for this accepted signal.
2350
+ * @param record Durable Agent execution record.
2351
+ * @param reason Safe terminal reason retained in Agent audit.
2352
+ * @returns Original AgentInvocationTerminated System Event envelope.
2353
+ */
2354
+ terminationEvent(repository, runtime, tenantId, record, reason) {
2355
+ const accepted = record.accepted;
2356
+ if (accepted?.recipientId === undefined)
2357
+ throw new Error("Agent termination requires the original typed recipient.");
2358
+ const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2359
+ const unresolvedToolCalls = this.unresolvedToolCalls(record);
2360
+ const payload = create(AgentInvocationTerminatedSchema, {
2361
+ agent: scope.agent,
2362
+ sourceSignal: scope.source,
2363
+ reason,
2364
+ unresolvedAttempts: this.unresolvedAttempts(record),
2365
+ unresolvedToolCalls,
2366
+ terminatedAt: Time.currentTime(),
2367
+ });
2368
+ return AgentInteractionEvents.envelope(accepted, runtime.signalMetadata, accepted.recipientId, AgentInvocationTerminatedSchema, payload);
2369
+ },
2370
+ /**
2371
+ * Returns IDs only for physically reserved model requests without saved outcomes.
2372
+ *
2373
+ * @param record Durable execution journal.
2374
+ * @returns Original attempt IDs for unresolved physical sends.
2375
+ */
2376
+ unresolvedAttempts(record) {
2377
+ return record.journal.flatMap((entry) => {
2378
+ if (entry.evidence.case !== "attempt")
2379
+ return [];
2380
+ const attempt = entry.evidence.value;
2381
+ return attempt.transportReserved &&
2382
+ attempt.response.case === undefined &&
2383
+ !this.hasSavedOperationResult(record, attempt.operation?.value) &&
2384
+ attempt.attempt !== undefined
2385
+ ? [attempt.attempt]
2386
+ : [];
2387
+ });
2388
+ },
2389
+ /**
2390
+ * Returns exact IDs for dispatched write effects without saved results.
2391
+ *
2392
+ * @param record Durable Agent execution record.
2393
+ * @returns Original ToolCall IDs for unresolved dispatched writes.
2394
+ */
2395
+ unresolvedToolCalls(record) {
2396
+ return record.journal.flatMap((entry) => {
2397
+ if (entry.evidence.case !== "tool")
2398
+ return [];
2399
+ const tool = entry.evidence.value;
2400
+ return tool.request?.effect === ToolEffect.WRITE &&
2401
+ tool.response === undefined &&
2402
+ (tool.dispatched || tool.outcomeUnknown) &&
2403
+ tool.request.call !== undefined
2404
+ ? [tool.request.call]
2405
+ : [];
2406
+ });
2407
+ },
2408
+ /**
2409
+ * Persists start facts, runs selected handlers, and conditionally completes a no-op.
2410
+ *
2411
+ * @param repository Agent registration.
2412
+ * @param runtime Bound services.
2413
+ * @param tenantId Complete tenant, when applicable.
2414
+ * @param session Fenced Agent execution session.
2415
+ * @returns When the fenced repository operation completes.
2416
+ */
2417
+ async execute(repository, runtime, tenantId, session) {
2418
+ const accepted = session.record().accepted;
2419
+ if (accepted?.key === undefined || accepted.recipientId === undefined)
2420
+ throw new Error("Claimed Agent invocation lacks accepted source and recipient.");
2421
+ const entityId = InboxMessages.targetEntityId(accepted.recipientId, repository.idField);
2422
+ const support = new ProcessManagerExecutionSupport(repository, runtime);
2423
+ const loaded = await support.load(entityId, tenantId === undefined ? {} : { tenantId });
2424
+ const handlers = this.handlers(repository, accepted);
2425
+ const configuration = repositoryAccess.agentConfiguration(repository);
2426
+ if (runtime.ai === undefined)
2427
+ throw new Error("Agent execution requires a bound AI registry.");
2428
+ this.assertRevisions(repository, accepted, handlers, runtime.ai, configuration);
2429
+ const record = session.record().started === undefined
2430
+ ? await this.start(repository, runtime, session, loaded, entityId, tenantId)
2431
+ : await this.selectMissingModels(repository, runtime, session, accepted, tenantId);
2432
+ if (session.signal.aborted)
2433
+ return;
2434
+ const completed = await this.completeExecution(repository, runtime, tenantId, session, loaded, accepted, handlers, entityId, record.started?.initialVersion ?? create(VersionSchema));
2435
+ if (completed.status === AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_DELIVERY)
2436
+ await this.deliver(runtime, session);
2437
+ },
2438
+ /**
2439
+ * Creates or rejects the draft before one conditional Entity completion.
2440
+ *
2441
+ * @param repository Registered Entity repository for this operation.
2442
+ * @param runtime Bounded Context storage and publication services.
2443
+ * @param tenantId Tenant selected for this accepted signal.
2444
+ * @param session Fenced Agent execution session.
2445
+ * @param loaded Loaded Entity and its repository storage state.
2446
+ * @param accepted Durable original signal and saved handler bindings.
2447
+ * @param handlers Generated handlers in their saved order.
2448
+ * @param entityId Typed recipient Entity identifier.
2449
+ * @param initialVersion Entity version before selected handlers run.
2450
+ * @returns Record after conditional Entity and execution completion.
2451
+ */
2452
+ async completeExecution(repository, runtime, tenantId, session, loaded, accepted, handlers, entityId, initialVersion) {
2453
+ const outputs = await this.outputsOrRejection(repository, runtime, tenantId, session, loaded.entity, accepted, handlers, entityId, initialVersion);
2454
+ return this.complete(repository, runtime, tenantId, session, loaded, entityId, outputs);
2455
+ },
2456
+ /**
2457
+ * Rejects a Command draft and persists only its declared rejection.
2458
+ *
2459
+ * @param repository Registered Entity repository for this operation.
2460
+ * @param runtime Bounded Context storage and publication services.
2461
+ * @param tenantId Tenant selected for this accepted signal.
2462
+ * @param session Fenced Agent execution session.
2463
+ * @param entity Loaded recipient Entity instance.
2464
+ * @param accepted Durable original signal and saved handler bindings.
2465
+ * @param handlers Generated handlers in their saved order.
2466
+ * @param entityId Typed recipient Entity identifier.
2467
+ * @param initialVersion Entity version before selected handlers run.
2468
+ * @returns Prepared outgoing signals, or only the declared rejection.
2469
+ */
2470
+ async outputsOrRejection(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion) {
2471
+ try {
2472
+ return await this.produceOutputs(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion);
2473
+ }
2474
+ catch (error) {
2475
+ if (!RejectionThrowable.is(error) || accepted.signal.case !== "command")
2476
+ throw error;
2477
+ const handler = handlers[0];
2478
+ if (handler === undefined)
2479
+ throw new Error("Agent rejection requires a Command assignee.");
2480
+ RepositorySignals.requireDeclaredRejection(handler.handler, error);
2481
+ session.discardPreferenceChanges();
2482
+ return [this.rejectionOutput(runtime, repository, accepted.signal.value, entityId, error)];
2483
+ }
2484
+ },
2485
+ /**
2486
+ * Wraps the original rejection Event as one pending saved output.
2487
+ *
2488
+ * @param runtime Bounded Context storage and publication services.
2489
+ * @param repository Registered Entity repository for this operation.
2490
+ * @param command Original typed Command envelope.
2491
+ * @param entityId Typed recipient Entity identifier.
2492
+ * @param error Failure raised while processing this accepted signal.
2493
+ * @returns Prepared original rejection Event for durable delivery.
2494
+ */
2495
+ rejectionOutput(runtime, repository, command, entityId, error) {
2496
+ return create(AgentOutgoingSignalSchema, {
2497
+ signal: {
2498
+ case: "event",
2499
+ value: RepositorySignals.rejectionEvent(runtime, repository, command, entityId, error),
2500
+ },
2501
+ });
2502
+ },
2503
+ /**
2504
+ * Restores handler results and binds their declared original output envelopes.
2505
+ *
2506
+ * @param repository Registered Entity repository for this operation.
2507
+ * @param runtime Bounded Context storage and publication services.
2508
+ * @param tenantId Tenant selected for this accepted signal.
2509
+ * @param session Fenced Agent execution session.
2510
+ * @param entity Loaded recipient Entity instance.
2511
+ * @param accepted Durable original signal and saved handler bindings.
2512
+ * @param handlers Generated handlers in their saved order.
2513
+ * @param entityId Typed recipient Entity identifier.
2514
+ * @param initialVersion Entity version before selected handlers run.
2515
+ * @returns Prepared outgoing signals from accepted handler results.
2516
+ */
2517
+ async produceOutputs(repository, runtime, tenantId, session, entity, accepted, handlers, entityId, initialVersion) {
2518
+ const produced = await this.invoke(repository, runtime, tenantId, session, entity, accepted, handlers);
2519
+ return this.bindOutputs(repository, runtime, accepted, entityId, initialVersion, produced);
2520
+ },
2521
+ /**
2522
+ * Commits changed state and the fenced execution record as one provider mutation.
2523
+ *
2524
+ * @param repository Agent registration.
2525
+ * @param runtime Bounded Context Stand and provider services.
2526
+ * @param tenantId Complete delivery tenant.
2527
+ * @param session Serialized provider claim and current record.
2528
+ * @param loaded Entity after handler transaction commit.
2529
+ * @param entityId Original typed recipient.
2530
+ * @param outputs Original emitted Event or Command envelopes.
2531
+ * @returns Complete record after the atomic provider mutation.
2532
+ */
2533
+ async complete(repository, runtime, tenantId, session, loaded, entityId, outputs) {
2534
+ await this.authorizePreferenceChanges(repository, runtime, tenantId, session);
2535
+ const version = RepositoryEntities.repositoryVersion(loaded.entity);
2536
+ const preferences = session.preferences();
2537
+ const emitted = this.emittedHistory(outputs);
2538
+ const preferenceEvents = this.preferenceEvents(repository, runtime, tenantId, session);
2539
+ const historyEntries = [...emitted, ...this.preferenceHistory(preferenceEvents)];
2540
+ const makeNext = (record) => this.completionFacts(record, version, outputs, preferences);
2541
+ const completed = await this.commitCompletion(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries);
2542
+ for (const event of preferenceEvents)
2543
+ await runtime.publishAgentSystemEvent(event);
2544
+ return completed;
2545
+ },
2546
+ /**
2547
+ * Checks effective staged model changes before any completion mutation.
2548
+ *
2549
+ * @param repository Agent registration and model policy.
2550
+ * @param runtime Bounded Context and AI registry.
2551
+ * @param tenantId Accepted tenant when applicable.
2552
+ * @param session Fenced Agent execution and staged preferences.
2553
+ * @returns When every effective change is authorized.
2554
+ */
2555
+ async authorizePreferenceChanges(repository, runtime, tenantId, session) {
2556
+ const policy = repositoryAccess.agentConfiguration(repository).ai;
2557
+ if (policy?.authorizeSelection === undefined)
2558
+ return;
2559
+ const accepted = session.record().accepted;
2560
+ if (runtime.ai === undefined || accepted === undefined)
2561
+ throw new Error("Agent model preference requires accepted start facts.");
2562
+ const deadlineMs = this.savedModelDeadline(session, "preference");
2563
+ const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2564
+ for (const kind of [AiModelKind.GENERATION, AiModelKind.DECISION]) {
2565
+ const previous = this.preferenceModel(session.initialPreferences(), kind);
2566
+ const selected = this.preferenceModel(session.preferences(), kind);
2567
+ if (previous === undefined && selected === undefined)
2568
+ continue;
2569
+ if (previous !== undefined &&
2570
+ selected !== undefined &&
2571
+ equals(ModelRefSchema, previous, selected))
2572
+ continue;
2573
+ await AgentModelSelection.authorizeSelection(runtime.ai, policy, scope, selected, session.signal, deadlineMs);
2574
+ }
2575
+ },
2576
+ /**
2577
+ * Reads an optional saved model deadline and detects incomplete persisted bounds.
2578
+ *
2579
+ * @param session Fenced Agent execution session.
2580
+ * @param context Selection or preference authorization context.
2581
+ * @returns Saved absolute deadline, when configured.
2582
+ */
2583
+ savedModelDeadline(session, context) {
2584
+ const record = session.record();
2585
+ const deadline = record.started?.deadline;
2586
+ if (deadline === undefined && record.started?.bounds?.deadlineMillis !== undefined)
2587
+ throw new Error(`Agent model ${context} deadline was not saved.`);
2588
+ return deadline === undefined
2589
+ ? undefined
2590
+ : Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000);
2591
+ },
2592
+ /**
2593
+ * Applies the same provider fence to preference events and Entity completion.
2594
+ *
2595
+ * @param repository Registered Entity repository for this operation.
2596
+ * @param runtime Bounded Context storage and publication services.
2597
+ * @param tenantId Tenant selected for this accepted signal.
2598
+ * @param session Fenced Agent execution session.
2599
+ * @param loaded Loaded Entity and its repository storage state.
2600
+ * @param entityId Typed recipient Entity identifier.
2601
+ * @param makeNext Callback constructing the next durable execution record.
2602
+ * @param historyEntries Agent history rows included in the fenced mutation.
2603
+ * @returns Saved execution record after conditional completion.
2604
+ */
2605
+ commitCompletion(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries) {
2606
+ return RepositoryEntities.repositoryChanged(loaded.entity)
2607
+ ? this.commitChanged(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries)
2608
+ : session.complete(makeNext, historyEntries);
2609
+ },
2610
+ /**
2611
+ * Wraps original preference Events as indexed Agent history entries.
2612
+ *
2613
+ * @param events Original Event envelopes retained for diagnostics.
2614
+ * @returns Typed Agent history rows for saved preference changes.
2615
+ */
2616
+ preferenceHistory(events) {
2617
+ return events.map((event) => {
2618
+ const occurredAt = event.context?.timestamp;
2619
+ if (occurredAt === undefined)
2620
+ throw new Error("Agent preference Event requires Time.");
2621
+ return create(AgentHistoryEntrySchema, {
2622
+ occurredAt,
2623
+ item: { case: "systemEvent", value: event },
2624
+ });
2625
+ });
2626
+ },
2627
+ /**
2628
+ * Captures only effective preference changes committed by this signal.
2629
+ *
2630
+ * @param repository Registered Entity repository for this operation.
2631
+ * @param runtime Bounded Context storage and publication services.
2632
+ * @param tenantId Tenant selected for this accepted signal.
2633
+ * @param session Fenced Agent execution session.
2634
+ * @returns Original model-selection System Events.
2635
+ */
2636
+ preferenceEvents(repository, runtime, tenantId, session) {
2637
+ const accepted = session.record().accepted;
2638
+ if (accepted?.recipientId === undefined)
2639
+ throw new Error("Agent preference requires accepted work.");
2640
+ const scope = AgentModelSelection.scope(accepted, repository.stateSchema, tenantId);
2641
+ return [AiModelKind.GENERATION, AiModelKind.DECISION].flatMap((kind) => {
2642
+ const previous = this.preferenceModel(session.initialPreferences(), kind);
2643
+ const selected = this.preferenceModel(session.preferences(), kind);
2644
+ if (previous === undefined && selected === undefined)
2645
+ return [];
2646
+ if (previous !== undefined &&
2647
+ selected !== undefined &&
2648
+ equals(ModelRefSchema, previous, selected))
2649
+ return [];
2650
+ return [this.preferenceEvent(accepted, scope, kind, previous, selected, runtime)];
2651
+ });
2652
+ },
2653
+ /**
2654
+ * Builds one effective selection change with its original causal context.
2655
+ *
2656
+ * @param accepted Durable original signal and saved handler bindings.
2657
+ * @param scope Accepted actor, source, Bounded Context, and tenant.
2658
+ * @param kind Generation or decision model kind.
2659
+ * @param previous Previously selected model deployment.
2660
+ * @param selected Newly selected model deployment.
2661
+ * @param runtime Bounded Context storage and publication services.
2662
+ * @returns Original model-selection System Event.
2663
+ */
2664
+ preferenceEvent(accepted, scope, kind, previous, selected, runtime) {
2665
+ const recipientId = accepted.recipientId;
2666
+ if (recipientId === undefined)
2667
+ throw new Error("Agent preference requires a recipient.");
2668
+ const payload = create(SelectionChangedSchema, {
2669
+ agent: scope.agent,
2670
+ sourceSignal: scope.source,
2671
+ kind,
2672
+ ...(previous === undefined ? {} : { previousModel: previous }),
2673
+ ...(selected === undefined ? {} : { selectedModel: selected }),
2674
+ changedAt: Time.currentTime(),
2675
+ });
2676
+ runtime.registerSystemEventSchema(SelectionChangedSchema);
2677
+ return AgentInteractionEvents.envelope(accepted, runtime.signalMetadata, recipientId, SelectionChangedSchema, payload);
2678
+ },
2679
+ /**
2680
+ * Reads an explicit model selection, including inherited defaults as absence.
2681
+ *
2682
+ * @param preferences Current Agent instance model preferences.
2683
+ * @param kind Generation or decision model kind.
2684
+ * @returns Selected deployment reference, or inherited default.
2685
+ */
2686
+ preferenceModel(preferences, kind) {
2687
+ const selected = preferences.find((entry) => entry.kind === kind);
2688
+ return selected?.selection.case === "model" ? selected.selection.value : undefined;
2689
+ },
2690
+ /**
2691
+ * Builds the immutable completion image and emitted domain history.
2692
+ *
2693
+ * @param record Durable Agent execution record.
2694
+ * @param version Entity version associated with the signal.
2695
+ * @param outputs Prepared outgoing domain signals.
2696
+ * @param preferences Current Agent instance model preferences.
2697
+ * @returns Record completed with original outputs and resulting version.
2698
+ */
2699
+ completionFacts(record, version, outputs, preferences) {
2700
+ const initialVersion = record.started?.initialVersion;
2701
+ if (initialVersion === undefined)
2702
+ throw new Error("Agent completion requires its started Version.");
2703
+ record.status =
2704
+ outputs.length === 0
2705
+ ? AgentInvocationStatus.AGENT_INVOCATION_COMPLETED
2706
+ : AgentInvocationStatus.AGENT_INVOCATION_COMPLETED_PENDING_DELIVERY;
2707
+ record.completion = create(AgentExecutionCompletionSchema, {
2708
+ initialVersion,
2709
+ resultingVersion: version,
2710
+ preferences: [...preferences],
2711
+ outgoing: [...outputs],
2712
+ });
2713
+ return record;
2714
+ },
2715
+ /**
2716
+ * Stores only committed domain Events in the Agent's emitted history.
2717
+ *
2718
+ * @param outputs Prepared outgoing domain signals.
2719
+ * @returns Committed domain Event rows for Agent history.
2720
+ */
2721
+ emittedHistory(outputs) {
2722
+ return outputs.flatMap((output) => {
2723
+ if (output.signal.case !== "event" || output.signal.value.context?.rejection !== undefined)
2724
+ return [];
2725
+ const event = output.signal.value;
2726
+ const occurredAt = event.context?.timestamp;
2727
+ if (occurredAt === undefined)
2728
+ throw new Error("Agent emitted Event requires its original timestamp.");
2729
+ return [
2730
+ create(AgentHistoryEntrySchema, {
2731
+ occurredAt,
2732
+ item: { case: "domainEvent", value: event },
2733
+ }),
2734
+ ];
2735
+ });
2736
+ },
2737
+ /**
2738
+ * Commits changed Entity state with the already prepared execution image.
2739
+ *
2740
+ * @param repository Registered Entity repository for this operation.
2741
+ * @param runtime Bounded Context storage and publication services.
2742
+ * @param tenantId Tenant selected for this accepted signal.
2743
+ * @param session Fenced Agent execution session.
2744
+ * @param loaded Loaded Entity and its repository storage state.
2745
+ * @param entityId Typed recipient Entity identifier.
2746
+ * @param makeNext Callback constructing the next durable execution record.
2747
+ * @param historyEntries Agent history rows included in the fenced mutation.
2748
+ * @returns Saved execution record after the changed Entity commit.
2749
+ */
2750
+ async commitChanged(repository, runtime, tenantId, session, loaded, entityId, makeNext, historyEntries) {
2751
+ const version = RepositoryEntities.repositoryVersion(loaded.entity);
2752
+ const state = RepositoryEntities.repositoryState(loaded.entity);
2753
+ const lifecycle = RepositoryEntities.repositoryLifecycle(loaded.entity);
2754
+ const deferred = await standAccess.deferUpdate(runtime.stand, repository.stateSchema, state, RepositoryStand.standUpdateOptions(tenantId, version, lifecycle), repository.metadata);
2755
+ const prepared = RepositoryStand.preparedRecord(repository, deferred, entityId);
2756
+ return this.commitDeferred(repository, session, loaded, entityId, makeNext, historyEntries, deferred, prepared);
2757
+ },
2758
+ /**
2759
+ * Applies the deferred Stand image only after fenced provider completion.
2760
+ *
2761
+ * @param repository Registered Entity repository for this operation.
2762
+ * @param session Fenced Agent execution session.
2763
+ * @param loaded Loaded Entity and its repository storage state.
2764
+ * @param entityId Typed recipient Entity identifier.
2765
+ * @param makeNext Callback constructing the next durable execution record.
2766
+ * @param historyEntries Agent history rows included in the fenced mutation.
2767
+ * @param deferred Prepared Stand update awaiting conditional commit.
2768
+ * @param prepared Prepared conditional Entity commit.
2769
+ * @returns Saved execution record after the deferred Entity commit.
2770
+ */
2771
+ async commitDeferred(repository, session, loaded, entityId, makeNext, historyEntries, deferred, prepared) {
2772
+ try {
2773
+ const completed = await session.complete(makeNext, historyEntries, {
2774
+ context: loaded.storageInput.context,
2775
+ entity: loaded.storageInput,
2776
+ entityId,
2777
+ next: prepared,
2778
+ ...(RepositoryStorage.historyConfiguration(repository).stateHistory
2779
+ ? { states: [prepared] }
2780
+ : {}),
2781
+ });
2782
+ deferred.notify();
2783
+ return completed;
2784
+ }
2785
+ catch (error) {
2786
+ deferred.cancel();
2787
+ throw error;
2788
+ }
2789
+ },
2790
+ /**
2791
+ * Binds declared generated Event and Command results in handler order.
2792
+ *
2793
+ * @param repository Agent declaration.
2794
+ * @param runtime Signal metadata factory.
2795
+ * @param accepted Original accepted envelope.
2796
+ * @param entityId Typed recipient.
2797
+ * @param version Entity Version before this transition.
2798
+ * @param produced Handler results in declaration order.
2799
+ * @returns Original outgoing envelopes ready for one conditional completion.
2800
+ */
2801
+ bindOutputs(repository, runtime, accepted, entityId, version, produced) {
2802
+ const source = accepted.signal;
2803
+ if (source.case !== "command" && source.case !== "event")
2804
+ throw new Error("Agent output requires its accepted source signal.");
2805
+ const routing = repositoryRoutings.get(repository);
2806
+ if (routing === undefined)
2807
+ throw new Error("Agent output requires registered schemas.");
2808
+ return produced.map((signal) => this.bindOutput(repository, runtime, routing, source, entityId, version, signal));
2809
+ },
2810
+ /**
2811
+ * Binds one declared result using its generated Event or Command role.
2812
+ *
2813
+ * @param repository Registered Entity repository for this operation.
2814
+ * @param runtime Bounded Context storage and publication services.
2815
+ * @param routing Registered generated handler routing metadata.
2816
+ * @param source Original accepted source signal.
2817
+ * @param entityId Typed recipient Entity identifier.
2818
+ * @param version Entity version associated with the signal.
2819
+ * @param signal Original domain signal or execution cancellation signal.
2820
+ * @returns Typed outgoing signal with its original envelope.
2821
+ */
2822
+ bindOutput(repository, runtime, routing, source, entityId, version, signal) {
2823
+ const typeName = EntityInvocation.messageTypeName(signal);
2824
+ const eventSchema = routing.producedEventSchemas.find((entry) => entry.typeName === typeName);
2825
+ if (eventSchema !== undefined)
2826
+ return this.bindEventOutput(repository, runtime, source, entityId, version, signal, eventSchema);
2827
+ const schema = routing.producedCommandSchemas.find((entry) => entry.typeName === typeName);
2828
+ if (schema === undefined)
2829
+ throw new Error(`Agent cannot emit undeclared signal "${typeName}".`);
2830
+ return this.bindCommandOutput(runtime, source, signal, schema);
2831
+ },
2832
+ /**
2833
+ * Creates the original domain Event envelope and producer context.
2834
+ *
2835
+ * @param repository Registered Entity repository for this operation.
2836
+ * @param runtime Bounded Context storage and publication services.
2837
+ * @param source Original accepted source signal.
2838
+ * @param entityId Typed recipient Entity identifier.
2839
+ * @param version Entity version associated with the signal.
2840
+ * @param signal Original domain signal or execution cancellation signal.
2841
+ * @param schema Generated message descriptor for the signal.
2842
+ * @returns Typed original Event envelope for durable delivery.
2843
+ */
2844
+ bindEventOutput(repository, runtime, source, entityId, version, signal, schema) {
2845
+ if (source.case !== "command" && source.case !== "event")
2846
+ throw new Error("Missing source.");
2847
+ const metadata = source.case === "command"
2848
+ ? runtime.signalMetadata.eventFromCommand(source.value, { version: version.number })
2849
+ : runtime.signalMetadata.eventFromEvent(source.value, { version: version.number });
2850
+ const event = create(EventSchema, {
2851
+ id: metadata.id,
2852
+ message: AnyMessages.pack(schema, signal),
2853
+ context: RepositorySignals.eventContextWithProducer(metadata.context, repository, entityId, version),
2854
+ });
2855
+ return create(AgentOutgoingSignalSchema, { signal: { case: "event", value: event } });
2856
+ },
2857
+ /**
2858
+ * Creates the original Command envelope from its accepted origin.
2859
+ *
2860
+ * @param runtime Bounded Context storage and publication services.
2861
+ * @param source Original accepted source signal.
2862
+ * @param signal Original domain signal or execution cancellation signal.
2863
+ * @param schema Generated message descriptor for the signal.
2864
+ * @returns Typed original Command envelope for durable delivery.
2865
+ */
2866
+ bindCommandOutput(runtime, source, signal, schema) {
2867
+ if (source.case !== "command" && source.case !== "event")
2868
+ throw new Error("Missing source.");
2869
+ const metadata = source.case === "command"
2870
+ ? runtime.signalMetadata.commandFromCommand(source.value)
2871
+ : runtime.signalMetadata.commandFromEvent(source.value);
2872
+ const command = create(CommandSchema, {
2873
+ id: metadata.id,
2874
+ message: AnyMessages.pack(schema, signal),
2875
+ context: metadata.context,
2876
+ });
2877
+ return create(AgentOutgoingSignalSchema, { signal: { case: "command", value: command } });
2878
+ },
2879
+ /**
2880
+ * Publishes saved envelopes through normal bus acceptance before marking original IDs.
2881
+ *
2882
+ * @param runtime Bound normal bus acceptance callbacks.
2883
+ * @param session Fenced record containing pending saved envelopes.
2884
+ * @returns When the fenced repository operation completes.
2885
+ */
2886
+ async deliver(runtime, session) {
2887
+ const completed = session.record();
2888
+ const key = completed.accepted?.key;
2889
+ if (key === undefined)
2890
+ throw new Error("Agent output delivery requires its accepted key.");
2891
+ for (const [index, output] of (completed.completion?.outgoing ?? []).entries()) {
2892
+ if (session.signal.aborted)
2893
+ return;
2894
+ if (output.delivered)
2895
+ continue;
2896
+ const signal = output.signal;
2897
+ if (signal.case !== "event" && signal.case !== "command")
2898
+ throw new Error("Agent outgoing signal has no original envelope.");
2899
+ const key = this.outputKey(output);
2900
+ const plan = output.plan ??
2901
+ (await (signal.case === "event"
2902
+ ? runtime.prepareSavedEvent(signal.value)
2903
+ : runtime.prepareSavedCommand(signal.value)));
2904
+ if (output.plan === undefined) {
2905
+ await session.update((current) => {
2906
+ const pending = current.completion?.outgoing[index];
2907
+ if (pending?.plan !== undefined || pending?.signal.case !== signal.case)
2908
+ throw new Error("Saved Agent output changed before plan installation.");
2909
+ pending.plan = plan;
2910
+ return current;
2911
+ });
2912
+ }
2913
+ if (signal.case === "event")
2914
+ await runtime.publishSavedEvent(signal.value, plan);
2915
+ else
2916
+ await runtime.publishSavedCommand(signal.value, plan);
2917
+ await session.markDelivered([key]);
2918
+ runtime.recordAcceptedSaved(signal.value);
2919
+ }
2920
+ },
2921
+ /**
2922
+ * Returns the typed original ID of one completed outgoing signal.
2923
+ *
2924
+ * @param output Prepared outgoing domain signal.
2925
+ * @returns Original durable key for the outgoing signal.
2926
+ */
2927
+ outputKey(output) {
2928
+ if (output.signal.case === "event" && output.signal.value.id !== undefined)
2929
+ return create(AgentSignalKeySchema, {
2930
+ id: { case: "event", value: output.signal.value.id },
2931
+ });
2932
+ if (output.signal.case === "command" && output.signal.value.id !== undefined)
2933
+ return create(AgentSignalKeySchema, {
2934
+ id: { case: "command", value: output.signal.value.id },
2935
+ });
2936
+ throw new Error("Agent outgoing signal requires its original typed ID.");
2937
+ },
2938
+ /**
2939
+ * Validates every stored binding against the current generated selectors.
2940
+ *
2941
+ * @param repository Agent registration.
2942
+ * @param accepted Immutable source and ordered bindings.
2943
+ * @returns Current matching handler metadata.
2944
+ */
2945
+ handlers(repository, accepted) {
2946
+ const routing = repositoryRoutings.get(repository);
2947
+ if (routing === undefined)
2948
+ throw new Error("Agent execution requires registered handlers.");
2949
+ const current = this.selectedHandlers(routing, accepted);
2950
+ if (accepted.handlers.length !== current.length || current.length === 0)
2951
+ throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent handler binding changed before replay.");
2952
+ for (const [ordinal, handler] of current.entries()) {
2953
+ const binding = accepted.handlers[ordinal];
2954
+ if (binding === undefined || !AgentAdmission.matchesHandler(binding, handler, ordinal))
2955
+ throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent handler binding changed before replay.");
2956
+ }
2957
+ return current;
2958
+ },
2959
+ /**
2960
+ * Finds current Command or Event handlers from the immutable source envelope.
2961
+ *
2962
+ * @param routing Registered generated handler routing metadata.
2963
+ * @param accepted Durable original signal and saved handler bindings.
2964
+ * @returns Generated handlers selected for the accepted signal.
2965
+ */
2966
+ selectedHandlers(routing, accepted) {
2967
+ if (accepted.signal.case === "command") {
2968
+ const binding = accepted.handlers[0];
2969
+ const current = routing.commandReadiness?.findCommandAssignee(binding?.signalType ?? "");
2970
+ return current === undefined ? [] : [current.registeredHandler];
2971
+ }
2972
+ if (accepted.signal.case !== "event")
2973
+ throw new Error("Agent source envelope is missing.");
2974
+ const event = accepted.signal.value;
2975
+ const packed = EntityInvocation.requireSignalMessage(event.message, "event");
2976
+ const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
2977
+ const value = EntityInvocation.unpackRequired(packed, schema, "event");
2978
+ const external = event.context?.external === true;
2979
+ return [
2980
+ ...routing.eventReactors(schema.typeName, value, external),
2981
+ ...routing.commandReactions(schema.typeName, value, external),
2982
+ ];
2983
+ },
2984
+ /**
2985
+ * Records start facts and the original dispatch System Event before handler execution.
2986
+ *
2987
+ * @param repository Agent registration.
2988
+ * @param runtime Bound services.
2989
+ * @param session Fenced provider claim and current record.
2990
+ * @param loaded Restored Entity and initial Version.
2991
+ * @param entityId Original typed recipient.
2992
+ * @param tenantId Accepted delivery tenant when applicable.
2993
+ * @returns Updated record image for exact subsequent CAS.
2994
+ */
2995
+ async start(repository, runtime, session, loaded, entityId, tenantId) {
2996
+ const accepted = session.record().accepted;
2997
+ if (accepted === undefined)
2998
+ throw new Error("Agent start requires accepted work.");
2999
+ const started = this.startFacts(repository, runtime, accepted, loaded);
3000
+ const audit = this.startAudit(runtime, repository, accepted, entityId);
3001
+ await session.update((current) => {
3002
+ current.started = started;
3003
+ return current;
3004
+ }, [this.startHistory(audit)]);
3005
+ if (accepted.signal.case === "command")
3006
+ HandlerDispatchPublisher.publishCommand(runtime, audit);
3007
+ else
3008
+ HandlerDispatchPublisher.publishReactor(runtime, audit);
3009
+ return this.selectMissingModels(repository, runtime, session, accepted, tenantId);
3010
+ },
3011
+ /**
3012
+ * Stores the original dispatch audit envelope in mandatory Agent history.
3013
+ *
3014
+ * @param audit Original Agent System event envelope.
3015
+ * @returns Typed Agent history row for the original dispatch Event.
3016
+ */
3017
+ startHistory(audit) {
3018
+ const occurredAt = audit.context?.timestamp;
3019
+ if (occurredAt === undefined)
3020
+ throw new Error("Agent dispatch audit requires its timestamp.");
3021
+ return create(AgentHistoryEntrySchema, {
3022
+ occurredAt,
3023
+ item: { case: "systemEvent", value: audit },
3024
+ });
3025
+ },
3026
+ /**
3027
+ * Verifies admission revisions and fixes whole-invocation limits before execution.
3028
+ *
3029
+ * @param repository Registered Entity repository for this operation.
3030
+ * @param runtime Bounded Context storage and publication services.
3031
+ * @param accepted Durable original signal and saved handler bindings.
3032
+ * @param loaded Loaded Entity and its repository storage state.
3033
+ * @returns Durable deadline, initial version, and whole-invocation bounds.
3034
+ */
3035
+ startFacts(repository, runtime, accepted, loaded) {
3036
+ const configuration = repositoryAccess.agentConfiguration(repository);
3037
+ const ai = runtime.ai;
3038
+ if (accepted.key === undefined || ai === undefined || configuration.ai === undefined)
3039
+ throw new Error("Agent execution start requires accepted work and AI policy.");
3040
+ const limits = registryOptions(ai).invocationLimits ?? {};
3041
+ return create(AgentExecutionStartSchema, {
3042
+ ...(limits.deadlineMs === undefined
3043
+ ? {}
3044
+ : { deadline: addMillis(Time.currentTime(), limits.deadlineMs) }),
3045
+ initialVersion: RepositoryEntities.priorVersion(loaded.current),
3046
+ bounds: this.invocationBounds(limits),
3047
+ });
3048
+ },
3049
+ /**
3050
+ * Persists each model kind without a saved connection identity.
3051
+ *
3052
+ * @param repository Agent registration and permitted capabilities.
3053
+ * @param runtime Bound AI registry.
3054
+ * @param session Current fenced execution record.
3055
+ * @param accepted Original accepted signal and actor.
3056
+ * @param tenantId Accepted tenant when applicable.
3057
+ * @returns Record after each missing identity has been saved.
3058
+ */
3059
+ async selectMissingModels(repository, runtime, session, accepted, tenantId) {
3060
+ const ai = runtime.ai;
3061
+ const configuration = repositoryAccess.agentConfiguration(repository).ai;
3062
+ if (ai === undefined || configuration === undefined)
3063
+ throw new Error("Agent model selection requires saved start facts and AI policy.");
3064
+ const deadlineMs = this.savedModelDeadline(session, "selection");
3065
+ const source = accepted.signal.value?.message;
3066
+ for (const kind of ["generation", "decision"]) {
3067
+ if (!configuration.models.some((model) => model.definition.kind === kind))
3068
+ continue;
3069
+ const modelKind = AgentModelSelection.kind(kind);
3070
+ if (session.record().started?.models.some((model) => model.kind === modelKind))
3071
+ continue;
3072
+ const selected = await AgentModelSelection.selectKind(ai, configuration, AgentModelSelection.scope(accepted, repository.stateSchema, tenantId), session.initialPreferences(), session.signal, deadlineMs, kind, source);
3073
+ await this.saveSelectedModel(session, selected);
3074
+ }
3075
+ return session.record();
3076
+ },
3077
+ /**
3078
+ * Writes one selected model under the current Agent claim.
3079
+ *
3080
+ * @param session Fenced Agent execution session.
3081
+ * @param selected Authorized deployment and connection identity.
3082
+ * @returns When the selection is durable.
3083
+ */
3084
+ async saveSelectedModel(session, selected) {
3085
+ await session.update((record) => {
3086
+ if (record.started === undefined)
3087
+ throw new Error("Agent start facts disappeared.");
3088
+ record.started.models.push(selected);
3089
+ return record;
3090
+ });
3091
+ },
3092
+ /**
3093
+ * Rejects accepted work when its exact handler and AI policy changed.
3094
+ *
3095
+ * @param repository Registered Entity repository for this operation.
3096
+ * @param accepted Durable original signal and saved handler bindings.
3097
+ * @param handlers Generated handlers in their saved order.
3098
+ * @param ai Configured AI registry for this Bounded Context.
3099
+ * @param configuration Repository and AI policy used for revision checks.
3100
+ */
3101
+ assertRevisions(repository, accepted, handlers, ai, configuration) {
3102
+ if (configuration.ai === undefined)
3103
+ throw new Error("Agent AI policy is required.");
3104
+ const revisions = AgentRevisions.atAdmission(repository.stateSchema, handlers, ai, configuration.ai);
3105
+ if (accepted.codeRevision?.value !== configuration.codeRevision ||
3106
+ accepted.schemaRevision?.value !== revisions.schema ||
3107
+ accepted.policyRevision?.value !== revisions.policy)
3108
+ throw new AgentExecutionFault("REVISION_CHANGED", "Accepted Agent code, schema, or policy changed before execution.");
3109
+ },
3110
+ /**
3111
+ * Copies the whole-invocation budget into its durable integer fields.
3112
+ *
3113
+ * @param limits Configured whole-invocation limits.
3114
+ * @returns Whole-transition limits persisted at invocation start.
3115
+ */
3116
+ invocationBounds(limits) {
3117
+ return create(AgentInvocationBoundsSchema, {
3118
+ ...(limits.operations === undefined ? {} : { operations: BigInt(limits.operations) }),
3119
+ ...(limits.modelRequests === undefined
3120
+ ? {}
3121
+ : { modelRequests: BigInt(limits.modelRequests) }),
3122
+ ...(limits.toolCalls === undefined ? {} : { toolCalls: BigInt(limits.toolCalls) }),
3123
+ ...(limits.recordedReads === undefined
3124
+ ? {}
3125
+ : { recordedReads: BigInt(limits.recordedReads) }),
3126
+ ...(limits.deadlineMs === undefined ? {} : { deadlineMillis: BigInt(limits.deadlineMs) }),
3127
+ ...(limits.totalInputBytes === undefined
3128
+ ? {}
3129
+ : { totalInputBytes: BigInt(limits.totalInputBytes) }),
3130
+ ...(limits.totalOutputBytes === undefined
3131
+ ? {}
3132
+ : { totalOutputBytes: BigInt(limits.totalOutputBytes) }),
3133
+ ...(limits.maxRecoveryBytes === undefined
3134
+ ? {}
3135
+ : { maxRecoveryBytes: BigInt(limits.maxRecoveryBytes) }),
3136
+ });
3137
+ },
3138
+ /**
3139
+ * Creates a genuine source-specific dispatch System Event before the handler.
3140
+ *
3141
+ * @param runtime Bounded Context storage and publication services.
3142
+ * @param repository Registered Entity repository for this operation.
3143
+ * @param accepted Durable original signal and saved handler bindings.
3144
+ * @param entityId Typed recipient Entity identifier.
3145
+ * @returns Original dispatch System Event envelope.
3146
+ */
3147
+ startAudit(runtime, repository, accepted, entityId) {
3148
+ const source = accepted.signal;
3149
+ if (source.case === "command")
3150
+ return HandlerDispatchPublisher.commandEvent(runtime, repository, source.value, entityId);
3151
+ if (source.case === "event")
3152
+ return HandlerDispatchPublisher.reactorEvent(runtime, repository, source.value, entityId);
3153
+ throw new Error("Agent start requires its accepted envelope.");
3154
+ },
3155
+ /**
3156
+ * Invokes saved Command or Event bindings inside one Entity transaction.
3157
+ *
3158
+ * @param entity Restored Agent instance.
3159
+ * @param accepted Original accepted signal.
3160
+ * @param handlers Validated current handler bindings.
3161
+ * @param repository Registered Entity repository for this operation.
3162
+ * @param runtime Bounded Context storage and publication services.
3163
+ * @param tenantId Tenant selected for this accepted signal.
3164
+ * @param session Fenced Agent execution session.
3165
+ * @returns Outputs returned by the selected generated handlers.
3166
+ */
3167
+ async invoke(repository, runtime, tenantId, session, entity, accepted, handlers) {
3168
+ transactionalEntityAccess.start(entity);
3169
+ try {
3170
+ const signals = [];
3171
+ for (const [ordinal, handler] of handlers.entries()) {
3172
+ signals.push(...(await this.invokeBoundHandler(repository, runtime, tenantId, session, entity, accepted, handler, ordinal)));
3173
+ }
3174
+ await this.commitInvokedEntity(entity);
3175
+ return signals;
3176
+ }
3177
+ catch (error) {
3178
+ transactionalEntityAccess.rollback(entity);
3179
+ throw error;
3180
+ }
3181
+ },
3182
+ /**
3183
+ * Validates the transaction result before Agent output completion.
3184
+ *
3185
+ * @param entity Loaded recipient Entity instance.
3186
+ * @returns When the fenced repository operation completes.
3187
+ */
3188
+ async commitInvokedEntity(entity) {
3189
+ const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
3190
+ if (commit.status === "rejected")
3191
+ throw new TransitionValidationError(commit.validation.error);
3192
+ },
3193
+ /**
3194
+ * Binds named AI and history reads around one generated handler call.
3195
+ *
3196
+ * @param repository Registered Entity repository for this operation.
3197
+ * @param runtime Bounded Context storage and publication services.
3198
+ * @param tenantId Tenant selected for this accepted signal.
3199
+ * @param session Fenced Agent execution session.
3200
+ * @param entity Loaded recipient Entity instance.
3201
+ * @param accepted Durable original signal and saved handler bindings.
3202
+ * @param handler Generated handler selected for this signal.
3203
+ * @param ordinal Saved position of the selected handler.
3204
+ * @returns Outputs returned by one generated handler.
3205
+ */
3206
+ async invokeBoundHandler(repository, runtime, tenantId, session, entity, accepted, handler, ordinal) {
3207
+ const facade = this.handlerFacade(repository, runtime, tenantId, session, accepted, ordinal);
3208
+ const reads = new AgentReadRuntime(session, ordinal);
3209
+ const release = this.bindHandlerCapabilities(entity, runtime, tenantId, accepted, reads, facade);
3210
+ try {
3211
+ const deadlineEpochMs = this.handlerDeadline(session);
3212
+ const signals = await this.awaitHandler(this.invokeHandler(entity, accepted, handler), session.signal, deadlineEpochMs);
3213
+ reads.finish();
3214
+ facade.finish();
3215
+ return signals;
3216
+ }
3217
+ finally {
3218
+ facade.close();
3219
+ release();
3220
+ }
3221
+ },
3222
+ /**
3223
+ * Binds Agent AI, indexed history and routed projection queries for one handler.
3224
+ *
3225
+ * @param entity Loaded recipient Entity instance.
3226
+ * @param runtime Bounded Context storage and publication services.
3227
+ * @param tenantId Tenant selected for this accepted signal.
3228
+ * @param accepted Durable original signal and saved handler bindings.
3229
+ * @param reads Handler-scoped durable read facade.
3230
+ * @param facade Handler-scoped AI facade.
3231
+ * @returns Release callback for handler-scoped AI and read facades.
3232
+ */
3233
+ bindHandlerCapabilities(entity, runtime, tenantId, accepted, reads, facade) {
3234
+ const releaseHistory = AgentHistoryReads.withJournal(entity, (scope, view, request, live, maxBytes) => reads.history(scope, view, request, live, maxBytes));
3235
+ let releaseQuery;
3236
+ try {
3237
+ releaseQuery = ProcessManagerQueries.bind(entity, runtime, accepted.actor, tenantId, (schema, query, live) => reads.query(schema, query, live));
3238
+ const releaseAi = AgentAiBindings.bind(entity, facade);
3239
+ return () => {
3240
+ releaseQuery?.();
3241
+ releaseHistory();
3242
+ releaseAi();
3243
+ };
3244
+ }
3245
+ catch (error) {
3246
+ releaseQuery?.();
3247
+ releaseHistory();
3248
+ throw error;
3249
+ }
3250
+ },
3251
+ /**
3252
+ * Creates one handler-scoped facade from its accepted model policy.
3253
+ *
3254
+ * @param repository Registered Entity repository for this operation.
3255
+ * @param runtime Bounded Context storage and publication services.
3256
+ * @param tenantId Tenant selected for this accepted signal.
3257
+ * @param session Fenced Agent execution session.
3258
+ * @param accepted Durable original signal and saved handler bindings.
3259
+ * @param ordinal Saved position of the selected handler.
3260
+ * @returns AI facade bound to one selected handler.
3261
+ */
3262
+ handlerFacade(repository, runtime, tenantId, session, accepted, ordinal) {
3263
+ const ai = runtime.ai;
3264
+ const configuration = repositoryAccess.agentConfiguration(repository).ai;
3265
+ if (ai === undefined || configuration === undefined)
3266
+ throw new Error("Agent handler requires its bound AI registry.");
3267
+ return new AgentAiRuntime(ai, configuration, AgentModelSelection.scope(accepted, repository.stateSchema, tenantId), session, ordinal, new AgentInteractionAudit(session, accepted, repository.stateSchema, runtime.signalMetadata, runtime.registerSystemEventSchema, runtime.publishAgentSystemEvent));
3268
+ },
3269
+ /**
3270
+ * Invokes one selected generated handler and validates its declared results.
3271
+ *
3272
+ * @param entity Loaded recipient Entity instance.
3273
+ * @param accepted Durable original signal and saved handler bindings.
3274
+ * @param handler Generated handler selected for this signal.
3275
+ * @returns Declared outputs returned by one generated handler.
3276
+ */
3277
+ async invokeHandler(entity, accepted, handler) {
3278
+ const source = accepted.signal;
3279
+ if (source.case !== "command" && source.case !== "event")
3280
+ throw new Error("Agent source envelope is missing.");
3281
+ const kind = source.case;
3282
+ const message = EntityInvocation.unpackRequired(EntityInvocation.requireSignalMessage(source.value.message, kind), handler.handler.schema, kind);
3283
+ const context = source.case === "command"
3284
+ ? EntityInvocation.commandHandlerContext(source.value)
3285
+ : EntityInvocation.eventHandlerContext(source.value);
3286
+ const produced = await EntityInvocation.invokeEntityMethod(entity, handler.handler.methodName, message, handler.handler.parameterCount, context);
3287
+ const signals = produced === undefined
3288
+ ? []
3289
+ : Array.isArray(produced)
3290
+ ? Array.from(produced)
3291
+ : [produced];
3292
+ RepositoryHandlers.requireDeclaredOutputs(handler.handler, signals);
3293
+ return signals;
3294
+ },
3295
+ /**
3296
+ * Reads the original invocation deadline before starting an application callback.
3297
+ *
3298
+ * @param session Current fenced execution session.
3299
+ * @returns Absolute saved deadline in milliseconds.
3300
+ */
3301
+ handlerDeadline(session) {
3302
+ const record = session.record();
3303
+ const deadline = record.started?.deadline;
3304
+ if (deadline === undefined && record.started?.bounds?.deadlineMillis !== undefined)
3305
+ throw new Error("Agent handler requires its saved deadline.");
3306
+ if (session.signal.aborted || this.deadlineExpired(record))
3307
+ throw new Error("Agent handler deadline or claim ended.");
3308
+ return deadline === undefined
3309
+ ? undefined
3310
+ : Number(deadline.seconds) * 1_000 + Math.floor(deadline.nanos / 1_000_000);
3311
+ },
3312
+ /**
3313
+ * Awaits an application handler within its saved deadline and current claim.
3314
+ *
3315
+ * @typeParam Value Handler result.
3316
+ * @param work Already-started generated handler invocation.
3317
+ * @param signal Session cancellation when claim authority ends.
3318
+ * @param deadlineEpochMs Original saved invocation deadline.
3319
+ * @returns Handler result only while its claim and deadline remain valid.
3320
+ */
3321
+ async awaitHandler(work, signal, deadlineEpochMs) {
3322
+ let timer;
3323
+ let abort = () => undefined;
3324
+ const cutoff = new Promise((_resolve, reject) => {
3325
+ abort = () => {
3326
+ reject(new Error("Agent handler claim ended."));
3327
+ };
3328
+ const tick = () => {
3329
+ if (deadlineEpochMs === undefined)
3330
+ return;
3331
+ const remaining = deadlineEpochMs - Time.currentTimeMillis();
3332
+ if (remaining <= 0)
3333
+ reject(new Error("Agent handler deadline expired."));
3334
+ else
3335
+ timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
3336
+ };
3337
+ signal.addEventListener("abort", abort, { once: true });
3338
+ if (signal.aborted)
3339
+ abort();
3340
+ else
3341
+ tick();
3342
+ });
3343
+ try {
3344
+ const result = await Promise.race([work, cutoff]);
3345
+ if (signal.aborted ||
3346
+ (deadlineEpochMs !== undefined && Time.currentTimeMillis() >= deadlineEpochMs))
3347
+ throw new Error("Agent handler deadline or claim ended.");
3348
+ return result;
3349
+ }
3350
+ finally {
3351
+ clearTimeout(timer);
3352
+ signal.removeEventListener("abort", abort);
3353
+ }
3354
+ },
3355
+ };
3356
+ /**
3357
+ * Adds a positive timeout to a Time-sampled Protobuf timestamp.
3358
+ *
3359
+ * @param at Provider-independent current time.
3360
+ * @param millis Bounded additional milliseconds.
3361
+ * @returns Absolute timestamp retaining nanosecond precision.
3362
+ */
3363
+ function addMillis(at, millis) {
3364
+ const nanos = at.nanos + (millis % 1000) * 1_000_000;
3365
+ return {
3366
+ ...at,
3367
+ seconds: at.seconds + BigInt(Math.floor(millis / 1000)) + BigInt(Math.floor(nanos / 1_000_000_000)),
3368
+ nanos: nanos % 1_000_000_000,
3369
+ };
3370
+ }
3371
+ /**
3372
+ * Executes one Process Manager Command against its loaded Entity state.
1755
3373
  */
1756
3374
  class ProcessManagerCommandExecution {
1757
3375
  #repository;
@@ -1764,7 +3382,7 @@ class ProcessManagerCommandExecution {
1764
3382
  *
1765
3383
  * @param repository Process Manager repository receiving the Command.
1766
3384
  * @param routing Registered Command routes and schemas.
1767
- * @param runtime Built context services.
3385
+ * @param runtime Built Bounded Context services.
1768
3386
  * @param command Source Command to execute.
1769
3387
  */
1770
3388
  constructor(repository, routing, runtime, command) {
@@ -1787,7 +3405,7 @@ class ProcessManagerCommandExecution {
1787
3405
  return undefined;
1788
3406
  const tenantOptions = RepositoryTenants.commandStandOptions(this.#runtime.context, this.#command);
1789
3407
  const loaded = await this.#support.load(intake.route.entityId, tenantOptions);
1790
- this.#publishDispatch(intake.route.entityId);
3408
+ await this.#publishDispatch(loaded, intake.route.entityId);
1791
3409
  try {
1792
3410
  const produced = await this.#invoke(loaded.entity, intake.assignee, intake.message, tenantOptions.tenantId);
1793
3411
  return await this.#commitAndPublish(loaded, tenantOptions, intake, produced);
@@ -1800,12 +3418,29 @@ class ProcessManagerCommandExecution {
1800
3418
  }
1801
3419
  }
1802
3420
  /**
1803
- * Publishes a best-effort Command dispatch diagnostic.
3421
+ * Persists required Agent dispatch history before the handler runs.
3422
+ * Process Manager diagnostic publication remains best effort.
1804
3423
  *
1805
- * @param entityId Target Process Manager identifier.
3424
+ * @param loaded Entity with mandatory Agent history when applicable.
3425
+ * @param entityId Target Process Manager or Agent identifier.
3426
+ * @returns Completion after the Agent audit append and publication.
1806
3427
  */
1807
- #publishDispatch(entityId) {
1808
- HandlerDispatchPublisher.command(this.#runtime, this.#repository, this.#command, entityId);
3428
+ async #publishDispatch(loaded, entityId) {
3429
+ if (this.#repository.entityFamily !== "agent") {
3430
+ HandlerDispatchPublisher.command(this.#runtime, this.#repository, this.#command, entityId);
3431
+ return;
3432
+ }
3433
+ const history = loaded.agentHistory;
3434
+ if (history === undefined)
3435
+ throw new Error("Agent history storage is required.");
3436
+ const event = HandlerDispatchPublisher.commandEvent(this.#runtime, this.#repository, this.#command, entityId);
3437
+ if (event.context?.timestamp === undefined)
3438
+ throw new Error("Agent System Event requires its original timestamp.");
3439
+ await history.append(entityId, create(AgentHistoryEntrySchema, {
3440
+ occurredAt: event.context.timestamp,
3441
+ item: { case: "systemEvent", value: event },
3442
+ }));
3443
+ HandlerDispatchPublisher.publishCommand(this.#runtime, event);
1809
3444
  }
1810
3445
  /**
1811
3446
  * Decodes a registered Command and finds its assignee.
@@ -1895,7 +3530,7 @@ class ProcessManagerCommandExecution {
1895
3530
  /**
1896
3531
  * Binds query access for one Command handler invocation.
1897
3532
  *
1898
- * @param entity Process Manager instance.
3533
+ * @param entity Entity instance.
1899
3534
  * @param assignee Registered Command handler.
1900
3535
  * @param message Decoded Command message.
1901
3536
  * @param tenantId Tenant for query reads, when present.
@@ -1913,7 +3548,7 @@ class ProcessManagerCommandExecution {
1913
3548
  /**
1914
3549
  * Invokes a Command handler and checks delivery before the Entity transaction commits.
1915
3550
  *
1916
- * @param entity Process Manager instance to mutate.
3551
+ * @param entity Entity instance to mutate.
1917
3552
  * @param assignee Registered Command handler.
1918
3553
  * @param message Decoded Command message.
1919
3554
  * @returns Validated handler signals after commit.
@@ -1924,7 +3559,7 @@ class ProcessManagerCommandExecution {
1924
3559
  const produced = await EntityInvocation.invokeEntityMethod(entity, assignee.handler.methodName, message, assignee.handler.parameterCount, EntityInvocation.commandHandlerContext(this.#command));
1925
3560
  const signals = this.#support.normalizeProducedSignals(produced);
1926
3561
  if (signals.length === 0)
1927
- throw new Error("Repository Process Manager command handlers must return a signal.");
3562
+ throw new Error("Repository command handlers must return a signal.");
1928
3563
  RepositoryHandlers.requireDeclaredOutputs(assignee.handler, signals);
1929
3564
  const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, assignee.handler.kind === "command-assignment" && signals.length > 0));
1930
3565
  if (commit.status === "rejected") {
@@ -1938,11 +3573,11 @@ class ProcessManagerCommandExecution {
1938
3573
  }
1939
3574
  }
1940
3575
  /**
1941
- * Binds assignment results as Process Manager Events.
3576
+ * Binds assignment results as Entity Events.
1942
3577
  *
1943
3578
  * @param produced Domain Event messages from the handler.
1944
- * @param entityId Target Process Manager identifier.
1945
- * @param version Process Manager Version before this dispatch.
3579
+ * @param entityId Target Entity identifier.
3580
+ * @param version Entity Version before this dispatch.
1946
3581
  * @returns Frozen Event envelope list.
1947
3582
  */
1948
3583
  #bindProducedEvents(produced, entityId, version) {
@@ -1952,15 +3587,15 @@ class ProcessManagerCommandExecution {
1952
3587
  * Packs one declared Event with source and producer context.
1953
3588
  *
1954
3589
  * @param signal Domain Event message to pack.
1955
- * @param entityId Target Process Manager identifier.
1956
- * @param version Process Manager Version before this dispatch.
3590
+ * @param entityId Target Entity identifier.
3591
+ * @param version Entity Version before this dispatch.
1957
3592
  * @returns Bound Event envelope.
1958
3593
  */
1959
3594
  #bindProducedEvent(signal, entityId, version) {
1960
3595
  const typeName = EntityInvocation.messageTypeName(signal);
1961
3596
  const schema = this.#routing.producedEventSchemas.find((candidate) => candidate.typeName === typeName);
1962
3597
  if (schema === undefined) {
1963
- throw new Error(`Repository process-manager execution cannot pack event message "${typeName}".`);
3598
+ throw new Error(`Repository Entity handler cannot pack Event message "${typeName}".`);
1964
3599
  }
1965
3600
  const metadata = this.#runtime.signalMetadata.eventFromCommand(this.#command, {
1966
3601
  version: version.number,
@@ -1982,7 +3617,7 @@ class ProcessManagerCommandExecution {
1982
3617
  const typeName = EntityInvocation.messageTypeName(signal);
1983
3618
  const schema = this.#routing.producedCommandSchemas.find((candidate) => candidate.typeName === typeName);
1984
3619
  if (schema === undefined) {
1985
- throw new Error(`Repository process-manager execution cannot pack command message "${typeName}".`);
3620
+ throw new Error(`Repository Entity handler cannot pack Command message "${typeName}".`);
1986
3621
  }
1987
3622
  const metadata = this.#runtime.signalMetadata.commandFromCommand(this.#command);
1988
3623
  return create(CommandSchema, {
@@ -2018,7 +3653,7 @@ class ProcessManagerCommandExecution {
2018
3653
  }
2019
3654
  }
2020
3655
  /**
2021
- * Runs Process Manager Event handlers and persists their results.
3656
+ * Runs Process Manager or Agent Event handlers and persists their results.
2022
3657
  */
2023
3658
  class ProcessManagerEventExecution {
2024
3659
  #repository;
@@ -2027,9 +3662,9 @@ class ProcessManagerEventExecution {
2027
3662
  #event;
2028
3663
  #support;
2029
3664
  /**
2030
- * Captures Process Manager routing, runtime, and source Event.
3665
+ * Captures Entity routing, runtime, and source Event.
2031
3666
  *
2032
- * @param repository Process Manager repository receiving the Event.
3667
+ * @param repository Entity repository receiving the Event.
2033
3668
  * @param routing Registered Event routes and schemas.
2034
3669
  * @param runtime Built context services.
2035
3670
  * @param event Source Event to execute.
@@ -2042,7 +3677,7 @@ class ProcessManagerEventExecution {
2042
3677
  this.#support = new ProcessManagerExecutionSupport(repository, runtime);
2043
3678
  }
2044
3679
  /**
2045
- * Delivers a routed Event to the durable Process Manager inbox.
3680
+ * Delivers a routed Event to the durable Entity inbox.
2046
3681
  *
2047
3682
  * @param acceptedRoute Route accepted before dispatch.
2048
3683
  * @returns Completion after inbox handoff.
@@ -2062,7 +3697,7 @@ class ProcessManagerEventExecution {
2062
3697
  /**
2063
3698
  * Delivers a routed Event for one target under the duplicate-dispatch guard.
2064
3699
  *
2065
- * @param entityId Target Process Manager identifier.
3700
+ * @param entityId Target Entity identifier.
2066
3701
  * @param acceptedRoute Route accepted before replay.
2067
3702
  * @returns Completion after guarded target execution.
2068
3703
  */
@@ -2099,7 +3734,7 @@ class ProcessManagerEventExecution {
2099
3734
  /**
2100
3735
  * Invokes handlers, commits state, and publishes resulting Events and Commands.
2101
3736
  *
2102
- * @param entityId Target Process Manager identifier.
3737
+ * @param entityId Target Entity identifier.
2103
3738
  * @param intake Decoded Event and matching handlers.
2104
3739
  * @returns Completion after accepted results are published.
2105
3740
  */
@@ -2109,11 +3744,10 @@ class ProcessManagerEventExecution {
2109
3744
  const produced = await this.#invokeHandlers(entityId, loaded.entity, intake, tenantOptions.tenantId);
2110
3745
  const events = this.#bindProducedEvents(produced.events, entityId, RepositoryEntities.priorVersion(loaded.current));
2111
3746
  const commands = this.#bindProducedCommands(produced.commands);
2112
- const diagnostics = [
2113
- DispatchGuards.guardedJournalEvent(this.#repository, this.#event, entityId),
2114
- ...events,
2115
- ];
2116
- const committed = await this.#support.commit(loaded, tenantOptions, diagnostics);
3747
+ const diagnostics = this.#repository.entityFamily === "agent"
3748
+ ? events
3749
+ : [DispatchGuards.guardedJournalEvent(this.#repository, this.#event, entityId), ...events];
3750
+ const committed = await this.#support.commit(loaded, tenantOptions, diagnostics, events);
2117
3751
  if (!committed)
2118
3752
  return;
2119
3753
  this.#publishChangedState(loaded, entityId);
@@ -2121,10 +3755,10 @@ class ProcessManagerEventExecution {
2121
3755
  await this.#postCommands(commands);
2122
3756
  }
2123
3757
  /**
2124
- * Publishes a state-change System Event when the Process Manager changed.
3758
+ * Publishes a state-change System Event when the Entity changed.
2125
3759
  *
2126
- * @param loaded Process Manager before and after handling.
2127
- * @param entityId Target Process Manager identifier.
3760
+ * @param loaded Entity before and after handling.
3761
+ * @param entityId Target Entity identifier.
2128
3762
  */
2129
3763
  #publishChangedState(loaded, entityId) {
2130
3764
  if (!RepositoryEntities.repositoryChanged(loaded.entity))
@@ -2153,8 +3787,8 @@ class ProcessManagerEventExecution {
2153
3787
  /**
2154
3788
  * Invokes Event handlers in one transaction with temporary query access.
2155
3789
  *
2156
- * @param entityId Target Process Manager identifier.
2157
- * @param entity Process Manager instance.
3790
+ * @param entityId Target Entity identifier.
3791
+ * @param entity Entity instance.
2158
3792
  * @param intake Decoded Event and matching handlers.
2159
3793
  * @param tenantId Tenant for query reads, when present.
2160
3794
  * @returns Validated Command and Event message lists.
@@ -2184,8 +3818,8 @@ class ProcessManagerEventExecution {
2184
3818
  /**
2185
3819
  * Invokes reactors and Command producers, collecting their separate outputs.
2186
3820
  *
2187
- * @param entityId Target Process Manager identifier.
2188
- * @param entity Process Manager instance.
3821
+ * @param entityId Target Entity identifier.
3822
+ * @param entity Entity instance.
2189
3823
  * @param intake Decoded Event and matching handlers.
2190
3824
  * @param context Copied source Event context.
2191
3825
  * @param events Output list for Event messages.
@@ -2193,15 +3827,23 @@ class ProcessManagerEventExecution {
2193
3827
  * @returns Completion after matching handlers run.
2194
3828
  */
2195
3829
  async #invokeEventHandlers(entityId, entity, intake, context, events, commands) {
2196
- if (intake.reactors.length > 0)
2197
- HandlerDispatchPublisher.reactor(this.#runtime, this.#repository, this.#event, entityId);
3830
+ if (intake.reactors.length > 0) {
3831
+ if (this.#repository.entityFamily === "agent") {
3832
+ const diagnostic = HandlerDispatchPublisher.reactorEvent(this.#runtime, this.#repository, this.#event, entityId);
3833
+ await AgentHistoryReads.appendSystem(entity, diagnostic);
3834
+ HandlerDispatchPublisher.publishReactor(this.#runtime, diagnostic);
3835
+ }
3836
+ else {
3837
+ HandlerDispatchPublisher.reactor(this.#runtime, this.#repository, this.#event, entityId);
3838
+ }
3839
+ }
2198
3840
  await this.#invokeHandlersInto(entity, intake.reactors, intake.message, context, events);
2199
3841
  await this.#invokeHandlersInto(entity, intake.commanders, intake.message, context, commands);
2200
3842
  }
2201
3843
  /**
2202
3844
  * Invokes each selected handler and appends its declared results.
2203
3845
  *
2204
- * @param entity Process Manager instance.
3846
+ * @param entity Entity instance.
2205
3847
  * @param handlers Handler registrations to invoke.
2206
3848
  * @param message Decoded source Event message.
2207
3849
  * @param context Copied source Event context.
@@ -2219,7 +3861,7 @@ class ProcessManagerEventExecution {
2219
3861
  /**
2220
3862
  * Checks delivery immediately before the Entity transaction commits and raises transition rejection.
2221
3863
  *
2222
- * @param entity Process Manager instance to commit.
3864
+ * @param entity Entity instance to commit.
2223
3865
  * @param producedEvents Whether handlers produced Events.
2224
3866
  * @returns Completion after a successful commit.
2225
3867
  */
@@ -2229,11 +3871,11 @@ class ProcessManagerEventExecution {
2229
3871
  throw new TransitionValidationError(commit.validation.error);
2230
3872
  }
2231
3873
  /**
2232
- * Binds reactor results as Process Manager Events.
3874
+ * Binds reactor results as Entity Events.
2233
3875
  *
2234
3876
  * @param produced Domain Event messages from handlers.
2235
- * @param entityId Target Process Manager identifier.
2236
- * @param version Process Manager Version before this dispatch.
3877
+ * @param entityId Target Entity identifier.
3878
+ * @param version Entity Version before this dispatch.
2237
3879
  * @returns Frozen Event envelope list.
2238
3880
  */
2239
3881
  #bindProducedEvents(produced, entityId, version) {
@@ -2243,15 +3885,15 @@ class ProcessManagerEventExecution {
2243
3885
  * Packs one declared Event with source and producer context.
2244
3886
  *
2245
3887
  * @param signal Domain Event message to pack.
2246
- * @param entityId Target Process Manager identifier.
2247
- * @param version Process Manager Version before this dispatch.
3888
+ * @param entityId Target Entity identifier.
3889
+ * @param version Entity Version before this dispatch.
2248
3890
  * @returns Bound Event envelope.
2249
3891
  */
2250
3892
  #bindProducedEvent(signal, entityId, version) {
2251
3893
  const typeName = EntityInvocation.messageTypeName(signal);
2252
3894
  const schema = this.#routing.producedEventSchemas.find((candidate) => candidate.typeName === typeName);
2253
3895
  if (schema === undefined) {
2254
- throw new Error(`Repository process-manager execution cannot pack event message "${typeName}".`);
3896
+ throw new Error(`Repository Entity handler cannot pack Event message "${typeName}".`);
2255
3897
  }
2256
3898
  const metadata = this.#runtime.signalMetadata.eventFromEvent(this.#event, {
2257
3899
  version: version.number,
@@ -2273,7 +3915,7 @@ class ProcessManagerEventExecution {
2273
3915
  const typeName = EntityInvocation.messageTypeName(signal);
2274
3916
  const schema = this.#routing.producedCommandSchemas.find((candidate) => candidate.typeName === typeName);
2275
3917
  if (schema === undefined) {
2276
- throw new Error(`Repository process-manager execution cannot pack command message "${typeName}".`);
3918
+ throw new Error(`Repository Entity handler cannot pack Command message "${typeName}".`);
2277
3919
  }
2278
3920
  const metadata = this.#runtime.signalMetadata.commandFromEvent(this.#event);
2279
3921
  return create(CommandSchema, {
@@ -2323,7 +3965,7 @@ const RepositoryIdentity = {
2323
3965
  const displayName = RepositoryIdentity.entityTypeName(entityType);
2324
3966
  const schema = RepositoryIdentity.readRepositorySchemaOption(options, displayName, entityFamily);
2325
3967
  const metadata = RepositoryIdentity.describeRepositoryEntityMetadata(displayName, entityFamily, schema);
2326
- if (metadata.kind !== entityFamily) {
3968
+ if (metadata.kind !== (entityFamily === "agent" ? "entity" : entityFamily)) {
2327
3969
  throw new RepositoryIdentityError("ENTITY_SCHEMA_KIND_MISMATCH", `Repository entity type "${displayName}" does not match the supplied state schema.`);
2328
3970
  }
2329
3971
  if (options.stateUpdateRouting !== undefined && entityFamily !== "projection") {
@@ -2340,34 +3982,34 @@ const RepositoryIdentity = {
2340
3982
  requireEntityType(options) {
2341
3983
  if (!RepositoryIdentity.isRepositoryOptionsObject(options)) {
2342
3984
  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.");
3985
+ "extending Aggregate, Projection, ProcessManager, or Agent.");
2344
3986
  }
2345
3987
  const entityType = RepositoryIdentity.readEntityTypeOption(options);
2346
3988
  if (typeof entityType !== "function" || !RepositoryIdentity.isClassConstructor(entityType)) {
2347
3989
  throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", `Repository entity type "${RepositoryIdentity.entityTypeName(entityType)}" must be a class constructor ` +
2348
- "extending Aggregate, Projection, or ProcessManager.");
3990
+ "extending Aggregate, Projection, ProcessManager, or Agent.");
2349
3991
  }
2350
3992
  return entityType;
2351
3993
  },
2352
3994
  /**
2353
- * Validates and returns Aggregate, Projection, or Process Manager inheritance.
3995
+ * Validates and returns Aggregate, Projection, Process Manager, or Agent inheritance.
2354
3996
  *
2355
3997
  * @param entityType Validated class constructor.
2356
- * @returns Aggregate, Projection, or Process Manager.
3998
+ * @returns Aggregate, Projection, Process Manager, or Agent.
2357
3999
  */
2358
4000
  requireEntityFamily(entityType) {
2359
4001
  const family = RepositoryIdentity.resolveRepositoryEntityFamily(entityType);
2360
4002
  if (family !== undefined)
2361
4003
  return family;
2362
4004
  throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", `Repository entity type "${RepositoryIdentity.entityTypeName(entityType)}" must extend ` +
2363
- "Aggregate, Projection, or ProcessManager.");
4005
+ "Aggregate, Projection, ProcessManager, or Agent.");
2364
4006
  },
2365
4007
  /**
2366
4008
  * Creates a frozen identity snapshot from validated Entity metadata.
2367
4009
  *
2368
4010
  * @typeParam EntityType Concrete Entity constructor represented by the snapshot.
2369
4011
  * @param entityType Entity constructor.
2370
- * @param entityFamily Aggregate, Projection, or Process Manager family.
4012
+ * @param entityFamily Aggregate, Projection, Process Manager, or Agent family.
2371
4013
  * @param metadata Descriptor-derived state metadata.
2372
4014
  * @returns Copy-safe repository identity snapshot.
2373
4015
  */
@@ -2421,7 +4063,7 @@ const RepositoryIdentity = {
2421
4063
  }
2422
4064
  catch {
2423
4065
  throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "Repository options entityType must be readable and resolve to a class constructor " +
2424
- "extending Aggregate, Projection, or ProcessManager.");
4066
+ "extending Aggregate, Projection, ProcessManager, or Agent.");
2425
4067
  }
2426
4068
  },
2427
4069
  /**
@@ -2471,22 +4113,21 @@ const RepositoryIdentity = {
2471
4113
  return undefined;
2472
4114
  }
2473
4115
  const runtimeEntityType = entityType;
2474
- if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Aggregate, Aggregate.prototype)) {
4116
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Aggregate, Aggregate.prototype))
2475
4117
  return "aggregate";
2476
- }
2477
- if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Projection, Projection.prototype)) {
4118
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Projection, Projection.prototype))
2478
4119
  return "projection";
2479
- }
2480
- if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, ProcessManager, ProcessManager.prototype)) {
4120
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, ProcessManager, ProcessManager.prototype))
2481
4121
  return "process-manager";
2482
- }
4122
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Agent, Agent.prototype))
4123
+ return "agent";
2483
4124
  return undefined;
2484
4125
  },
2485
4126
  /**
2486
4127
  * Checks constructor and prototype inheritance against an Entity family.
2487
4128
  *
2488
4129
  * @param entityType Candidate constructor.
2489
- * @param familyConstructor Aggregate, Projection, or Process Manager constructor.
4130
+ * @param familyConstructor Entity family constructor.
2490
4131
  * @param familyPrototype Prototype for the same family.
2491
4132
  * @returns Whether both inheritance paths match.
2492
4133
  */
@@ -2697,7 +4338,36 @@ const RepositoryEntities = {
2697
4338
  const storageInput = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
2698
4339
  const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, storageInput);
2699
4340
  RepositoryHistoryInternals.bindEntityHistory(entity, storage, entityId, repository.stateSchema);
2700
- return { commits: storage.commits, events: storage.events, storageInput };
4341
+ if (repository.entityFamily === "agent")
4342
+ this.bindAgentHistory(entity, entityId, storageInput, storage.agentHistory);
4343
+ return {
4344
+ commits: storage.commits,
4345
+ events: storage.events,
4346
+ ...(storage.agentHistory === undefined ? {} : { agentHistory: storage.agentHistory }),
4347
+ storageInput,
4348
+ };
4349
+ },
4350
+ /**
4351
+ * Attaches the indexed history port to one restored Agent.
4352
+ *
4353
+ * @param entity Restored Agent instance.
4354
+ * @param entityId Typed Agent identifier.
4355
+ * @param input Entity storage scope.
4356
+ * @param history Mandatory Agent history port.
4357
+ */
4358
+ bindAgentHistory(entity, entityId, input, history) {
4359
+ if (history === undefined)
4360
+ throw new Error("Agent history storage is required.");
4361
+ AgentHistoryReads.bind(entity, {
4362
+ storage: history,
4363
+ entityId,
4364
+ scope: {
4365
+ context: input.context.name,
4366
+ tenant: JSON.stringify(input.context.tenantId ?? null),
4367
+ repository: input.sourceType.typeName,
4368
+ entity: input.id.key(entityId),
4369
+ },
4370
+ });
2701
4371
  },
2702
4372
  /**
2703
4373
  * Restores an Entity from Stand or creates a fresh instance at Version zero.
@@ -2729,15 +4399,17 @@ const RepositoryEntities = {
2729
4399
  * @param stored Prior Stand snapshot, when present.
2730
4400
  * @param commits Durable commit storage.
2731
4401
  * @param events Durable diagnostic Event history.
4402
+ * @param agentHistory Mandatory Agent history when this is an Agent.
2732
4403
  * @param storageInput Entity storage specification.
2733
4404
  * @param schema Entity state schema.
2734
4405
  * @param entityId Entity identifier.
2735
4406
  * @returns The loaded Entity and prior record.
2736
4407
  */
2737
- loaded(entity, stored, commits, events, storageInput, schema, entityId) {
4408
+ loaded(entity, stored, commits, events, agentHistory, storageInput, schema, entityId) {
2738
4409
  return Object.freeze({
2739
4410
  commits,
2740
4411
  events,
4412
+ ...(agentHistory === undefined ? {} : { agentHistory }),
2741
4413
  current: stored === undefined
2742
4414
  ? undefined
2743
4415
  : EntityRecords.pack(schema, entityId, stored.state, stored.versionMessage, {
@@ -2863,8 +4535,30 @@ const RepositorySignals = {
2863
4535
  * @returns Deferred rejection Event publication.
2864
4536
  */
2865
4537
  postRejectionEvent(runtime, repository, command, entityId, rejection) {
4538
+ const event = this.rejectionEvent(runtime, repository, command, entityId, rejection);
4539
+ return async () => {
4540
+ try {
4541
+ // spine-log-boundary: server.repository_rejection_follow_up
4542
+ await runtime.publisher.publishRejectionEvent(event);
4543
+ }
4544
+ catch (error) {
4545
+ runtime.publisher.reportFailure("event", event, error);
4546
+ }
4547
+ };
4548
+ },
4549
+ /**
4550
+ * Creates the original declared rejection envelope without publishing it.
4551
+ *
4552
+ * @param runtime Bounded Context storage and publication services.
4553
+ * @param repository Registered Entity repository for this operation.
4554
+ * @param command Original typed Command envelope.
4555
+ * @param entityId Typed recipient Entity identifier.
4556
+ * @param rejection Declared rejection from the selected handler.
4557
+ * @returns Original declared-rejection Event envelope.
4558
+ */
4559
+ rejectionEvent(runtime, repository, command, entityId, rejection) {
2866
4560
  const metadata = runtime.signalMetadata.eventFromCommand(command, {});
2867
- const event = create(EventSchema, {
4561
+ return create(EventSchema, {
2868
4562
  id: metadata.id,
2869
4563
  message: AnyMessages.pack(rejection.schema, rejection.messageThrown()),
2870
4564
  context: create(EventContextSchema, {
@@ -2875,15 +4569,6 @@ const RepositorySignals = {
2875
4569
  }),
2876
4570
  }),
2877
4571
  });
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
4572
  },
2888
4573
  /**
2889
4574
  * Validates a nonempty Command ID for Event origin metadata.
@@ -2915,11 +4600,7 @@ const RepositorySignals = {
2915
4600
  * @returns Timestamp derived from the current clock.
2916
4601
  */
2917
4602
  executionTimestamp() {
2918
- const milliseconds = Date.now();
2919
- return create(TimestampSchema, {
2920
- seconds: BigInt(Math.floor(milliseconds / 1_000)),
2921
- nanos: (milliseconds % 1_000) * 1_000_000,
2922
- });
4603
+ return Time.currentTime();
2923
4604
  },
2924
4605
  };
2925
4606
  /**
@@ -3204,20 +4885,40 @@ class HandlerDispatchPublishing {
3204
4885
  */
3205
4886
  command(runtime, repository, command, entityId) {
3206
4887
  try {
3207
- const context = runtime.signalMetadata.eventContext({
3208
- origin: runtime.signalMetadata.originFromCommand(command),
3209
- });
3210
- const event = create(EventSchema, {
3211
- id: runtime.signalMetadata.eventId(),
3212
- message: AnyMessages.pack(EntityLog.CommandDispatchedToHandlerSchema, this.#message(repository, command, entityId, context.timestamp)),
3213
- context,
3214
- });
3215
- this.#post(runtime, EntityLog.CommandDispatchedToHandlerSchema, event);
4888
+ this.publishCommand(runtime, this.commandEvent(runtime, repository, command, entityId));
3216
4889
  }
3217
4890
  catch (error) {
3218
4891
  runtime.publisher.reportFailure("event", create(EventSchema), error);
3219
4892
  }
3220
4893
  }
4894
+ /**
4895
+ * Builds the genuine Command dispatch diagnostic before its publication.
4896
+ *
4897
+ * @param runtime Bounded Context signal metadata.
4898
+ * @param repository Receiving repository.
4899
+ * @param command Source Command.
4900
+ * @param entityId Target Entity identifier.
4901
+ * @returns Original System Event envelope.
4902
+ */
4903
+ commandEvent(runtime, repository, command, entityId) {
4904
+ const context = runtime.signalMetadata.eventContext({
4905
+ origin: runtime.signalMetadata.originFromCommand(command),
4906
+ });
4907
+ return create(EventSchema, {
4908
+ id: runtime.signalMetadata.eventId(),
4909
+ message: AnyMessages.pack(EntityLog.CommandDispatchedToHandlerSchema, this.#message(repository, command, entityId, context.timestamp)),
4910
+ context,
4911
+ });
4912
+ }
4913
+ /**
4914
+ * Sends the exact System envelope already retained for an Agent.
4915
+ *
4916
+ * @param runtime Bounded Context publication services.
4917
+ * @param event Original System Event envelope.
4918
+ */
4919
+ publishCommand(runtime, event) {
4920
+ this.#post(runtime, EntityLog.CommandDispatchedToHandlerSchema, event);
4921
+ }
3221
4922
  /**
3222
4923
  * Publishes a best-effort EventDispatchedToSubscriber diagnostic.
3223
4924
  *
@@ -3240,6 +4941,34 @@ class HandlerDispatchPublishing {
3240
4941
  reactor(runtime, repository, event, entityId) {
3241
4942
  this.#publishEvent(runtime, repository, event, entityId, EntityLog.EventDispatchedToReactorSchema);
3242
4943
  }
4944
+ /**
4945
+ * Builds the genuine Event-to-reactor diagnostic with its original envelope.
4946
+ *
4947
+ * @param runtime Bounded Context signal metadata.
4948
+ * @param repository Receiving repository.
4949
+ * @param source Source Event.
4950
+ * @param entityId Target Entity identifier.
4951
+ * @returns Original System Event envelope.
4952
+ */
4953
+ reactorEvent(runtime, repository, source, entityId) {
4954
+ const context = runtime.signalMetadata.eventContext({
4955
+ origin: runtime.signalMetadata.originFromEvent(source),
4956
+ });
4957
+ return create(EventSchema, {
4958
+ id: runtime.signalMetadata.eventId(),
4959
+ message: AnyMessages.pack(EntityLog.EventDispatchedToReactorSchema, this.#eventMessage(EntityLog.EventDispatchedToReactorSchema, repository, source, entityId, context.timestamp)),
4960
+ context,
4961
+ });
4962
+ }
4963
+ /**
4964
+ * Publishes the same Event-to-reactor diagnostic retained by Agent history.
4965
+ *
4966
+ * @param runtime Bounded Context publication services.
4967
+ * @param event Original System Event envelope.
4968
+ */
4969
+ publishReactor(runtime, event) {
4970
+ this.#post(runtime, EntityLog.EventDispatchedToReactorSchema, event);
4971
+ }
3243
4972
  /**
3244
4973
  * Builds and publishes one subscriber or reactor dispatch diagnostic.
3245
4974
  *
@@ -3755,9 +5484,10 @@ const RepositoryHandlers = {
3755
5484
  throw new RepositoryIdentityError("ENTITY_SCHEMA_KIND_MISMATCH", `Repository entity type "${entityType.name}" does not match the supplied handler metadata.`);
3756
5485
  }
3757
5486
  if (metadata.kind !== "process-manager" &&
5487
+ metadata.kind !== "entity" &&
3758
5488
  (handlersMetadata.commandSubstitutions.length > 0 ||
3759
5489
  handlersMetadata.commandReactions.length > 0)) {
3760
- throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "Only Process Manager repositories support @Command handlers.");
5490
+ throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "Only Process Manager and Agent repositories support @Command handlers.");
3761
5491
  }
3762
5492
  }
3763
5493
  },
@@ -4715,24 +6445,64 @@ const RepositoryStorage = {
4715
6445
  * @typeParam S Generated Entity state type.
4716
6446
  * @param factory Provider storage factory.
4717
6447
  * @param input Entity storage location and schema.
6448
+ * @param agent Whether to open mandatory Agent history.
4718
6449
  * @returns Storage ports closed together by one handle.
4719
6450
  */
4720
- openEntityStorage(factory, input) {
6451
+ openEntityStorage(factory, input, agent = false) {
4721
6452
  const candidate = factory;
4722
- if (candidate.createEntityStorage === undefined) {
4723
- throw new Error("StorageFactory does not provide the required atomic Entity commit storage seam.");
4724
- }
6453
+ if (candidate.createEntityStorage === undefined)
6454
+ throw new Error("StorageFactory does not provide required atomic Entity commit storage.");
4725
6455
  const entity = candidate.createEntityStorage(input);
4726
6456
  const commits = EntityCommitStorageFactories.create(factory, input);
6457
+ let ports;
6458
+ try {
6459
+ if (agent)
6460
+ ports = this.openAgentPorts(factory, input);
6461
+ }
6462
+ catch (error) {
6463
+ commits.close();
6464
+ entity.close();
6465
+ throw error;
6466
+ }
4727
6467
  return {
4728
6468
  ...entity,
4729
6469
  commits,
6470
+ ...ports,
4730
6471
  close: () => {
6472
+ ports?.agentExecution.close();
6473
+ ports?.agentHistory.close();
4731
6474
  commits.close();
4732
6475
  entity.close();
4733
6476
  },
4734
6477
  };
4735
6478
  },
6479
+ /**
6480
+ * Opens both mandatory Agent ports and closes the first if the second fails.
6481
+ *
6482
+ * @typeParam I Typed Agent identifier.
6483
+ * @typeParam S Generated Agent state message.
6484
+ * @param factory Storage factory registered with the Bounded Context.
6485
+ * @param input Original Inbox or storage input.
6486
+ * @returns Indexed history and execution handles for the same tenant.
6487
+ */
6488
+ openAgentPorts(factory, input) {
6489
+ const agentHistory = AgentHistoryStorageFactories.create(factory, {
6490
+ context: input.context,
6491
+ stateType: input.sourceType.typeName,
6492
+ id: { key: input.id.key },
6493
+ });
6494
+ try {
6495
+ const agentExecution = AgentExecutionStorageFactories.create(factory, {
6496
+ entity: input,
6497
+ stateType: input.sourceType.typeName,
6498
+ });
6499
+ return { agentHistory, agentExecution };
6500
+ }
6501
+ catch (error) {
6502
+ agentHistory.close();
6503
+ throw error;
6504
+ }
6505
+ },
4736
6506
  /**
4737
6507
  * Returns one shared Entity storage handle per context and state type.
4738
6508
  *
@@ -4746,7 +6516,7 @@ const RepositoryStorage = {
4746
6516
  openRepositoryEntityStorage(repository, factory, input) {
4747
6517
  const key = JSON.stringify({ context: input.context, state: input.sourceType.typeName });
4748
6518
  let handles = repositoryEntityHandles.get(repository);
4749
- const handle = RepositoryStorage.openEntityStorage(factory, input);
6519
+ const handle = RepositoryStorage.openEntityStorage(factory, input, repository.entityFamily === "agent");
4750
6520
  if (handles === undefined) {
4751
6521
  handles = new Map();
4752
6522
  repositoryEntityHandles.set(repository, handles);
@@ -5206,21 +6976,40 @@ const DispatchGuards = {
5206
6976
  async dispatchOnce(repository, runtime, event, entityId, eventId, journalEventId, depth, guard, dispatch) {
5207
6977
  if (guard.completed.has(eventId))
5208
6978
  return;
5209
- const storage = RepositoryStorage.openEntityStorage(runtime.storageFactory, RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForEvent(runtime.context, event)));
5210
- let persisted;
5211
- try {
5212
- persisted = await storage.events.backward(entityId, depth);
5213
- }
5214
- finally {
5215
- storage.close();
6979
+ if (repository.entityFamily === "agent") {
6980
+ await dispatch();
6981
+ DispatchGuards.rememberGuardCompletion(guard, eventId, depth);
6982
+ return;
5216
6983
  }
5217
- if (persisted.some((candidate) => candidate.id?.value === journalEventId)) {
6984
+ if (await this.wasDispatched(repository, runtime, event, entityId, journalEventId, depth)) {
5218
6985
  DispatchGuards.rememberGuardCompletion(guard, eventId, depth);
5219
6986
  return;
5220
6987
  }
5221
6988
  await dispatch();
5222
6989
  DispatchGuards.rememberGuardCompletion(guard, eventId, depth);
5223
6990
  },
6991
+ /**
6992
+ * Finds an already dispatched Process Manager Event in retained diagnostic history.
6993
+ *
6994
+ * @param repository Process Manager repository.
6995
+ * @param runtime Repository storage runtime.
6996
+ * @param event Incoming Event envelope.
6997
+ * @param entityId Target Process Manager identifier.
6998
+ * @param journalEventId Target-specific diagnostic identifier.
6999
+ * @param depth Maximum retained history read.
7000
+ * @returns Whether the Event was already dispatched.
7001
+ */
7002
+ async wasDispatched(repository, runtime, event, entityId, journalEventId, depth) {
7003
+ const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForEvent(runtime.context, event));
7004
+ const storage = RepositoryStorage.openEntityStorage(runtime.storageFactory, input);
7005
+ try {
7006
+ const persisted = await storage.events.backward(entityId, depth);
7007
+ return persisted.some((candidate) => candidate.id?.value === journalEventId);
7008
+ }
7009
+ finally {
7010
+ storage.close();
7011
+ }
7012
+ },
5224
7013
  /**
5225
7014
  * Records a successful delivery and discards the oldest excess identifiers.
5226
7015
  *
@@ -5465,6 +7254,10 @@ const InboxReplay = {
5465
7254
  * @returns Any Command follow-up work that must wait for Inbox completion.
5466
7255
  */
5467
7256
  async replayPmInbox(repository, routing, message, deliveryTenantId) {
7257
+ if (repository.entityFamily === "agent") {
7258
+ await InboxReplay.replayAgentInbox(repository, routing, message, deliveryTenantId);
7259
+ return undefined;
7260
+ }
5468
7261
  if (message.label === "HANDLE_COMMAND") {
5469
7262
  return await InboxReplay.replayProcessManagerCommand(repository, routing, message, deliveryTenantId);
5470
7263
  }
@@ -5474,6 +7267,152 @@ const InboxReplay = {
5474
7267
  }
5475
7268
  throw new Error(`Entity Inbox replay does not handle "${message.label}" messages.`);
5476
7269
  },
7270
+ /**
7271
+ * Persists selected Agent work before the normal Inbox acknowledges delivery.
7272
+ *
7273
+ * @param repository Agent repository receiving the original signal.
7274
+ * @param routing Registered handler routes and schemas.
7275
+ * @param message Original durable Inbox message.
7276
+ * @param deliveryTenantId Tenant selected by delivery.
7277
+ * @returns When the fenced repository operation completes.
7278
+ */
7279
+ async replayAgentInbox(repository, routing, message, deliveryTenantId) {
7280
+ const runtime = repositoryRuntimes.get(repository);
7281
+ if (runtime === undefined)
7282
+ throw new Error("Agent Inbox replay requires a bound runtime.");
7283
+ if (message.label === "HANDLE_COMMAND") {
7284
+ await InboxReplay.replayAgentCommand(repository, routing, runtime, message, deliveryTenantId);
7285
+ return;
7286
+ }
7287
+ if (message.label !== "REACT_UPON_EVENT")
7288
+ throw new Error(`Agent Inbox replay does not handle "${message.label}" messages.`);
7289
+ await InboxReplay.replayAgentEvent(repository, routing, runtime, message, deliveryTenantId);
7290
+ },
7291
+ /**
7292
+ * Admits a Command using its stored target and registered assignee.
7293
+ *
7294
+ * @param repository Agent repository.
7295
+ * @param routing Registered Command routes.
7296
+ * @param runtime Bounded Context services.
7297
+ * @param message Original Inbox row.
7298
+ * @param tenantId Delivery tenant.
7299
+ * @returns When the fenced repository operation completes.
7300
+ */
7301
+ async replayAgentCommand(repository, routing, runtime, message, tenantId) {
7302
+ const command = InboxMessages.readInboxCommand(message);
7303
+ InboxReplay.validateReplayTenant(runtime.context, tenantId, command);
7304
+ InboxReplay.validateReplayedCommandPayload(routing, command);
7305
+ const route = InboxReplay.replayCommandRoute(repository, routing, message, command);
7306
+ const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
7307
+ if (assignee === undefined)
7308
+ throw new Error("Agent Command has no selected assignee.");
7309
+ await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityId, { kind: "command", value: command }, command.context?.actorContext ?? create(ActorContextSchema), [assignee.registeredHandler]);
7310
+ },
7311
+ /**
7312
+ * Admits matching Event handlers from the stored target without rerouting.
7313
+ *
7314
+ * @param repository Agent repository.
7315
+ * @param routing Registered Event routes.
7316
+ * @param runtime Bounded Context services.
7317
+ * @param message Original Inbox row.
7318
+ * @param tenantId Delivery tenant.
7319
+ * @returns When the fenced repository operation completes.
7320
+ */
7321
+ async replayAgentEvent(repository, routing, runtime, message, tenantId) {
7322
+ const event = InboxMessages.readPmInboxEvent(message);
7323
+ InboxReplay.validatePmReplayTenant(runtime.context, tenantId, event);
7324
+ InboxReplay.validateReplayedEventPayload(routing, event, "Agent Inbox replay requires an Event payload.");
7325
+ const route = InboxReplay.replayEventRoute(repository, routing, message, event, "Agent Inbox replay");
7326
+ const handlers = InboxReplay.agentEventHandlers(routing, route.messageFullTypeName, event);
7327
+ if (handlers.length === 0)
7328
+ return;
7329
+ await InboxReplay.admitAgent(repository, runtime, message, tenantId, route.entityIds[0], { kind: "event", value: event }, runtime.signalMetadata.originFromEvent(event).actorContext ?? create(ActorContextSchema), handlers);
7330
+ },
7331
+ /**
7332
+ * Restores selected Event and Command reactions from the original payload.
7333
+ *
7334
+ * @param routing Registered generated handler routing metadata.
7335
+ * @param typeName Generated message type name.
7336
+ * @param event Original typed Event envelope.
7337
+ * @returns Generated reactions selected for the accepted Event.
7338
+ */
7339
+ agentEventHandlers(routing, typeName, event) {
7340
+ const packed = EntityInvocation.requireSignalMessage(event.message, "event");
7341
+ const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
7342
+ const value = EntityInvocation.unpackRequired(packed, schema, "event");
7343
+ const external = event.context?.external === true;
7344
+ return [
7345
+ ...routing.eventReactors(typeName, value, external),
7346
+ ...routing.commandReactions(typeName, value, external),
7347
+ ];
7348
+ },
7349
+ /**
7350
+ * Opens the actual tenant handle and records a selected invocation before handoff.
7351
+ *
7352
+ * @param repository Registered Agent repository.
7353
+ * @param runtime Bounded Context services including the effective registry.
7354
+ * @param message Durable Inbox receipt.
7355
+ * @param tenantId Selected delivery tenant.
7356
+ * @param entityId Typed target from the stored route.
7357
+ * @param signal Original source envelope.
7358
+ * @param actor Original actor metadata.
7359
+ * @param handlers Selected handler metadata in execution order.
7360
+ * @returns When the fenced repository operation completes.
7361
+ */
7362
+ async admitAgent(repository, runtime, message, tenantId, entityId, signal, actor, handlers) {
7363
+ const revisions = InboxReplay.agentRevisions(repository, runtime, handlers);
7364
+ const input = RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForTenant(runtime.context, tenantId));
7365
+ const execution = InboxReplay.agentExecutionHandle(repository, runtime, input);
7366
+ await execution.admit(AgentAdmission.create({
7367
+ stateType: repository.stateSchema.typeName,
7368
+ agentKey: input.id.key(entityId),
7369
+ recipientId: message.inboxId.targetId,
7370
+ signal,
7371
+ actor,
7372
+ inbox: message,
7373
+ handlers,
7374
+ codeRevision: revisions.codeRevision,
7375
+ schemaRevision: revisions.schema,
7376
+ policyRevision: revisions.policy,
7377
+ }));
7378
+ runtime.wakeAcceptedAgent(repository, tenantId);
7379
+ },
7380
+ /**
7381
+ * Opens the actual tenant's mandatory execution handle.
7382
+ *
7383
+ * @typeParam I Typed Agent identifier.
7384
+ * @typeParam S Generated Agent state message.
7385
+ * @param repository Registered Entity repository for this operation.
7386
+ * @param runtime Bounded Context storage and publication services.
7387
+ * @param input Original Inbox or storage input.
7388
+ * @returns Fenced execution handle for the accepted tenant.
7389
+ */
7390
+ agentExecutionHandle(repository, runtime, input) {
7391
+ const storage = RepositoryStorage.openRepositoryEntityStorage(repository, runtime.storageFactory, input);
7392
+ if (storage.agentExecution === undefined)
7393
+ throw new Error("Agent Inbox replay requires durable execution storage.");
7394
+ return storage.agentExecution;
7395
+ },
7396
+ /**
7397
+ * Records the registered code, schema, and AI policy before Inbox handoff.
7398
+ *
7399
+ * @param repository Registered Entity repository for this operation.
7400
+ * @param runtime Bounded Context storage and publication services.
7401
+ * @param handlers Generated handlers in their saved order.
7402
+ * @returns Pinned code, schema, and AI policy digests.
7403
+ */
7404
+ agentRevisions(repository, runtime, handlers) {
7405
+ const ai = runtime.ai;
7406
+ const configuration = repositoryAccess.agentConfiguration(repository);
7407
+ if (ai === undefined ||
7408
+ configuration.ai === undefined ||
7409
+ configuration.codeRevision === undefined)
7410
+ throw new Error("Agent Inbox replay requires registered AI capabilities and revisions.");
7411
+ return {
7412
+ codeRevision: configuration.codeRevision,
7413
+ ...AgentRevisions.atAdmission(repository.stateSchema, handlers, ai, configuration.ai),
7414
+ };
7415
+ },
5477
7416
  /**
5478
7417
  * Dispatches a stored Process Manager Command after tenant and payload validation.
5479
7418
  *
@@ -5784,12 +7723,12 @@ const InboxHandoff = {
5784
7723
  * @param runtime Inbox, storage, tenant mode, and delivery strategy.
5785
7724
  * @param command Command envelope to retain and deliver.
5786
7725
  * @returns Completion of Inbox receipt and its selected delivery operation.
7726
+ * @param acceptedRoute Recipient route saved when the signal was accepted.
5787
7727
  */
5788
- async handoffEntityCommand(repository, runtime, command) {
5789
- const route = await repository.routeCommand(command);
7728
+ async handoffEntityCommand(repository, runtime, command, acceptedRoute) {
7729
+ const route = acceptedRoute ?? (await repository.routeCommand(command));
5790
7730
  const commandId = RepositorySignals.requireCommandId(command);
5791
- const whenReceived = new Date();
5792
- const keepUntil = new Date(whenReceived.getTime() + inboxDedupMs);
7731
+ const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
5793
7732
  const deliveryTenantId = RepositoryTenants.requireCommandTenant(runtime.context, command);
5794
7733
  const delivery = new Delivery({
5795
7734
  context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
@@ -5819,8 +7758,7 @@ const InboxHandoff = {
5819
7758
  */
5820
7759
  async handoffProjectionEvent(repository, runtime, event, entityId) {
5821
7760
  const eventId = RepositorySignals.requireEventId(event);
5822
- const whenReceived = new Date();
5823
- const keepUntil = new Date(whenReceived.getTime() + inboxDedupMs);
7761
+ const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
5824
7762
  const deliveryTenantId = RepositoryTenants.requireProjectionTenant(runtime.context, event);
5825
7763
  const delivery = new Delivery({
5826
7764
  context: RepositoryTenants.projectionDeliveryContext(runtime.context, deliveryTenantId),
@@ -5850,8 +7788,7 @@ const InboxHandoff = {
5850
7788
  */
5851
7789
  async handoffPmEvent(repository, runtime, event, entityId) {
5852
7790
  const eventId = RepositorySignals.requireEventId(event);
5853
- const whenReceived = new Date();
5854
- const keepUntil = new Date(whenReceived.getTime() + inboxDedupMs);
7791
+ const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
5855
7792
  const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
5856
7793
  const delivery = new Delivery({
5857
7794
  context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
@@ -5871,8 +7808,7 @@ const InboxHandoff = {
5871
7808
  */
5872
7809
  async handoffPmEvents(repository, runtime, event, entityIds) {
5873
7810
  const eventId = RepositorySignals.requireEventId(event);
5874
- const whenReceived = new Date();
5875
- const keepUntil = new Date(whenReceived.getTime() + inboxDedupMs);
7811
+ const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
5876
7812
  const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
5877
7813
  const delivery = new Delivery({
5878
7814
  context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
@@ -5911,6 +7847,255 @@ const InboxHandoff = {
5911
7847
  },
5912
7848
  };
5913
7849
  Object.freeze(InboxHandoff);
7850
+ /**
7851
+ * Rebinds generated repository recipients without repeating accepted route queries.
7852
+ */
7853
+ const SavedRepositoryDispatch = {
7854
+ /**
7855
+ * Maps a generated repository family to its serialized binding value.
7856
+ *
7857
+ * @param repository Registered Entity repository for this operation.
7858
+ * @returns Repository family encoded in a saved dispatch target.
7859
+ */
7860
+ family(repository) {
7861
+ switch (repository.entityFamily) {
7862
+ case "aggregate":
7863
+ return AgentSavedRepositoryFamily.AGENT_SAVED_AGGREGATE;
7864
+ case "projection":
7865
+ return AgentSavedRepositoryFamily.AGENT_SAVED_PROJECTION;
7866
+ case "process-manager":
7867
+ return AgentSavedRepositoryFamily.AGENT_SAVED_PROCESS_MANAGER;
7868
+ case "agent":
7869
+ return AgentSavedRepositoryFamily.AGENT_SAVED_AGENT;
7870
+ }
7871
+ },
7872
+ /**
7873
+ * Finds the current generated handlers that determine an Event handoff.
7874
+ *
7875
+ * @param routing Registered generated handler routing metadata.
7876
+ * @param event Original typed Event envelope.
7877
+ * @returns Generated handlers matching the outgoing Event.
7878
+ */
7879
+ eventHandlers(routing, event) {
7880
+ const packed = EntityInvocation.requireSignalMessage(event.message, "event");
7881
+ const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
7882
+ const value = EntityInvocation.unpackRequired(packed, schema, "event");
7883
+ const external = event.context?.external === true;
7884
+ return [
7885
+ ...routing.eventReactors(schema.typeName, value, external),
7886
+ ...routing.commandReactions(schema.typeName, value, external),
7887
+ ...routing
7888
+ .eventSubscribers(schema.typeName, value, external)
7889
+ .map((entry) => entry.registeredHandler),
7890
+ ];
7891
+ },
7892
+ /**
7893
+ * Calculates a digest of ordered generated metadata and message descriptors.
7894
+ *
7895
+ * @param repository Registered Entity repository for this operation.
7896
+ * @param handlers Generated handlers in their saved order.
7897
+ * @returns Digest of ordered generated handler metadata.
7898
+ */
7899
+ fingerprint(repository, handlers) {
7900
+ const selected = handlers.map(({ entity, handler }, ordinal) => ({
7901
+ ordinal,
7902
+ receiver: entity.schema.typeName,
7903
+ kind: handler.kind,
7904
+ method: handler.methodName,
7905
+ signal: handler.messageFullTypeName,
7906
+ origin: handler.origin,
7907
+ parameters: handler.parameterCount,
7908
+ where: handler.where === undefined ? null : [handler.where.eventField, handler.where.equals],
7909
+ descriptor: Buffer.from(toBinary(FileDescriptorProtoSchema, handler.schema.file.proto)).toString("base64"),
7910
+ }));
7911
+ const state = Buffer.from(toBinary(FileDescriptorProtoSchema, repository.stateSchema.file.proto));
7912
+ return createHash("sha256").update(state).update(JSON.stringify(selected)).digest("hex");
7913
+ },
7914
+ /**
7915
+ * Restores a typed ID only when its canonical wire representation is unchanged.
7916
+ *
7917
+ * @param repository Registered Entity repository for this operation.
7918
+ * @param packed Original typed Protobuf payload.
7919
+ * @returns Typed recipient identifier from the frozen route.
7920
+ */
7921
+ recipient(repository, packed) {
7922
+ const id = InboxMessages.targetEntityId(packed, repository.idField);
7923
+ if (!equals(AnySchema, InboxMessages.inboxTargetId(id, repository.idField), packed))
7924
+ throw new Error("Saved Agent output recipient ID is not canonical for its repository.");
7925
+ return id;
7926
+ },
7927
+ /**
7928
+ * Checks the stable repository and signal identity before saved transport.
7929
+ *
7930
+ * @param repository Registered Entity repository for this operation.
7931
+ * @param target Frozen recipient binding.
7932
+ * @param kind Generation or decision model kind.
7933
+ * @returns Whether the checked condition holds.
7934
+ */
7935
+ matches(repository, target, kind) {
7936
+ return (target.kind === kind &&
7937
+ target.receiverStateType === repository.stateSchema.typeName &&
7938
+ target.repositoryFamily === SavedRepositoryDispatch.family(repository));
7939
+ },
7940
+ /**
7941
+ * Prepares one typed Command recipient with its selected generated assignee.
7942
+ *
7943
+ * @param repository Registered Entity repository for this operation.
7944
+ * @param routing Registered generated handler routing metadata.
7945
+ * @param command Original typed Command envelope.
7946
+ * @returns Validated frozen Command recipient target.
7947
+ */
7948
+ async prepareCommand(repository, routing, command) {
7949
+ const route = await repository.routeCommand(command);
7950
+ const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
7951
+ if (assignee === undefined)
7952
+ throw new Error("Saved Agent Command has no generated assignee.");
7953
+ return create(SavedTargetSchema, {
7954
+ kind: AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND,
7955
+ receiverStateType: repository.stateSchema.typeName,
7956
+ repositoryFamily: SavedRepositoryDispatch.family(repository),
7957
+ signalType: route.messageFullTypeName,
7958
+ bindingFingerprint: SavedRepositoryDispatch.fingerprint(repository, [
7959
+ assignee.registeredHandler,
7960
+ ]),
7961
+ recipients: [InboxMessages.inboxTargetId(route.entityId, repository.idField)],
7962
+ });
7963
+ },
7964
+ /**
7965
+ * Resolves the original Command recipient without calling its route again.
7966
+ *
7967
+ * @param repository Registered Entity repository for this operation.
7968
+ * @param routing Registered generated handler routing metadata.
7969
+ * @param command Original typed Command envelope.
7970
+ * @param target Frozen recipient binding.
7971
+ * @returns When the fenced repository operation completes.
7972
+ */
7973
+ bindCommand(repository, routing, command, target) {
7974
+ const { runtime, signalType, recipient } = SavedRepositoryDispatch.requireCommandBinding(repository, routing, command, target);
7975
+ const entityId = SavedRepositoryDispatch.recipient(repository, recipient);
7976
+ const route = {
7977
+ entityId,
7978
+ messageFullTypeName: signalType,
7979
+ invocation: "deferred",
7980
+ };
7981
+ return Promise.resolve(() => InboxHandoff.handoffEntityCommand(repository, runtime, command, route));
7982
+ },
7983
+ /**
7984
+ * Checks saved Command metadata, typed target presence, and runtime registration.
7985
+ *
7986
+ * @param repository Registered Entity repository for this operation.
7987
+ * @param routing Registered generated handler routing metadata.
7988
+ * @param command Original typed Command envelope.
7989
+ * @param target Frozen recipient binding.
7990
+ * @returns Registered Command route and original recipient.
7991
+ */
7992
+ requireCommandBinding(repository, routing, command, target) {
7993
+ const packed = EntityInvocation.requireSignalMessage(command.message, "command");
7994
+ const schema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, packed.typeUrl, "command");
7995
+ const assignee = routing.commandReadiness?.findCommandAssignee(schema.typeName);
7996
+ const fingerprint = assignee === undefined
7997
+ ? ""
7998
+ : SavedRepositoryDispatch.fingerprint(repository, [assignee.registeredHandler]);
7999
+ const recipient = target.recipients[0];
8000
+ const runtime = repositoryRuntimes.get(repository);
8001
+ if (!SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND) ||
8002
+ target.signalType !== schema.typeName ||
8003
+ target.bindingFingerprint !== fingerprint ||
8004
+ target.recipients.length !== 1 ||
8005
+ recipient === undefined ||
8006
+ runtime === undefined)
8007
+ throw new Error("Saved Agent Command repository binding changed before delivery.");
8008
+ return { runtime, signalType: schema.typeName, recipient };
8009
+ },
8010
+ /**
8011
+ * Prepares the exact ordered Event recipients and selected generated handlers.
8012
+ *
8013
+ * @param repository Registered Entity repository for this operation.
8014
+ * @param routing Registered generated handler routing metadata.
8015
+ * @param event Original typed Event envelope.
8016
+ * @returns Validated frozen Event recipient target.
8017
+ */
8018
+ async prepareEvent(repository, routing, event) {
8019
+ const route = await repository.routeEvent(event);
8020
+ return create(SavedTargetSchema, {
8021
+ kind: AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_EVENT,
8022
+ receiverStateType: repository.stateSchema.typeName,
8023
+ repositoryFamily: SavedRepositoryDispatch.family(repository),
8024
+ signalType: route.messageFullTypeName,
8025
+ bindingFingerprint: SavedRepositoryDispatch.fingerprint(repository, SavedRepositoryDispatch.eventHandlers(routing, event)),
8026
+ recipients: route.entityIds.map((id) => InboxMessages.inboxTargetId(id, repository.idField)),
8027
+ });
8028
+ },
8029
+ /**
8030
+ * Restores the saved Event route and current generated handler metadata.
8031
+ *
8032
+ * @param repository Registered Entity repository for this operation.
8033
+ * @param routing Registered generated handler routing metadata.
8034
+ * @param event Original typed Event envelope.
8035
+ * @param target Frozen recipient binding.
8036
+ * @returns When the fenced repository operation completes.
8037
+ */
8038
+ bindEvent(repository, routing, event, target) {
8039
+ const { signalType } = SavedRepositoryDispatch.requireEventBinding(repository, routing, event, target);
8040
+ const entityIds = target.recipients.map((id) => SavedRepositoryDispatch.recipient(repository, id));
8041
+ const route = {
8042
+ entityIds,
8043
+ messageFullTypeName: signalType,
8044
+ invocation: "deferred",
8045
+ };
8046
+ return Promise.resolve(() => RepositoryDispatch.dispatchRepositoryEvent(repository, routing, event, route));
8047
+ },
8048
+ /**
8049
+ * Checks saved Event metadata and active repository registration.
8050
+ *
8051
+ * @param repository Registered Entity repository for this operation.
8052
+ * @param routing Registered generated handler routing metadata.
8053
+ * @param event Original typed Event envelope.
8054
+ * @param target Frozen recipient binding.
8055
+ * @returns Registered Event route for the frozen target.
8056
+ */
8057
+ requireEventBinding(repository, routing, event, target) {
8058
+ const packed = EntityInvocation.requireSignalMessage(event.message, "event");
8059
+ const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, packed.typeUrl, "event");
8060
+ const fingerprint = SavedRepositoryDispatch.fingerprint(repository, SavedRepositoryDispatch.eventHandlers(routing, event));
8061
+ const runtime = repositoryRuntimes.get(repository);
8062
+ if (!SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_EVENT) ||
8063
+ target.signalType !== schema.typeName ||
8064
+ target.bindingFingerprint !== fingerprint ||
8065
+ runtime === undefined)
8066
+ throw new Error("Saved Agent Event repository binding changed before delivery.");
8067
+ return { runtime, signalType: schema.typeName };
8068
+ },
8069
+ /**
8070
+ * Creates one internal Command binding from the repository registration.
8071
+ *
8072
+ * @param repository Registered Entity repository for this operation.
8073
+ * @param routing Registered generated handler routing metadata.
8074
+ * @returns Saved Command binding for this registration.
8075
+ */
8076
+ commandBinding(repository, routing) {
8077
+ return {
8078
+ prepare: (command) => SavedRepositoryDispatch.prepareCommand(repository, routing, command),
8079
+ matches: (target) => SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_COMMAND),
8080
+ bind: (command, target) => SavedRepositoryDispatch.bindCommand(repository, routing, command, target),
8081
+ };
8082
+ },
8083
+ /**
8084
+ * Creates one internal Event binding from the repository registration.
8085
+ *
8086
+ * @param repository Registered Entity repository for this operation.
8087
+ * @param routing Registered generated handler routing metadata.
8088
+ * @returns Saved Event binding for this registration.
8089
+ */
8090
+ eventBinding(repository, routing) {
8091
+ return {
8092
+ prepare: (event) => SavedRepositoryDispatch.prepareEvent(repository, routing, event),
8093
+ matches: (target) => SavedRepositoryDispatch.matches(repository, target, AgentSavedTargetKind.AGENT_SAVED_REPOSITORY_EVENT),
8094
+ bind: (event, target) => SavedRepositoryDispatch.bindEvent(repository, routing, event, target),
8095
+ };
8096
+ },
8097
+ };
8098
+ Object.freeze(SavedRepositoryDispatch);
5914
8099
  /**
5915
8100
  * Connects bus dispatchers and Inbox replay to repository execution.
5916
8101
  */
@@ -5939,12 +8124,13 @@ const RepositoryDispatch = {
5939
8124
  * @returns The dispatcher, or undefined when no Command schema is registered.
5940
8125
  */
5941
8126
  commandDispatcher(repository, routing) {
5942
- return routing.commandSchemas.length === 0
5943
- ? undefined
5944
- : Object.freeze({
5945
- messageSchemas: () => routing.commandSchemas,
5946
- dispatch: (command) => RepositoryDispatch.dispatchRepositoryCommand(repository, routing, command),
5947
- });
8127
+ if (routing.commandSchemas.length === 0)
8128
+ return undefined;
8129
+ const dispatcher = Object.freeze({
8130
+ messageSchemas: () => routing.commandSchemas,
8131
+ dispatch: (command) => RepositoryDispatch.dispatchRepositoryCommand(repository, routing, command),
8132
+ });
8133
+ return SavedDispatcherBindings.command(dispatcher, SavedRepositoryDispatch.commandBinding(repository, routing));
5948
8134
  },
5949
8135
  /**
5950
8136
  * Creates an Event dispatcher that preserves routes computed during acceptance.
@@ -5969,7 +8155,8 @@ const RepositoryDispatch = {
5969
8155
  return RepositoryDispatch.dispatchRepositoryEvent(repository, routing, event, route);
5970
8156
  },
5971
8157
  });
5972
- return EventDispatcherOriginSchemas.define(dispatcher, routing.domesticEventSchemas, routing.externalEventSchemas);
8158
+ const originAware = EventDispatcherOriginSchemas.define(dispatcher, routing.domesticEventSchemas, routing.externalEventSchemas);
8159
+ return SavedDispatcherBindings.event(originAware, SavedRepositoryDispatch.eventBinding(repository, routing));
5973
8160
  },
5974
8161
  /**
5975
8162
  * Creates a system-Event dispatcher for registered Entity-state subscriptions.
@@ -6018,7 +8205,9 @@ const RepositoryDispatch = {
6018
8205
  * @returns The Inbox target and supported operation labels, when applicable.
6019
8206
  */
6020
8207
  createEntityInboxTarget(repository, routing) {
6021
- if ((repository.entityFamily !== "aggregate" && repository.entityFamily !== "process-manager") ||
8208
+ if ((repository.entityFamily !== "aggregate" &&
8209
+ repository.entityFamily !== "process-manager" &&
8210
+ repository.entityFamily !== "agent") ||
6022
8211
  (routing.commandSchemas.length === 0 && routing.eventSchemas.length === 0)) {
6023
8212
  return undefined;
6024
8213
  }
@@ -6026,7 +8215,9 @@ const RepositoryDispatch = {
6026
8215
  targetTypeUrl: TypeUrls.derive(repository.stateSchema),
6027
8216
  labels: Object.freeze([
6028
8217
  ...(routing.commandSchemas.length === 0 ? [] : ["HANDLE_COMMAND"]),
6029
- ...(repository.entityFamily === "process-manager" && routing.eventSchemas.length > 0
8218
+ ...((repository.entityFamily === "process-manager" ||
8219
+ repository.entityFamily === "agent") &&
8220
+ routing.eventSchemas.length > 0
6030
8221
  ? ["REACT_UPON_EVENT"]
6031
8222
  : []),
6032
8223
  ]),
@@ -6090,6 +8281,7 @@ const RepositoryDispatch = {
6090
8281
  await RepositoryDispatch.dispatchAggregateEvent(repository, routing, runtime, event, route);
6091
8282
  return;
6092
8283
  case "process-manager":
8284
+ case "agent":
6093
8285
  await new ProcessManagerEventExecution(repository, routing, runtime, event).run(route);
6094
8286
  return;
6095
8287
  case "projection":
@@ -6154,7 +8346,7 @@ const RepositoryDispatch = {
6154
8346
  await InboxHandoff.handoffEntityCommand(repository, runtime, command);
6155
8347
  return;
6156
8348
  }
6157
- if (repository.entityFamily === "process-manager") {
8349
+ if (repository.entityFamily === "process-manager" || repository.entityFamily === "agent") {
6158
8350
  await InboxHandoff.handoffEntityCommand(repository, runtime, command);
6159
8351
  return;
6160
8352
  }