@caupulican/pi-adaptative 0.86.3 → 0.86.6
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/CHANGELOG.md +44 -0
- package/dist/core/agent-paths.d.ts +13 -0
- package/dist/core/agent-paths.d.ts.map +1 -1
- package/dist/core/agent-paths.js +23 -0
- package/dist/core/agent-paths.js.map +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts +7 -0
- package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
- package/dist/core/autonomy/bounded-completion.js +12 -4
- package/dist/core/autonomy/bounded-completion.js.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.d.ts +2 -0
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.js +6 -3
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
- package/dist/core/autonomy/subagent-prompt.js +1 -1
- package/dist/core/autonomy/subagent-prompt.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +25 -2
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +67 -0
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/delegation/replay-safe-mailbox-bounds.d.ts +4 -0
- package/dist/core/delegation/replay-safe-mailbox-bounds.d.ts.map +1 -0
- package/dist/core/delegation/replay-safe-mailbox-bounds.js +20 -0
- package/dist/core/delegation/replay-safe-mailbox-bounds.js.map +1 -0
- package/dist/core/delegation/sanitized-context-fork.d.ts +25 -0
- package/dist/core/delegation/sanitized-context-fork.d.ts.map +1 -0
- package/dist/core/delegation/sanitized-context-fork.js +299 -0
- package/dist/core/delegation/sanitized-context-fork.js.map +1 -0
- package/dist/core/delegation/session-root-mailbox.d.ts +75 -0
- package/dist/core/delegation/session-root-mailbox.d.ts.map +1 -0
- package/dist/core/delegation/session-root-mailbox.js +627 -0
- package/dist/core/delegation/session-root-mailbox.js.map +1 -0
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts +102 -9
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.js +1307 -125
- package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-agent-control.d.ts +183 -8
- package/dist/core/delegation/worker-agent-control.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control.js +778 -48
- package/dist/core/delegation/worker-agent-control.js.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.d.ts +9 -0
- package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.js +479 -214
- package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.d.ts +9 -0
- package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.js +21 -2
- package/dist/core/delegation/worker-authority-resolver.js.map +1 -1
- package/dist/core/delegation/worker-context-fork-store.d.ts +71 -0
- package/dist/core/delegation/worker-context-fork-store.d.ts.map +1 -0
- package/dist/core/delegation/worker-context-fork-store.js +437 -0
- package/dist/core/delegation/worker-context-fork-store.js.map +1 -0
- package/dist/core/delegation/worker-context-inheritance-policy.d.ts +18 -0
- package/dist/core/delegation/worker-context-inheritance-policy.d.ts.map +1 -0
- package/dist/core/delegation/worker-context-inheritance-policy.js +28 -0
- package/dist/core/delegation/worker-context-inheritance-policy.js.map +1 -0
- package/dist/core/delegation/worker-conversation-revision.d.ts +31 -0
- package/dist/core/delegation/worker-conversation-revision.d.ts.map +1 -0
- package/dist/core/delegation/worker-conversation-revision.js +203 -0
- package/dist/core/delegation/worker-conversation-revision.js.map +1 -0
- package/dist/core/delegation/worker-conversation-store.d.ts +130 -18
- package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -1
- package/dist/core/delegation/worker-conversation-store.js +1263 -79
- package/dist/core/delegation/worker-conversation-store.js.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.d.ts +30 -2
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.js +675 -111
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
- package/dist/core/delegation/worker-delegation-request.d.ts +2 -0
- package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-request.js.map +1 -1
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts +20 -1
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -1
- package/dist/core/delegation/worker-dispatch-scheduler.js +210 -37
- package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -1
- package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
- package/dist/core/delegation/worker-execution-policy.js +14 -2
- package/dist/core/delegation/worker-execution-policy.js.map +1 -1
- package/dist/core/delegation/worker-fleet-limits.d.ts +38 -0
- package/dist/core/delegation/worker-fleet-limits.d.ts.map +1 -0
- package/dist/core/delegation/worker-fleet-limits.js +113 -0
- package/dist/core/delegation/worker-fleet-limits.js.map +1 -0
- package/dist/core/delegation/worker-lane-projection.d.ts +1 -0
- package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -1
- package/dist/core/delegation/worker-lane-projection.js +6 -2
- package/dist/core/delegation/worker-lane-projection.js.map +1 -1
- package/dist/core/delegation/worker-lifecycle.d.ts +20 -3
- package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
- package/dist/core/delegation/worker-lifecycle.js +42 -18
- package/dist/core/delegation/worker-lifecycle.js.map +1 -1
- package/dist/core/delegation/worker-notification-coordinator.d.ts +4 -0
- package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-notification-coordinator.js +45 -4
- package/dist/core/delegation/worker-notification-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-provider-turn-protocol.d.ts +50 -0
- package/dist/core/delegation/worker-provider-turn-protocol.d.ts.map +1 -0
- package/dist/core/delegation/worker-provider-turn-protocol.js +185 -0
- package/dist/core/delegation/worker-provider-turn-protocol.js.map +1 -0
- package/dist/core/delegation/worker-recovery-coordinator.d.ts +45 -4
- package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-recovery-coordinator.js +223 -17
- package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-retry-policy.d.ts +36 -0
- package/dist/core/delegation/worker-retry-policy.d.ts.map +1 -0
- package/dist/core/delegation/worker-retry-policy.js +41 -0
- package/dist/core/delegation/worker-retry-policy.js.map +1 -0
- package/dist/core/delegation/worker-runner.d.ts +3 -1
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +4 -2
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-task-view.d.ts +43 -0
- package/dist/core/delegation/worker-task-view.d.ts.map +1 -0
- package/dist/core/delegation/worker-task-view.js +194 -0
- package/dist/core/delegation/worker-task-view.js.map +1 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.d.ts +55 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.d.ts.map +1 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.js +179 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.js.map +1 -0
- package/dist/core/delegation/worker-tree-budget-coordinator.d.ts +2 -2
- package/dist/core/delegation/worker-tree-budget-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-tree-budget-coordinator.js +12 -46
- package/dist/core/delegation/worker-tree-budget-coordinator.js.map +1 -1
- package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-controller.js +15 -0
- package/dist/core/goals/goal-continuation-controller.js.map +1 -1
- package/dist/core/goals/goal-state.d.ts +6 -0
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +1 -0
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +1 -0
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +25 -3
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/keybindings.d.ts +10 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/orchestration/attempt-ordering.d.ts +9 -0
- package/dist/core/orchestration/attempt-ordering.d.ts.map +1 -0
- package/dist/core/orchestration/attempt-ordering.js +29 -0
- package/dist/core/orchestration/attempt-ordering.js.map +1 -0
- package/dist/core/orchestration/capability-gateway.d.ts +19 -0
- package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
- package/dist/core/orchestration/capability-gateway.js +19 -2
- package/dist/core/orchestration/capability-gateway.js.map +1 -1
- package/dist/core/orchestration/contracts.d.ts +43 -3
- package/dist/core/orchestration/contracts.d.ts.map +1 -1
- package/dist/core/orchestration/contracts.js +24 -0
- package/dist/core/orchestration/contracts.js.map +1 -1
- package/dist/core/orchestration/delegation-ledger.d.ts +8 -3
- package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
- package/dist/core/orchestration/delegation-ledger.js +148 -55
- package/dist/core/orchestration/delegation-ledger.js.map +1 -1
- package/dist/core/orchestration/event-store.d.ts +6 -3
- package/dist/core/orchestration/event-store.d.ts.map +1 -1
- package/dist/core/orchestration/event-store.js +228 -30
- package/dist/core/orchestration/event-store.js.map +1 -1
- package/dist/core/orchestration/task-runtime-codecs.d.ts +31 -0
- package/dist/core/orchestration/task-runtime-codecs.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-codecs.js +1471 -0
- package/dist/core/orchestration/task-runtime-codecs.js.map +1 -0
- package/dist/core/orchestration/task-runtime-projection.d.ts +30 -0
- package/dist/core/orchestration/task-runtime-projection.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-projection.js +340 -0
- package/dist/core/orchestration/task-runtime-projection.js.map +1 -0
- package/dist/core/orchestration/task-runtime-reducer.d.ts +38 -0
- package/dist/core/orchestration/task-runtime-reducer.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-reducer.js +1270 -0
- package/dist/core/orchestration/task-runtime-reducer.js.map +1 -0
- package/dist/core/orchestration/task-runtime-state.d.ts +142 -0
- package/dist/core/orchestration/task-runtime-state.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-state.js +14 -0
- package/dist/core/orchestration/task-runtime-state.js.map +1 -0
- package/dist/core/orchestration/task-runtime.d.ts +44 -98
- package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
- package/dist/core/orchestration/task-runtime.js +357 -1113
- package/dist/core/orchestration/task-runtime.js.map +1 -1
- package/dist/core/orchestration/worker-context-fork-reference.d.ts +20 -0
- package/dist/core/orchestration/worker-context-fork-reference.d.ts.map +1 -0
- package/dist/core/orchestration/worker-context-fork-reference.js +53 -0
- package/dist/core/orchestration/worker-context-fork-reference.js.map +1 -0
- package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
- package/dist/core/orchestration/worker-execution-contract.js +4 -2
- package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
- package/dist/core/research/research-runner.d.ts +2 -0
- package/dist/core/research/research-runner.d.ts.map +1 -1
- package/dist/core/research/research-runner.js +1 -0
- package/dist/core/research/research-runner.js.map +1 -1
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +8 -0
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/secrets/credential-project.d.ts.map +1 -1
- package/dist/core/secrets/credential-project.js +24 -2
- package/dist/core/secrets/credential-project.js.map +1 -1
- package/dist/core/security/secret-text.d.ts +3 -0
- package/dist/core/security/secret-text.d.ts.map +1 -1
- package/dist/core/security/secret-text.js +15 -0
- package/dist/core/security/secret-text.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +160 -8
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +33 -15
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +763 -115
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/goal.d.ts +1 -0
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +9 -1
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/activity-lane.d.ts +5 -0
- package/dist/modes/interactive/components/activity-lane.d.ts.map +1 -1
- package/dist/modes/interactive/components/activity-lane.js +133 -49
- package/dist/modes/interactive/components/activity-lane.js.map +1 -1
- package/dist/modes/interactive/components/agents-overlay.d.ts +40 -0
- package/dist/modes/interactive/components/agents-overlay.d.ts.map +1 -0
- package/dist/modes/interactive/components/agents-overlay.js +153 -0
- package/dist/modes/interactive/components/agents-overlay.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +18 -13
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-event-controller.d.ts +1 -1
- package/dist/modes/interactive/interactive-event-controller.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-event-controller.js +6 -3
- package/dist/modes/interactive/interactive-event-controller.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +51 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/key-handlers.d.ts +1 -0
- package/dist/modes/interactive/key-handlers.d.ts.map +1 -1
- package/dist/modes/interactive/key-handlers.js +1 -0
- package/dist/modes/interactive/key-handlers.js.map +1 -1
- package/docs/harness-architecture.md +61 -17
- package/docs/settings.md +17 -9
- package/docs/worker-profiles.md +6 -2
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { existsSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
5
|
import { orchestrationEventStoreDir } from "../agent-paths.js";
|
|
5
6
|
import { withFileLockSync, writeFileAtomicSync } from "../util/atomic-file.js";
|
|
6
7
|
import { readBoundedDirectoryNamesSync, readBoundedTextFileSync } from "../util/bounded-file.js";
|
|
7
|
-
import { isOrchestrationEvent, ORCHESTRATION_SCHEMA_VERSION, } from "./contracts.js";
|
|
8
|
+
import { isOrchestrationEvent, MAX_ORCHESTRATION_IDENTIFIER_LENGTH, MAX_ORCHESTRATION_PROJECTION_SNAPSHOT_BYTES, MAX_ORCHESTRATION_SNAPSHOT_IDEMPOTENCY_BYTES, ORCHESTRATION_EVENT_TYPES, ORCHESTRATION_SCHEMA_VERSION, toJsonObject, } from "./contracts.js";
|
|
8
9
|
const EVENT_FILE_PATTERN = /^(\d{16})\.json$/;
|
|
9
10
|
const SNAPSHOT_FILE_PATTERN = /^(\d{16})\.json$/;
|
|
10
11
|
const DEFAULT_MAX_TAIL_EVENTS = 256;
|
|
@@ -18,8 +19,39 @@ const MAX_IDEMPOTENCY_DIRECTORY_ENTRIES = 2_048;
|
|
|
18
19
|
const MAX_SNAPSHOT_DIRECTORY_ENTRIES = 8;
|
|
19
20
|
const MAX_CURSOR_BYTES = 64 * 1024;
|
|
20
21
|
const MAX_BASELINE_BYTES = 64 * 1024;
|
|
21
|
-
|
|
22
|
-
const
|
|
22
|
+
const ORCHESTRATION_ACTORS = ["human", "kernel", "runtime", "policy", "router", "worker"];
|
|
23
|
+
const APPEND_INPUT_FIELDS = [
|
|
24
|
+
"type",
|
|
25
|
+
"aggregateId",
|
|
26
|
+
"actor",
|
|
27
|
+
"correlationId",
|
|
28
|
+
"causationId",
|
|
29
|
+
"idempotencyKey",
|
|
30
|
+
"payload",
|
|
31
|
+
];
|
|
32
|
+
const EVENT_FIELDS = [
|
|
33
|
+
"schemaVersion",
|
|
34
|
+
"ordinal",
|
|
35
|
+
"eventId",
|
|
36
|
+
"type",
|
|
37
|
+
"aggregateId",
|
|
38
|
+
"actor",
|
|
39
|
+
"occurredAt",
|
|
40
|
+
"correlationId",
|
|
41
|
+
"causationId",
|
|
42
|
+
"idempotencyKey",
|
|
43
|
+
"payload",
|
|
44
|
+
];
|
|
45
|
+
const SNAPSHOT_FIELDS = [
|
|
46
|
+
"version",
|
|
47
|
+
"schemaVersion",
|
|
48
|
+
"throughOrdinal",
|
|
49
|
+
"createdAt",
|
|
50
|
+
"projection",
|
|
51
|
+
"idempotencyEvents",
|
|
52
|
+
"digest",
|
|
53
|
+
];
|
|
54
|
+
const BASELINE_FIELDS = ["version", "throughOrdinal", "digest", "snapshotFile"];
|
|
23
55
|
export class OrchestrationEventStoreError extends Error {
|
|
24
56
|
constructor(message) {
|
|
25
57
|
super(message);
|
|
@@ -73,6 +105,9 @@ function parseCursor(filePath) {
|
|
|
73
105
|
function serialize(value) {
|
|
74
106
|
return `${JSON.stringify(value, null, "\t")}\n`;
|
|
75
107
|
}
|
|
108
|
+
function serializeCompact(value) {
|
|
109
|
+
return `${JSON.stringify(value)}\n`;
|
|
110
|
+
}
|
|
76
111
|
function serializeBounded(value, maxBytes, label, oversizedMessage = `${label} exceeds its byte limit.`) {
|
|
77
112
|
const serialized = serialize(value);
|
|
78
113
|
if (Buffer.byteLength(serialized, "utf-8") > maxBytes) {
|
|
@@ -80,6 +115,150 @@ function serializeBounded(value, maxBytes, label, oversizedMessage = `${label} e
|
|
|
80
115
|
}
|
|
81
116
|
return serialized;
|
|
82
117
|
}
|
|
118
|
+
function serializeCompactBounded(value, maxBytes, label) {
|
|
119
|
+
const serialized = serializeCompact(value);
|
|
120
|
+
if (Buffer.byteLength(serialized, "utf8") > maxBytes) {
|
|
121
|
+
throw new OrchestrationEventStoreError(`${label} exceeds its byte limit.`);
|
|
122
|
+
}
|
|
123
|
+
return serialized;
|
|
124
|
+
}
|
|
125
|
+
function isCanonicalIdentifier(value) {
|
|
126
|
+
return (typeof value === "string" &&
|
|
127
|
+
value.length > 0 &&
|
|
128
|
+
value.length <= MAX_ORCHESTRATION_IDENTIFIER_LENGTH &&
|
|
129
|
+
value.trim() === value);
|
|
130
|
+
}
|
|
131
|
+
function canonicalIdentifier(value, label) {
|
|
132
|
+
if (!isCanonicalIdentifier(value)) {
|
|
133
|
+
throw new OrchestrationEventStoreError(`${label} must be a non-empty canonical identifier of at most ${MAX_ORCHESTRATION_IDENTIFIER_LENGTH} characters.`);
|
|
134
|
+
}
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
function isCanonicalIsoTimestamp(value) {
|
|
138
|
+
if (typeof value !== "string" || value.length === 0 || value.trim() !== value)
|
|
139
|
+
return false;
|
|
140
|
+
const milliseconds = Date.parse(value);
|
|
141
|
+
return Number.isFinite(milliseconds) && new Date(milliseconds).toISOString() === value;
|
|
142
|
+
}
|
|
143
|
+
function canonicalIsoTimestamp(value, label) {
|
|
144
|
+
if (!isCanonicalIsoTimestamp(value)) {
|
|
145
|
+
throw new OrchestrationEventStoreError(`${label} must be a canonical ISO-8601 timestamp.`);
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
function canonicalJsonObject(value, label) {
|
|
150
|
+
try {
|
|
151
|
+
return toJsonObject(value);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
throw new OrchestrationEventStoreError(`${label} must be a finite JSON object: ${error instanceof Error ? error.message : String(error)}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function hasExactFields(record, fields) {
|
|
158
|
+
const actualFields = Object.keys(record);
|
|
159
|
+
return actualFields.length === fields.length && fields.every((field) => Object.hasOwn(record, field));
|
|
160
|
+
}
|
|
161
|
+
function normalizeAppendInput(input) {
|
|
162
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
163
|
+
throw new OrchestrationEventStoreError("Orchestration append input must be an object.");
|
|
164
|
+
}
|
|
165
|
+
const raw = input;
|
|
166
|
+
const unsupportedField = Object.keys(raw).find((field) => !APPEND_INPUT_FIELDS.some((candidate) => candidate === field));
|
|
167
|
+
if (unsupportedField) {
|
|
168
|
+
throw new OrchestrationEventStoreError(`Orchestration append input field '${unsupportedField}' is unsupported.`);
|
|
169
|
+
}
|
|
170
|
+
const type = ORCHESTRATION_EVENT_TYPES.find((candidate) => candidate === raw.type);
|
|
171
|
+
if (!type) {
|
|
172
|
+
throw new OrchestrationEventStoreError(`Orchestration event type '${String(raw.type)}' is invalid.`);
|
|
173
|
+
}
|
|
174
|
+
const actor = ORCHESTRATION_ACTORS.find((candidate) => candidate === raw.actor);
|
|
175
|
+
if (!actor) {
|
|
176
|
+
throw new OrchestrationEventStoreError(`Orchestration event actor '${String(raw.actor)}' is invalid.`);
|
|
177
|
+
}
|
|
178
|
+
const correlationId = raw.correlationId === undefined
|
|
179
|
+
? undefined
|
|
180
|
+
: canonicalIdentifier(raw.correlationId, "Orchestration correlation id");
|
|
181
|
+
const causationId = raw.causationId === undefined ? undefined : canonicalIdentifier(raw.causationId, "Orchestration causation id");
|
|
182
|
+
const idempotencyKey = raw.idempotencyKey === undefined
|
|
183
|
+
? undefined
|
|
184
|
+
: canonicalIdentifier(raw.idempotencyKey, "Orchestration idempotency key");
|
|
185
|
+
return {
|
|
186
|
+
type,
|
|
187
|
+
aggregateId: canonicalIdentifier(raw.aggregateId, "Orchestration aggregate id"),
|
|
188
|
+
actor,
|
|
189
|
+
...(correlationId ? { correlationId } : {}),
|
|
190
|
+
...(causationId ? { causationId } : {}),
|
|
191
|
+
...(idempotencyKey ? { idempotencyKey } : {}),
|
|
192
|
+
payload: canonicalJsonObject(raw.payload, "Orchestration event payload"),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function isCanonicalOrchestrationEvent(value) {
|
|
196
|
+
if (!isOrchestrationEvent(value))
|
|
197
|
+
return false;
|
|
198
|
+
if (Object.keys(value).some((field) => !EVENT_FIELDS.some((candidate) => candidate === field)))
|
|
199
|
+
return false;
|
|
200
|
+
if (!isCanonicalIdentifier(value.eventId) || !isCanonicalIdentifier(value.aggregateId))
|
|
201
|
+
return false;
|
|
202
|
+
if (!isCanonicalIsoTimestamp(value.occurredAt))
|
|
203
|
+
return false;
|
|
204
|
+
if (value.correlationId !== undefined && !isCanonicalIdentifier(value.correlationId))
|
|
205
|
+
return false;
|
|
206
|
+
if (value.causationId !== undefined && !isCanonicalIdentifier(value.causationId))
|
|
207
|
+
return false;
|
|
208
|
+
if (value.idempotencyKey !== undefined && !isCanonicalIdentifier(value.idempotencyKey))
|
|
209
|
+
return false;
|
|
210
|
+
try {
|
|
211
|
+
toJsonObject(value.payload);
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function assertCanonicalOrchestrationEvent(value) {
|
|
219
|
+
if (!isCanonicalOrchestrationEvent(value)) {
|
|
220
|
+
throw new OrchestrationEventStoreError("Constructed orchestration event is not canonical.");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function observeListenerResult(value) {
|
|
224
|
+
if ((typeof value !== "object" || value === null) && typeof value !== "function")
|
|
225
|
+
return;
|
|
226
|
+
let then;
|
|
227
|
+
try {
|
|
228
|
+
then = value.then;
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (typeof then === "function")
|
|
234
|
+
void Promise.resolve(value).catch(() => { });
|
|
235
|
+
}
|
|
236
|
+
function boundedIdempotencyEvents(events, maxEvents) {
|
|
237
|
+
const selectedNewestFirst = [];
|
|
238
|
+
let serializedBytes = 2;
|
|
239
|
+
for (const event of [...events].sort((left, right) => right.ordinal - left.ordinal)) {
|
|
240
|
+
if (selectedNewestFirst.length >= maxEvents)
|
|
241
|
+
break;
|
|
242
|
+
const eventBytes = Buffer.byteLength(JSON.stringify(event), "utf8");
|
|
243
|
+
const nextBytes = serializedBytes + (selectedNewestFirst.length > 0 ? 1 : 0) + eventBytes;
|
|
244
|
+
if (nextBytes > MAX_ORCHESTRATION_SNAPSHOT_IDEMPOTENCY_BYTES)
|
|
245
|
+
continue;
|
|
246
|
+
selectedNewestFirst.push(structuredClone(event));
|
|
247
|
+
serializedBytes = nextBytes;
|
|
248
|
+
}
|
|
249
|
+
return selectedNewestFirst.reverse();
|
|
250
|
+
}
|
|
251
|
+
function assertExactIdempotentReplay(existing, input) {
|
|
252
|
+
const matches = existing.type === input.type &&
|
|
253
|
+
existing.aggregateId === input.aggregateId &&
|
|
254
|
+
existing.actor === input.actor &&
|
|
255
|
+
existing.correlationId === (input.correlationId || undefined) &&
|
|
256
|
+
existing.causationId === (input.causationId || undefined) &&
|
|
257
|
+
isDeepStrictEqual(existing.payload, input.payload);
|
|
258
|
+
if (!matches) {
|
|
259
|
+
throw new OrchestrationEventStoreError(`Orchestration idempotency key '${input.idempotencyKey}' was reused with conflicting event content.`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
83
262
|
function eventFileName(ordinal) {
|
|
84
263
|
return `${String(ordinal).padStart(16, "0")}.json`;
|
|
85
264
|
}
|
|
@@ -120,13 +299,20 @@ export class OrchestrationEventStore {
|
|
|
120
299
|
this.maxIdempotencyEvents = positiveSafeInteger(options.maxIdempotencyEvents, DEFAULT_MAX_IDEMPOTENCY_EVENTS, MAX_CONFIGURED_IDEMPOTENCY_EVENTS, "maxIdempotencyEvents");
|
|
121
300
|
}
|
|
122
301
|
append(input, options = {}) {
|
|
302
|
+
const normalizedInput = normalizeAppendInput(input);
|
|
303
|
+
if (options.expectedLastOrdinal !== undefined &&
|
|
304
|
+
(!Number.isSafeInteger(options.expectedLastOrdinal) || options.expectedLastOrdinal < 0)) {
|
|
305
|
+
throw new OrchestrationEventStoreError("Expected orchestration ordinal must be a non-negative safe integer.");
|
|
306
|
+
}
|
|
123
307
|
const committed = withFileLockSync(this.cursorPath, () => {
|
|
124
308
|
const indexes = this.synchronizeIndexesUnlocked();
|
|
125
309
|
const actual = indexes.lastOrdinal;
|
|
126
|
-
if (
|
|
127
|
-
const existing = indexes.idempotencyEvents.get(
|
|
128
|
-
if (existing)
|
|
310
|
+
if (normalizedInput.idempotencyKey) {
|
|
311
|
+
const existing = indexes.idempotencyEvents.get(normalizedInput.idempotencyKey);
|
|
312
|
+
if (existing) {
|
|
313
|
+
assertExactIdempotentReplay(existing, normalizedInput);
|
|
129
314
|
return { event: existing, appended: false };
|
|
315
|
+
}
|
|
130
316
|
}
|
|
131
317
|
if (options.expectedLastOrdinal !== undefined) {
|
|
132
318
|
if (actual !== options.expectedLastOrdinal) {
|
|
@@ -138,23 +324,25 @@ export class OrchestrationEventStore {
|
|
|
138
324
|
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
139
325
|
ordinal,
|
|
140
326
|
eventId: this.createEventId(),
|
|
141
|
-
type:
|
|
142
|
-
aggregateId:
|
|
143
|
-
actor:
|
|
327
|
+
type: normalizedInput.type,
|
|
328
|
+
aggregateId: normalizedInput.aggregateId,
|
|
329
|
+
actor: normalizedInput.actor,
|
|
144
330
|
occurredAt: this.now(),
|
|
145
|
-
...(
|
|
146
|
-
...(
|
|
147
|
-
...(
|
|
148
|
-
payload:
|
|
331
|
+
...(normalizedInput.correlationId ? { correlationId: normalizedInput.correlationId } : {}),
|
|
332
|
+
...(normalizedInput.causationId ? { causationId: normalizedInput.causationId } : {}),
|
|
333
|
+
...(normalizedInput.idempotencyKey ? { idempotencyKey: normalizedInput.idempotencyKey } : {}),
|
|
334
|
+
payload: normalizedInput.payload,
|
|
149
335
|
};
|
|
336
|
+
assertCanonicalOrchestrationEvent(next);
|
|
150
337
|
const serializedEvent = serializeBounded(next, this.maxTailBytes, "Orchestration event", "Orchestration event exceeds its configured tail byte limit.");
|
|
338
|
+
options.validateBeforeCommit?.(next);
|
|
151
339
|
const nextTailBytes = indexes.tailBytes + Buffer.byteLength(serializedEvent);
|
|
152
340
|
const serializedCursor = serializeBounded({ version: 1, lastOrdinal: ordinal, tailBytes: nextTailBytes }, MAX_CURSOR_BYTES, "Orchestration cursor");
|
|
153
341
|
writeFileAtomicSync(join(this.eventsDir, eventFileName(ordinal)), serializedEvent);
|
|
154
342
|
indexes.lastOrdinal = ordinal;
|
|
155
343
|
indexes.tailBytes = nextTailBytes;
|
|
156
|
-
if (
|
|
157
|
-
indexes.idempotencyEvents.set(
|
|
344
|
+
if (normalizedInput.idempotencyKey)
|
|
345
|
+
indexes.idempotencyEvents.set(normalizedInput.idempotencyKey, next);
|
|
158
346
|
// The immutable event is authoritative. The cursor is only a corruption high-water mark,
|
|
159
347
|
// so a lock-protected direct overwrite avoids another tmp-file creation and rename on the
|
|
160
348
|
// Windows scanner-sensitive path; a torn cursor is safely rebuilt from the event tail.
|
|
@@ -162,8 +350,14 @@ export class OrchestrationEventStore {
|
|
|
162
350
|
return { event: next, appended: true };
|
|
163
351
|
});
|
|
164
352
|
if (committed.appended) {
|
|
165
|
-
for (const listener of this.listeners)
|
|
166
|
-
|
|
353
|
+
for (const listener of [...this.listeners]) {
|
|
354
|
+
try {
|
|
355
|
+
observeListenerResult(listener(structuredClone(committed.event)));
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
// The event is already durable. Observer failures cannot turn a committed transition into a reported failure.
|
|
359
|
+
}
|
|
360
|
+
}
|
|
167
361
|
}
|
|
168
362
|
return structuredClone(committed.event);
|
|
169
363
|
}
|
|
@@ -240,21 +434,19 @@ export class OrchestrationEventStore {
|
|
|
240
434
|
retainedByKey.set(event.idempotencyKey, event);
|
|
241
435
|
}
|
|
242
436
|
}
|
|
243
|
-
const idempotencyEvents = [...retainedByKey.values()]
|
|
244
|
-
|
|
245
|
-
.slice(-this.maxIdempotencyEvents)
|
|
246
|
-
.map((event) => structuredClone(event));
|
|
437
|
+
const idempotencyEvents = boundedIdempotencyEvents([...retainedByKey.values()], this.maxIdempotencyEvents);
|
|
438
|
+
const canonicalProjection = canonicalJsonObject(projection(), "Orchestration projection snapshot");
|
|
247
439
|
const content = {
|
|
248
440
|
version: 1,
|
|
249
441
|
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
250
442
|
throughOrdinal,
|
|
251
|
-
createdAt: this.now(),
|
|
252
|
-
projection:
|
|
443
|
+
createdAt: canonicalIsoTimestamp(this.now(), "Orchestration projection snapshot creation time"),
|
|
444
|
+
projection: canonicalProjection,
|
|
253
445
|
idempotencyEvents,
|
|
254
446
|
};
|
|
255
447
|
const digest = snapshotDigest(content);
|
|
256
448
|
const snapshotFile = eventFileName(throughOrdinal);
|
|
257
|
-
const serializedSnapshot =
|
|
449
|
+
const serializedSnapshot = serializeCompactBounded({ ...content, digest }, MAX_ORCHESTRATION_PROJECTION_SNAPSHOT_BYTES, "Orchestration projection snapshot");
|
|
258
450
|
const serializedBaseline = serializeBounded({ version: 1, throughOrdinal, digest, snapshotFile }, MAX_BASELINE_BYTES, "Orchestration snapshot baseline");
|
|
259
451
|
const serializedCursor = serializeBounded({ version: 1, lastOrdinal: throughOrdinal, tailBytes: 0 }, MAX_CURSOR_BYTES, "Orchestration cursor");
|
|
260
452
|
writeFileAtomicSync(join(this.snapshotsDir, snapshotFile), serializedSnapshot);
|
|
@@ -381,7 +573,8 @@ export class OrchestrationEventStore {
|
|
|
381
573
|
throw new OrchestrationEventStoreError("Invalid orchestration snapshot baseline.");
|
|
382
574
|
}
|
|
383
575
|
const baseline = parsed;
|
|
384
|
-
if (baseline
|
|
576
|
+
if (!hasExactFields(baseline, BASELINE_FIELDS) ||
|
|
577
|
+
baseline.version !== 1 ||
|
|
385
578
|
!Number.isSafeInteger(baseline.throughOrdinal) ||
|
|
386
579
|
Number(baseline.throughOrdinal) < 1 ||
|
|
387
580
|
typeof baseline.digest !== "string" ||
|
|
@@ -404,7 +597,7 @@ export class OrchestrationEventStore {
|
|
|
404
597
|
return undefined;
|
|
405
598
|
let parsed;
|
|
406
599
|
try {
|
|
407
|
-
parsed = JSON.parse(readBoundedTextFileSync(join(this.snapshotsDir, baseline.snapshotFile),
|
|
600
|
+
parsed = JSON.parse(readBoundedTextFileSync(join(this.snapshotsDir, baseline.snapshotFile), MAX_ORCHESTRATION_PROJECTION_SNAPSHOT_BYTES, "Orchestration projection snapshot"));
|
|
408
601
|
}
|
|
409
602
|
catch (error) {
|
|
410
603
|
throw new OrchestrationEventStoreError(`Failed to parse orchestration projection snapshot: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -413,7 +606,8 @@ export class OrchestrationEventStore {
|
|
|
413
606
|
throw new OrchestrationEventStoreError("Invalid orchestration projection snapshot.");
|
|
414
607
|
}
|
|
415
608
|
const snapshot = parsed;
|
|
416
|
-
if (snapshot
|
|
609
|
+
if (!hasExactFields(snapshot, SNAPSHOT_FIELDS) ||
|
|
610
|
+
snapshot.version !== 1 ||
|
|
417
611
|
snapshot.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION ||
|
|
418
612
|
snapshot.throughOrdinal !== baseline.throughOrdinal ||
|
|
419
613
|
typeof snapshot.createdAt !== "string" ||
|
|
@@ -422,7 +616,10 @@ export class OrchestrationEventStore {
|
|
|
422
616
|
Array.isArray(snapshot.projection) ||
|
|
423
617
|
!Array.isArray(snapshot.idempotencyEvents) ||
|
|
424
618
|
snapshot.idempotencyEvents.length > this.maxIdempotencyEvents ||
|
|
425
|
-
|
|
619
|
+
Buffer.byteLength(JSON.stringify(snapshot.idempotencyEvents), "utf8") >
|
|
620
|
+
MAX_ORCHESTRATION_SNAPSHOT_IDEMPOTENCY_BYTES ||
|
|
621
|
+
!isCanonicalIsoTimestamp(snapshot.createdAt) ||
|
|
622
|
+
!snapshot.idempotencyEvents.every(isCanonicalOrchestrationEvent) ||
|
|
426
623
|
!snapshot.idempotencyEvents.every((event) => event.ordinal <= baseline.throughOrdinal && event.idempotencyKey !== undefined) ||
|
|
427
624
|
new Set(snapshot.idempotencyEvents.map((event) => event.idempotencyKey)).size !==
|
|
428
625
|
snapshot.idempotencyEvents.length ||
|
|
@@ -430,12 +627,13 @@ export class OrchestrationEventStore {
|
|
|
430
627
|
snapshot.digest !== baseline.digest) {
|
|
431
628
|
throw new OrchestrationEventStoreError("Invalid orchestration projection snapshot.");
|
|
432
629
|
}
|
|
630
|
+
const projection = canonicalJsonObject(snapshot.projection, "Orchestration projection snapshot state");
|
|
433
631
|
const content = {
|
|
434
632
|
version: 1,
|
|
435
633
|
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
436
634
|
throughOrdinal: baseline.throughOrdinal,
|
|
437
635
|
createdAt: snapshot.createdAt,
|
|
438
|
-
projection
|
|
636
|
+
projection,
|
|
439
637
|
idempotencyEvents: snapshot.idempotencyEvents.map((event) => structuredClone(event)),
|
|
440
638
|
};
|
|
441
639
|
if (snapshotDigest(content) !== baseline.digest) {
|
|
@@ -512,7 +710,7 @@ export class OrchestrationEventStore {
|
|
|
512
710
|
catch (error) {
|
|
513
711
|
throw new OrchestrationEventStoreError(`Failed to parse orchestration event ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
514
712
|
}
|
|
515
|
-
if (!
|
|
713
|
+
if (!isCanonicalOrchestrationEvent(parsed)) {
|
|
516
714
|
throw new OrchestrationEventStoreError(`Invalid orchestration event record: ${name}`);
|
|
517
715
|
}
|
|
518
716
|
return parsed;
|