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