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