@exaudeus/workrail 3.15.0 → 3.16.0
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/dist/application/services/workflow-service.d.ts +2 -0
- package/dist/application/services/workflow-service.js +3 -0
- package/dist/console/assets/index-BE5PAgPO.js +28 -0
- package/dist/console/assets/index-BZNM03t1.css +1 -0
- package/dist/console/index.html +2 -2
- package/dist/env-flags.d.ts +1 -0
- package/dist/env-flags.js +4 -0
- package/dist/infrastructure/session/HttpServer.d.ts +3 -3
- package/dist/infrastructure/session/HttpServer.js +68 -74
- package/dist/infrastructure/storage/caching-workflow-storage.d.ts +2 -0
- package/dist/infrastructure/storage/caching-workflow-storage.js +15 -6
- package/dist/infrastructure/storage/file-workflow-storage.js +3 -4
- package/dist/infrastructure/storage/schema-validating-workflow-storage.js +9 -8
- package/dist/manifest.json +257 -193
- package/dist/mcp/assert-output.d.ts +37 -0
- package/dist/mcp/assert-output.js +52 -0
- package/dist/mcp/boundary-coercion.d.ts +1 -0
- package/dist/mcp/boundary-coercion.js +44 -0
- package/dist/mcp/dev-mode.d.ts +1 -0
- package/dist/mcp/dev-mode.js +4 -0
- package/dist/mcp/handler-factory.js +12 -9
- package/dist/mcp/handlers/session.js +8 -9
- package/dist/mcp/handlers/v2-advance-core/event-builders.d.ts +2 -0
- package/dist/mcp/handlers/v2-advance-core/event-builders.js +6 -6
- package/dist/mcp/handlers/v2-advance-core/index.d.ts +2 -0
- package/dist/mcp/handlers/v2-advance-core/index.js +4 -3
- package/dist/mcp/handlers/v2-advance-core/input-validation.d.ts +2 -0
- package/dist/mcp/handlers/v2-advance-core/input-validation.js +32 -9
- package/dist/mcp/handlers/v2-advance-core/outcome-blocked.d.ts +2 -0
- package/dist/mcp/handlers/v2-advance-core/outcome-blocked.js +1 -1
- package/dist/mcp/handlers/v2-advance-core/outcome-success.d.ts +2 -0
- package/dist/mcp/handlers/v2-advance-core/outcome-success.js +1 -1
- package/dist/mcp/handlers/v2-checkpoint.d.ts +1 -1
- package/dist/mcp/handlers/v2-checkpoint.js +5 -6
- package/dist/mcp/handlers/v2-execution/advance.d.ts +4 -2
- package/dist/mcp/handlers/v2-execution/advance.js +5 -7
- package/dist/mcp/handlers/v2-execution/continue-advance.js +56 -26
- package/dist/mcp/handlers/v2-execution/continue-rehydrate.d.ts +1 -1
- package/dist/mcp/handlers/v2-execution/continue-rehydrate.js +9 -9
- package/dist/mcp/handlers/v2-execution/replay.d.ts +6 -4
- package/dist/mcp/handlers/v2-execution/replay.js +47 -30
- package/dist/mcp/handlers/v2-execution/start.d.ts +2 -3
- package/dist/mcp/handlers/v2-execution/start.js +11 -11
- package/dist/mcp/handlers/v2-execution/workflow-object-cache.d.ts +5 -0
- package/dist/mcp/handlers/v2-execution/workflow-object-cache.js +19 -0
- package/dist/mcp/handlers/v2-execution-helpers.d.ts +1 -0
- package/dist/mcp/handlers/v2-execution-helpers.js +23 -7
- package/dist/mcp/handlers/v2-resume.d.ts +1 -1
- package/dist/mcp/handlers/v2-resume.js +3 -4
- package/dist/mcp/handlers/v2-state-conversion.js +5 -1
- package/dist/mcp/handlers/v2-workflow.d.ts +80 -0
- package/dist/mcp/handlers/v2-workflow.js +36 -21
- package/dist/mcp/handlers/workflow.d.ts +2 -5
- package/dist/mcp/handlers/workflow.js +15 -12
- package/dist/mcp/output-schemas.d.ts +20 -27
- package/dist/mcp/output-schemas.js +5 -7
- package/dist/mcp/server.js +22 -4
- package/dist/mcp/tool-call-timing.d.ts +24 -0
- package/dist/mcp/tool-call-timing.js +85 -0
- package/dist/mcp/transports/http-entry.js +3 -2
- package/dist/mcp/transports/http-listener.d.ts +1 -0
- package/dist/mcp/transports/http-listener.js +25 -0
- package/dist/mcp/transports/shutdown-hooks.d.ts +4 -1
- package/dist/mcp/transports/shutdown-hooks.js +3 -2
- package/dist/mcp/transports/stdio-entry.js +6 -28
- package/dist/mcp/v2-response-formatter.js +2 -4
- package/dist/mcp/validation/schema-introspection.d.ts +1 -0
- package/dist/mcp/validation/schema-introspection.js +15 -5
- package/dist/mcp/validation/suggestion-generator.js +2 -2
- package/dist/runtime/adapters/node-process-signals.d.ts +1 -0
- package/dist/runtime/adapters/node-process-signals.js +5 -0
- package/dist/runtime/adapters/noop-process-signals.d.ts +1 -0
- package/dist/runtime/adapters/noop-process-signals.js +2 -0
- package/dist/runtime/ports/process-signals.d.ts +1 -0
- package/dist/types/workflow-definition.d.ts +2 -0
- package/dist/types/workflow.d.ts +3 -0
- package/dist/types/workflow.js +35 -26
- package/dist/v2/durable-core/domain/context-template-resolver.js +2 -2
- package/dist/v2/durable-core/domain/function-definition-expander.js +2 -17
- package/dist/v2/durable-core/domain/prompt-renderer.d.ts +1 -0
- package/dist/v2/durable-core/domain/prompt-renderer.js +23 -18
- package/dist/v2/durable-core/domain/recap-recovery.js +23 -16
- package/dist/v2/durable-core/domain/retrieval-contract.js +13 -7
- package/dist/v2/durable-core/session-index.d.ts +22 -0
- package/dist/v2/durable-core/session-index.js +58 -0
- package/dist/v2/durable-core/sorted-event-log.d.ts +6 -0
- package/dist/v2/durable-core/sorted-event-log.js +15 -0
- package/dist/v2/infra/local/fs/index.js +8 -8
- package/dist/v2/infra/local/session-store/index.d.ts +1 -1
- package/dist/v2/infra/local/session-store/index.js +71 -61
- package/dist/v2/infra/local/session-summary-provider/index.js +9 -4
- package/dist/v2/infra/local/snapshot-store/index.js +2 -1
- package/dist/v2/ports/session-event-log-store.port.d.ts +1 -1
- package/dist/v2/projections/assessment-consequences.d.ts +2 -1
- package/dist/v2/projections/assessment-consequences.js +0 -5
- package/dist/v2/projections/assessments.d.ts +2 -1
- package/dist/v2/projections/assessments.js +2 -4
- package/dist/v2/projections/gaps.d.ts +2 -1
- package/dist/v2/projections/gaps.js +0 -5
- package/dist/v2/projections/preferences.d.ts +2 -1
- package/dist/v2/projections/preferences.js +0 -5
- package/dist/v2/projections/run-context.d.ts +2 -2
- package/dist/v2/projections/run-context.js +0 -5
- package/dist/v2/projections/run-dag.js +7 -1
- package/dist/v2/projections/run-execution-trace.d.ts +8 -0
- package/dist/v2/projections/run-execution-trace.js +124 -0
- package/dist/v2/projections/run-status-signals.d.ts +2 -2
- package/dist/v2/usecases/console-routes.d.ts +3 -1
- package/dist/v2/usecases/console-routes.js +123 -25
- package/dist/v2/usecases/console-service.d.ts +1 -0
- package/dist/v2/usecases/console-service.js +83 -25
- package/dist/v2/usecases/console-types.d.ts +53 -0
- package/dist/v2/usecases/worktree-service.js +32 -1
- package/package.json +6 -5
- package/spec/workflow.schema.json +18 -0
- package/workflows/adaptive-ticket-creation.json +23 -16
- package/workflows/architecture-scalability-audit.json +29 -22
- package/workflows/bug-investigation.agentic.v2.json +7 -0
- package/workflows/coding-task-workflow-agentic.json +7 -0
- package/workflows/coding-task-workflow-agentic.lean.v2.json +16 -8
- package/workflows/coding-task-workflow-agentic.v2.json +7 -0
- package/workflows/cross-platform-code-conversion.v2.json +7 -0
- package/workflows/document-creation-workflow.json +15 -8
- package/workflows/documentation-update-workflow.json +15 -8
- package/workflows/intelligent-test-case-generation.json +7 -0
- package/workflows/learner-centered-course-workflow.json +9 -2
- package/workflows/mr-review-workflow.agentic.v2.json +7 -0
- package/workflows/personal-learning-materials-creation-branched.json +15 -8
- package/workflows/presentation-creation.json +12 -5
- package/workflows/production-readiness-audit.json +7 -0
- package/workflows/relocation-workflow-us.json +39 -32
- package/workflows/scoped-documentation-workflow.json +33 -26
- package/workflows/ui-ux-design-workflow.json +7 -0
- package/workflows/workflow-diagnose-environment.json +6 -0
- package/workflows/workflow-for-workflows.json +7 -0
- package/workflows/workflow-for-workflows.v2.json +23 -11
- package/workflows/wr.discovery.json +8 -1
- package/dist/console/assets/index-BZYIjrzJ.js +0 -28
- package/dist/console/assets/index-OLCKbDdm.css +0 -1
|
@@ -6,18 +6,21 @@ exports.buildChildSummary = buildChildSummary;
|
|
|
6
6
|
const neverthrow_1 = require("neverthrow");
|
|
7
7
|
const constants_js_1 = require("../constants.js");
|
|
8
8
|
function collectAncestryRecap(args) {
|
|
9
|
-
const buildChain = (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
const buildChain = (start) => {
|
|
10
|
+
const result = [];
|
|
11
|
+
const visited = new Set();
|
|
12
|
+
let cur = start;
|
|
13
|
+
while (cur !== null && !visited.has(cur)) {
|
|
14
|
+
visited.add(cur);
|
|
15
|
+
result.push(cur);
|
|
16
|
+
cur = args.dag.nodesById[cur]?.parentNodeId ?? null;
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
16
19
|
};
|
|
17
20
|
const startNode = args.includeCurrentNode
|
|
18
21
|
? String(args.nodeId)
|
|
19
22
|
: args.dag.nodesById[String(args.nodeId)]?.parentNodeId ?? null;
|
|
20
|
-
const chain = buildChain(startNode
|
|
23
|
+
const chain = buildChain(startNode);
|
|
21
24
|
const recaps = chain.flatMap((nodeId) => {
|
|
22
25
|
const nodeOutputs = args.outputs.nodesById[nodeId];
|
|
23
26
|
if (!nodeOutputs)
|
|
@@ -35,15 +38,19 @@ function collectAncestryRecap(args) {
|
|
|
35
38
|
return (0, neverthrow_1.ok)([...recaps].reverse());
|
|
36
39
|
}
|
|
37
40
|
function collectDownstreamRecap(args) {
|
|
38
|
-
const buildPathBackward = (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
const buildPathBackward = (start) => {
|
|
42
|
+
const result = [];
|
|
43
|
+
const visited = new Set();
|
|
44
|
+
const fromId = String(args.fromNodeId);
|
|
45
|
+
let cur = start;
|
|
46
|
+
while (cur !== null && cur !== fromId && !visited.has(cur)) {
|
|
47
|
+
visited.add(cur);
|
|
48
|
+
result.push(cur);
|
|
49
|
+
cur = args.dag.nodesById[cur]?.parentNodeId ?? null;
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
45
52
|
};
|
|
46
|
-
const pathBackward = buildPathBackward(String(args.toNodeId)
|
|
53
|
+
const pathBackward = buildPathBackward(String(args.toNodeId));
|
|
47
54
|
const recaps = [...pathBackward].reverse().flatMap((nodeId) => {
|
|
48
55
|
const nodeOutputs = args.outputs.nodesById[nodeId];
|
|
49
56
|
if (!nodeOutputs)
|
|
@@ -63,17 +63,21 @@ exports.RESUME_PREVIEW_CONTRACT = {
|
|
|
63
63
|
};
|
|
64
64
|
const encoder = new TextEncoder();
|
|
65
65
|
const decoder = new TextDecoder('utf-8');
|
|
66
|
+
const TIER_PRIORITY = Object.fromEntries(exports.REHYDRATE_RETRIEVAL_CONTRACT.tiers.map((t) => [t.tier, t.priority]));
|
|
67
|
+
const TIER_RETENTION = Object.fromEntries(exports.REHYDRATE_RETRIEVAL_CONTRACT.tiers.map((t) => [t.tier, t.retention]));
|
|
68
|
+
const RESUME_PREVIEW_TIER_PRIORITY = Object.fromEntries(exports.RESUME_PREVIEW_CONTRACT.tiers.map((t) => [t.tier, t.priority]));
|
|
69
|
+
const RESUME_PREVIEW_TIER_MAX_BYTES = Object.fromEntries(exports.RESUME_PREVIEW_CONTRACT.tiers.map((t) => [t.tier, t.maxBytes]));
|
|
66
70
|
function getTierPriority(tier) {
|
|
67
|
-
return
|
|
71
|
+
return TIER_PRIORITY[tier] ?? Number.MAX_SAFE_INTEGER;
|
|
68
72
|
}
|
|
69
73
|
function getTierRetention(tier) {
|
|
70
|
-
return
|
|
74
|
+
return TIER_RETENTION[tier] ?? 'tail';
|
|
71
75
|
}
|
|
72
76
|
function getResumePreviewTierPriority(tier) {
|
|
73
|
-
return
|
|
77
|
+
return RESUME_PREVIEW_TIER_PRIORITY[tier] ?? Number.MAX_SAFE_INTEGER;
|
|
74
78
|
}
|
|
75
79
|
function getResumePreviewTierMaxBytes(tier) {
|
|
76
|
-
return
|
|
80
|
+
return RESUME_PREVIEW_TIER_MAX_BYTES[tier] ?? exports.RESUME_PREVIEW_CONTRACT.budgetBytes;
|
|
77
81
|
}
|
|
78
82
|
function compareAscii(a, b) {
|
|
79
83
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
@@ -283,7 +287,8 @@ function renderBudgetedRehydrateRecovery(args) {
|
|
|
283
287
|
const initiallyIncludedTiers = tiersInOrder.filter((tier) => (sectionsByTier.get(tier) ?? []).length > 0);
|
|
284
288
|
let includedTiers = initiallyIncludedTiers;
|
|
285
289
|
let recoveryText = renderFromTiers(includedTiers);
|
|
286
|
-
|
|
290
|
+
let recoveryBytes = encoder.encode(recoveryText).length;
|
|
291
|
+
while (recoveryBytes > constants_js_1.RECOVERY_BUDGET_BYTES) {
|
|
287
292
|
const droppableTierIndex = [...includedTiers]
|
|
288
293
|
.reverse()
|
|
289
294
|
.findIndex((tier) => getTierRetention(tier) === 'tail');
|
|
@@ -293,9 +298,10 @@ function renderBudgetedRehydrateRecovery(args) {
|
|
|
293
298
|
const actualIndex = includedTiers.length - 1 - droppableTierIndex;
|
|
294
299
|
includedTiers = includedTiers.filter((_, index) => index !== actualIndex);
|
|
295
300
|
recoveryText = renderFromTiers(includedTiers);
|
|
301
|
+
recoveryBytes = encoder.encode(recoveryText).length;
|
|
296
302
|
}
|
|
297
303
|
const omittedTierCount = initiallyIncludedTiers.length - includedTiers.length;
|
|
298
|
-
const needsSuffix = omittedTierCount > 0 ||
|
|
304
|
+
const needsSuffix = omittedTierCount > 0 || recoveryBytes > constants_js_1.RECOVERY_BUDGET_BYTES || includedTiers.length === 0;
|
|
299
305
|
const finalText = recoveryText.length === 0
|
|
300
306
|
? trimFinalRecoveryText(args.header, initiallyIncludedTiers.length)
|
|
301
307
|
: !needsSuffix
|
|
@@ -305,6 +311,6 @@ function renderBudgetedRehydrateRecovery(args) {
|
|
|
305
311
|
text: finalText,
|
|
306
312
|
includedTiers,
|
|
307
313
|
omittedTierCount,
|
|
308
|
-
truncatedWithinTier:
|
|
314
|
+
truncatedWithinTier: recoveryBytes > constants_js_1.RECOVERY_BUDGET_BYTES || includedTiers.length === 0,
|
|
309
315
|
};
|
|
310
316
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Brand } from '../../runtime/brand.js';
|
|
2
|
+
import type { SortedEventLog } from './sorted-event-log.js';
|
|
3
|
+
import type { DomainEventV1 } from './schemas/session/index.js';
|
|
4
|
+
import type { JsonObject } from './canonical/json-types.js';
|
|
5
|
+
export interface SessionIndexData {
|
|
6
|
+
readonly sortedEvents: SortedEventLog;
|
|
7
|
+
readonly runStartedByRunId: ReadonlyMap<string, Extract<DomainEventV1, {
|
|
8
|
+
kind: 'run_started';
|
|
9
|
+
}>>;
|
|
10
|
+
readonly nodeCreatedByNodeId: ReadonlyMap<string, Extract<DomainEventV1, {
|
|
11
|
+
kind: 'node_created';
|
|
12
|
+
}>>;
|
|
13
|
+
readonly hasChildEdgeByFromNodeId: ReadonlySet<string>;
|
|
14
|
+
readonly advanceRecordedByDedupeKey: ReadonlyMap<string, Extract<DomainEventV1, {
|
|
15
|
+
kind: 'advance_recorded';
|
|
16
|
+
}>>;
|
|
17
|
+
readonly nextEventIndex: number;
|
|
18
|
+
readonly hasPriorNotesByRunId: ReadonlySet<string>;
|
|
19
|
+
readonly runContextByRunId: ReadonlyMap<string, JsonObject>;
|
|
20
|
+
}
|
|
21
|
+
export type SessionIndex = Brand<SessionIndexData, 'v2.SessionIndex'>;
|
|
22
|
+
export declare function buildSessionIndex(events: SortedEventLog): SessionIndex;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildSessionIndex = buildSessionIndex;
|
|
4
|
+
const constants_js_1 = require("./constants.js");
|
|
5
|
+
function buildSessionIndex(events) {
|
|
6
|
+
const runStartedByRunId = new Map();
|
|
7
|
+
const nodeCreatedByNodeId = new Map();
|
|
8
|
+
const hasChildEdgeByFromNodeId = new Set();
|
|
9
|
+
const advanceRecordedByDedupeKey = new Map();
|
|
10
|
+
const hasPriorNotesByRunId = new Set();
|
|
11
|
+
const runContextByRunId = new Map();
|
|
12
|
+
for (const event of events) {
|
|
13
|
+
switch (event.kind) {
|
|
14
|
+
case 'run_started':
|
|
15
|
+
runStartedByRunId.set(event.scope.runId, event);
|
|
16
|
+
break;
|
|
17
|
+
case 'node_created':
|
|
18
|
+
nodeCreatedByNodeId.set(event.scope.nodeId, event);
|
|
19
|
+
break;
|
|
20
|
+
case 'edge_created':
|
|
21
|
+
hasChildEdgeByFromNodeId.add(event.data.fromNodeId);
|
|
22
|
+
break;
|
|
23
|
+
case 'advance_recorded':
|
|
24
|
+
advanceRecordedByDedupeKey.set(event.dedupeKey, event);
|
|
25
|
+
break;
|
|
26
|
+
case 'node_output_appended': {
|
|
27
|
+
const outputEvt = event;
|
|
28
|
+
if (outputEvt.data.outputChannel === constants_js_1.OUTPUT_CHANNEL.RECAP &&
|
|
29
|
+
outputEvt.data.payload.payloadKind === constants_js_1.PAYLOAD_KIND.NOTES) {
|
|
30
|
+
hasPriorNotesByRunId.add(event.scope.runId);
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
case 'context_set': {
|
|
35
|
+
const ctx = event.data.context;
|
|
36
|
+
if (ctx && typeof ctx === 'object' && !Array.isArray(ctx)) {
|
|
37
|
+
runContextByRunId.set(event.scope.runId, ctx);
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
default:
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const lastEvent = events[events.length - 1];
|
|
46
|
+
const nextEventIndex = lastEvent !== undefined ? lastEvent.eventIndex + 1 : 0;
|
|
47
|
+
const data = {
|
|
48
|
+
sortedEvents: events,
|
|
49
|
+
runStartedByRunId,
|
|
50
|
+
nodeCreatedByNodeId,
|
|
51
|
+
hasChildEdgeByFromNodeId,
|
|
52
|
+
advanceRecordedByDedupeKey,
|
|
53
|
+
nextEventIndex,
|
|
54
|
+
hasPriorNotesByRunId,
|
|
55
|
+
runContextByRunId,
|
|
56
|
+
};
|
|
57
|
+
return data;
|
|
58
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Result } from 'neverthrow';
|
|
2
|
+
import type { Brand } from '../../runtime/brand.js';
|
|
3
|
+
import type { DomainEventV1 } from './schemas/session/index.js';
|
|
4
|
+
import type { ProjectionError } from '../projections/projection-error.js';
|
|
5
|
+
export type SortedEventLog = Brand<readonly DomainEventV1[], 'v2.SortedEventLog'>;
|
|
6
|
+
export declare function asSortedEventLog(events: readonly DomainEventV1[]): Result<SortedEventLog, ProjectionError>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.asSortedEventLog = asSortedEventLog;
|
|
4
|
+
const neverthrow_1 = require("neverthrow");
|
|
5
|
+
function asSortedEventLog(events) {
|
|
6
|
+
for (let i = 1; i < events.length; i++) {
|
|
7
|
+
if (events[i].eventIndex <= events[i - 1].eventIndex) {
|
|
8
|
+
return (0, neverthrow_1.err)({
|
|
9
|
+
code: 'PROJECTION_INVARIANT_VIOLATION',
|
|
10
|
+
message: 'Events must be sorted by eventIndex strictly ascending (no duplicates)',
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return (0, neverthrow_1.ok)(events);
|
|
15
|
+
}
|
|
@@ -166,18 +166,18 @@ class NodeFileSystemV2 {
|
|
|
166
166
|
readdirWithMtime(dirPath) {
|
|
167
167
|
return neverthrow_1.ResultAsync.fromPromise((async () => {
|
|
168
168
|
const entries = await fs.readdir(dirPath);
|
|
169
|
+
const results = await Promise.allSettled(entries.map((name) => fs.stat(path.join(dirPath, name)).then((s) => ({ name, mtimeMs: s.mtimeMs }))));
|
|
169
170
|
const withMtime = [];
|
|
170
171
|
let skipped = 0;
|
|
171
|
-
for (
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
withMtime.push(
|
|
172
|
+
for (let i = 0; i < results.length; i++) {
|
|
173
|
+
const result = results[i];
|
|
174
|
+
if (result.status === 'fulfilled') {
|
|
175
|
+
withMtime.push(result.value);
|
|
175
176
|
}
|
|
176
|
-
|
|
177
|
-
const code = nodeErrorCode(
|
|
177
|
+
else {
|
|
178
|
+
const code = nodeErrorCode(result.reason);
|
|
178
179
|
skipped++;
|
|
179
|
-
console.error(`[workrail:session-enum] Skipping ${
|
|
180
|
-
continue;
|
|
180
|
+
console.error(`[workrail:session-enum] Skipping ${entries[i]}: stat failed (${code ?? 'unknown'}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)})`);
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
if (skipped > 0) {
|
|
@@ -10,7 +10,7 @@ export declare class LocalSessionEventLogStoreV2 implements SessionEventLogReado
|
|
|
10
10
|
private readonly fs;
|
|
11
11
|
private readonly sha256;
|
|
12
12
|
constructor(dataDir: DataDirPortV2, fs: FileSystemPortV2, sha256: Sha256PortV2);
|
|
13
|
-
append(lock: WithHealthySessionLock, plan: AppendPlanV2): ResultAsync<void, SessionEventLogStoreError>;
|
|
13
|
+
append(lock: WithHealthySessionLock, plan: AppendPlanV2, preloadedTruth?: LoadedSessionTruthV2): ResultAsync<void, SessionEventLogStoreError>;
|
|
14
14
|
load(sessionId: SessionId): ResultAsync<LoadedSessionTruthV2, SessionEventLogStoreError>;
|
|
15
15
|
loadValidatedPrefix(sessionId: SessionId): ResultAsync<LoadedValidatedPrefixV2, SessionEventLogStoreError>;
|
|
16
16
|
private appendImpl;
|
|
@@ -39,20 +39,22 @@ const jsonl_js_1 = require("../../../durable-core/canonical/jsonl.js");
|
|
|
39
39
|
const index_js_1 = require("../../../durable-core/schemas/session/index.js");
|
|
40
40
|
const constants_js_1 = require("../../../durable-core/constants.js");
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
|
+
const _utf8Decoder = new TextDecoder();
|
|
43
|
+
const _createdEventsDirs = new Set();
|
|
42
44
|
class LocalSessionEventLogStoreV2 {
|
|
43
45
|
constructor(dataDir, fs, sha256) {
|
|
44
46
|
this.dataDir = dataDir;
|
|
45
47
|
this.fs = fs;
|
|
46
48
|
this.sha256 = sha256;
|
|
47
49
|
}
|
|
48
|
-
append(lock, plan) {
|
|
50
|
+
append(lock, plan, preloadedTruth) {
|
|
49
51
|
if (!lock.assertHeld()) {
|
|
50
52
|
return (0, neverthrow_1.errAsync)({
|
|
51
53
|
code: 'SESSION_STORE_INVARIANT_VIOLATION',
|
|
52
54
|
message: 'WithHealthySessionLock used after gate callback ended (witness misuse-after-release)',
|
|
53
55
|
});
|
|
54
56
|
}
|
|
55
|
-
return this.appendImpl(lock.sessionId, plan);
|
|
57
|
+
return this.appendImpl(lock.sessionId, plan, preloadedTruth);
|
|
56
58
|
}
|
|
57
59
|
load(sessionId) {
|
|
58
60
|
return this.loadImpl(sessionId);
|
|
@@ -60,13 +62,20 @@ class LocalSessionEventLogStoreV2 {
|
|
|
60
62
|
loadValidatedPrefix(sessionId) {
|
|
61
63
|
return this.loadValidatedPrefixImpl(sessionId);
|
|
62
64
|
}
|
|
63
|
-
appendImpl(sessionId, plan) {
|
|
65
|
+
appendImpl(sessionId, plan, preloadedTruth) {
|
|
64
66
|
const sessionDir = this.dataDir.sessionDir(sessionId);
|
|
65
67
|
const eventsDir = this.dataDir.sessionEventsDir(sessionId);
|
|
66
68
|
const manifestPath = this.dataDir.sessionManifestPath(sessionId);
|
|
67
|
-
|
|
68
|
-
.
|
|
69
|
-
.
|
|
69
|
+
const mkdirpResult = _createdEventsDirs.has(eventsDir)
|
|
70
|
+
? (0, neverthrow_1.okAsync)(undefined)
|
|
71
|
+
: this.fs.mkdirp(eventsDir).mapErr(mapFsToStoreError).map(() => { _createdEventsDirs.add(eventsDir); });
|
|
72
|
+
const truthSource = preloadedTruth !== undefined
|
|
73
|
+
? mkdirpResult.andThen(() => (0, neverthrow_1.okAsync)({
|
|
74
|
+
manifest: preloadedTruth.manifest,
|
|
75
|
+
events: preloadedTruth.events,
|
|
76
|
+
}))
|
|
77
|
+
: mkdirpResult.andThen(() => this.loadTruthOrEmpty(sessionId));
|
|
78
|
+
return truthSource
|
|
70
79
|
.andThen(({ manifest, events: existingEvents }) => {
|
|
71
80
|
const contiguityRes = validateManifestContiguity(manifest);
|
|
72
81
|
if (contiguityRes.isErr())
|
|
@@ -116,24 +125,23 @@ class LocalSessionEventLogStoreV2 {
|
|
|
116
125
|
sha256: digest,
|
|
117
126
|
bytes: segmentBytes.length,
|
|
118
127
|
};
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
snapshotRef: p.snapshotRef,
|
|
132
|
-
createdByEventId: p.createdByEventId,
|
|
133
|
-
}));
|
|
134
|
-
return this.appendManifestRecords(manifestPath, records);
|
|
135
|
-
});
|
|
128
|
+
const pins = sortedPins(plan.snapshotPins);
|
|
129
|
+
const startIndex = segClosed.manifestIndex + 1;
|
|
130
|
+
const pinRecords = pins.map((p, i) => ({
|
|
131
|
+
v: 1,
|
|
132
|
+
manifestIndex: startIndex + i,
|
|
133
|
+
sessionId,
|
|
134
|
+
kind: constants_js_1.MANIFEST_KIND.SNAPSHOT_PINNED,
|
|
135
|
+
eventIndex: p.eventIndex,
|
|
136
|
+
snapshotRef: p.snapshotRef,
|
|
137
|
+
createdByEventId: p.createdByEventId,
|
|
138
|
+
}));
|
|
139
|
+
return this.appendManifestRecords(manifestPath, [segClosed, ...pinRecords]);
|
|
136
140
|
});
|
|
141
|
+
})
|
|
142
|
+
.orElse((error) => {
|
|
143
|
+
_createdEventsDirs.delete(eventsDir);
|
|
144
|
+
return (0, neverthrow_1.errAsync)(error);
|
|
137
145
|
});
|
|
138
146
|
}
|
|
139
147
|
loadImpl(sessionId) {
|
|
@@ -147,7 +155,7 @@ class LocalSessionEventLogStoreV2 {
|
|
|
147
155
|
if (segRes.isErr())
|
|
148
156
|
return (0, neverthrow_1.errAsync)(segRes.error);
|
|
149
157
|
const segments = manifest.filter((m) => m.kind === constants_js_1.MANIFEST_KIND.SEGMENT_CLOSED);
|
|
150
|
-
return
|
|
158
|
+
return loadSegmentsParallel({
|
|
151
159
|
segments,
|
|
152
160
|
sessionDir,
|
|
153
161
|
sha256: this.sha256,
|
|
@@ -334,17 +342,14 @@ function validateAppendPlan(sessionId, plan, expectedFirstEventIndex) {
|
|
|
334
342
|
});
|
|
335
343
|
}
|
|
336
344
|
for (let i = 0; i < plan.events.length; i++) {
|
|
337
|
-
const e =
|
|
338
|
-
if (
|
|
339
|
-
return (0, neverthrow_1.err)({ code: 'SESSION_STORE_INVARIANT_VIOLATION', message: `Invalid domain event at index ${i}` });
|
|
340
|
-
}
|
|
341
|
-
if (e.data.sessionId !== sessionId) {
|
|
345
|
+
const e = plan.events[i];
|
|
346
|
+
if (e.sessionId !== sessionId) {
|
|
342
347
|
return (0, neverthrow_1.err)({
|
|
343
348
|
code: 'SESSION_STORE_INVARIANT_VIOLATION',
|
|
344
349
|
message: `Domain event sessionId mismatch at index ${i}`,
|
|
345
350
|
});
|
|
346
351
|
}
|
|
347
|
-
if (i > 0 &&
|
|
352
|
+
if (i > 0 && e.eventIndex !== plan.events[i - 1].eventIndex + 1) {
|
|
348
353
|
return (0, neverthrow_1.err)({
|
|
349
354
|
code: 'SESSION_STORE_INVARIANT_VIOLATION',
|
|
350
355
|
message: `Non-contiguous eventIndex in AppendPlan at index ${i}`,
|
|
@@ -439,7 +444,7 @@ function parseJsonlText(text, schema) {
|
|
|
439
444
|
return (0, neverthrow_1.ok)(out);
|
|
440
445
|
}
|
|
441
446
|
function parseJsonlLines(bytes, schema) {
|
|
442
|
-
const text =
|
|
447
|
+
const text = _utf8Decoder.decode(bytes);
|
|
443
448
|
return parseJsonlText(text, schema);
|
|
444
449
|
}
|
|
445
450
|
function extractSnapshotPinsFromEvents(events) {
|
|
@@ -573,41 +578,46 @@ function processSegmentForSalvage(args) {
|
|
|
573
578
|
});
|
|
574
579
|
});
|
|
575
580
|
}
|
|
576
|
-
function
|
|
581
|
+
function loadSegmentsParallel(args) {
|
|
577
582
|
if (args.segments.length === 0)
|
|
578
583
|
return (0, neverthrow_1.okAsync)([]);
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
return args.fs.readFileBytes(segmentPath)
|
|
582
|
-
.mapErr((e) => {
|
|
584
|
+
return neverthrow_1.ResultAsync.fromPromise(Promise.all(args.segments.map((seg) => args.fs.readFileBytes(path.join(args.sessionDir, seg.segmentRelPath))
|
|
585
|
+
.match((bytes) => ({ ok: true, bytes, seg }), (e) => {
|
|
583
586
|
if (e.code === 'FS_NOT_FOUND') {
|
|
584
587
|
return {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
588
|
+
ok: false,
|
|
589
|
+
error: {
|
|
590
|
+
code: 'SESSION_STORE_CORRUPTION_DETECTED',
|
|
591
|
+
location: 'tail',
|
|
592
|
+
reason: { code: 'missing_attested_segment', message: `Missing attested segment: ${seg.segmentRelPath}` },
|
|
593
|
+
message: `Missing attested segment: ${seg.segmentRelPath}`,
|
|
594
|
+
},
|
|
589
595
|
};
|
|
590
596
|
}
|
|
591
|
-
return mapFsToStoreError(e);
|
|
592
|
-
})
|
|
593
|
-
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
597
|
+
return { ok: false, error: mapFsToStoreError(e) };
|
|
598
|
+
}))), (e) => ({ code: 'SESSION_STORE_IO_ERROR', message: String(e) })).andThen((readResults) => {
|
|
599
|
+
const allEvents = [];
|
|
600
|
+
for (const result of readResults) {
|
|
601
|
+
if (!result.ok)
|
|
602
|
+
return (0, neverthrow_1.err)(result.error);
|
|
603
|
+
const { bytes, seg } = result;
|
|
604
|
+
const actual = args.sha256.sha256(bytes);
|
|
605
|
+
if (actual !== seg.sha256) {
|
|
606
|
+
return (0, neverthrow_1.err)({
|
|
607
|
+
code: 'SESSION_STORE_CORRUPTION_DETECTED',
|
|
608
|
+
location: 'tail',
|
|
609
|
+
reason: { code: 'digest_mismatch', message: `Segment digest mismatch: ${seg.segmentRelPath}` },
|
|
610
|
+
message: `Segment digest mismatch: ${seg.segmentRelPath}`,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
const parsedRes = parseJsonlLines(bytes, index_js_1.DomainEventV1Schema);
|
|
614
|
+
if (parsedRes.isErr())
|
|
615
|
+
return (0, neverthrow_1.err)(parsedRes.error);
|
|
616
|
+
const boundsResult = validateSegmentBounds(parsedRes.value, seg);
|
|
617
|
+
if (boundsResult.isErr())
|
|
618
|
+
return (0, neverthrow_1.err)(boundsResult.error);
|
|
619
|
+
allEvents.push(...parsedRes.value);
|
|
602
620
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
return (0, neverthrow_1.errAsync)(parsedRes.error);
|
|
606
|
-
const parsed = parsedRes.value;
|
|
607
|
-
const boundsResult = validateSegmentBounds(parsed, head);
|
|
608
|
-
if (boundsResult.isErr())
|
|
609
|
-
return (0, neverthrow_1.errAsync)(boundsResult.error);
|
|
610
|
-
return (0, neverthrow_1.okAsync)(parsed);
|
|
611
|
-
})
|
|
612
|
-
.andThen((events) => loadSegmentsRecursive({ ...args, segments: tail }).map((rest) => [...events, ...rest]));
|
|
621
|
+
return (0, neverthrow_1.ok)(allEvents);
|
|
622
|
+
});
|
|
613
623
|
}
|
|
@@ -8,6 +8,7 @@ const session_health_js_1 = require("../../../projections/session-health.js");
|
|
|
8
8
|
const run_dag_js_1 = require("../../../projections/run-dag.js");
|
|
9
9
|
const node_outputs_js_1 = require("../../../projections/node-outputs.js");
|
|
10
10
|
const run_context_js_1 = require("../../../projections/run-context.js");
|
|
11
|
+
const sorted_event_log_js_1 = require("../../../durable-core/sorted-event-log.js");
|
|
11
12
|
const snapshot_state_js_1 = require("../../../durable-core/projections/snapshot-state.js");
|
|
12
13
|
const index_js_1 = require("../../../durable-core/ids/index.js");
|
|
13
14
|
const constants_js_1 = require("../../../durable-core/constants.js");
|
|
@@ -87,6 +88,10 @@ function projectSessionSummary(sessionId, truth, mtimeMs) {
|
|
|
87
88
|
const workflow = extractWorkflowIdentity(truth.events, bestRun.run.runId);
|
|
88
89
|
if (!workflow)
|
|
89
90
|
return null;
|
|
91
|
+
const sortedEventsRes = (0, sorted_event_log_js_1.asSortedEventLog)(truth.events);
|
|
92
|
+
const sessionTitle = sortedEventsRes.isOk()
|
|
93
|
+
? deriveSessionTitle(sortedEventsRes.value, bestRun.run.runId)
|
|
94
|
+
: null;
|
|
90
95
|
const outputsRes = (0, node_outputs_js_1.projectNodeOutputsV2)(truth.events);
|
|
91
96
|
const recapSnippet = outputsRes.isOk()
|
|
92
97
|
? extractAggregateRecap(outputsRes.value, bestRun.run, bestRun.tipNodeId)
|
|
@@ -102,7 +107,7 @@ function projectSessionSummary(sessionId, truth, mtimeMs) {
|
|
|
102
107
|
recapSnippet,
|
|
103
108
|
observations: extractObservations(truth.events),
|
|
104
109
|
workflow,
|
|
105
|
-
sessionTitle
|
|
110
|
+
sessionTitle,
|
|
106
111
|
lastModifiedMs: mtimeMs,
|
|
107
112
|
pendingStepId: null,
|
|
108
113
|
isComplete: false,
|
|
@@ -150,8 +155,8 @@ function extractWorkflowIdentity(events, runId) {
|
|
|
150
155
|
workflowHash: (0, index_js_1.asWorkflowHash)((0, index_js_1.asSha256Digest)(event.data.workflowHash)),
|
|
151
156
|
};
|
|
152
157
|
}
|
|
153
|
-
function deriveSessionTitle(
|
|
154
|
-
const contextRes = (0, run_context_js_1.projectRunContextV2)(
|
|
158
|
+
function deriveSessionTitle(sortedEvents, runId) {
|
|
159
|
+
const contextRes = (0, run_context_js_1.projectRunContextV2)(sortedEvents);
|
|
155
160
|
if (contextRes.isOk()) {
|
|
156
161
|
const runCtx = contextRes.value.byRunId[runId];
|
|
157
162
|
if (runCtx) {
|
|
@@ -163,7 +168,7 @@ function deriveSessionTitle(events, runId) {
|
|
|
163
168
|
}
|
|
164
169
|
}
|
|
165
170
|
}
|
|
166
|
-
return extractTitleFromFirstRecap(
|
|
171
|
+
return extractTitleFromFirstRecap(sortedEvents);
|
|
167
172
|
}
|
|
168
173
|
function extractTitleFromFirstRecap(events) {
|
|
169
174
|
const outputsRes = (0, node_outputs_js_1.projectNodeOutputsV2)(events);
|
|
@@ -5,6 +5,7 @@ const neverthrow_1 = require("neverthrow");
|
|
|
5
5
|
const index_js_1 = require("../../../durable-core/ids/index.js");
|
|
6
6
|
const index_js_2 = require("../../../durable-core/schemas/execution-snapshot/index.js");
|
|
7
7
|
const jcs_js_1 = require("../../../durable-core/canonical/jcs.js");
|
|
8
|
+
const _utf8Decoder = new TextDecoder();
|
|
8
9
|
class LocalSnapshotStoreV2 {
|
|
9
10
|
constructor(dataDir, fs, crypto) {
|
|
10
11
|
this.dataDir = dataDir;
|
|
@@ -41,7 +42,7 @@ class LocalSnapshotStoreV2 {
|
|
|
41
42
|
.andThen((bytes) => {
|
|
42
43
|
let parsed;
|
|
43
44
|
try {
|
|
44
|
-
parsed = JSON.parse(
|
|
45
|
+
parsed = JSON.parse(_utf8Decoder.decode(bytes));
|
|
45
46
|
}
|
|
46
47
|
catch {
|
|
47
48
|
return (0, neverthrow_1.errAsync)({
|
|
@@ -48,5 +48,5 @@ export interface SessionEventLogReadonlyStorePortV2 {
|
|
|
48
48
|
loadValidatedPrefix(sessionId: SessionId): ResultAsync<LoadedValidatedPrefixV2, SessionEventLogStoreError>;
|
|
49
49
|
}
|
|
50
50
|
export interface SessionEventLogAppendStorePortV2 {
|
|
51
|
-
append(lock: WithHealthySessionLock, plan: AppendPlanV2): ResultAsync<void, SessionEventLogStoreError>;
|
|
51
|
+
append(lock: WithHealthySessionLock, plan: AppendPlanV2, preloadedTruth?: LoadedSessionTruthV2): ResultAsync<void, SessionEventLogStoreError>;
|
|
52
52
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Result } from 'neverthrow';
|
|
2
2
|
import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
|
|
3
|
+
import type { SortedEventLog } from '../durable-core/sorted-event-log.js';
|
|
3
4
|
import type { ProjectionError } from './projection-error.js';
|
|
4
5
|
type AssessmentConsequenceAppliedEventV1 = Extract<DomainEventV1, {
|
|
5
6
|
kind: 'assessment_consequence_applied';
|
|
@@ -14,6 +15,6 @@ export interface AppliedAssessmentConsequenceViewV2 {
|
|
|
14
15
|
export interface AssessmentConsequencesProjectionV2 {
|
|
15
16
|
readonly byNodeId: Readonly<Record<string, readonly AppliedAssessmentConsequenceViewV2[]>>;
|
|
16
17
|
}
|
|
17
|
-
export declare function projectAssessmentConsequencesV2(events:
|
|
18
|
+
export declare function projectAssessmentConsequencesV2(events: SortedEventLog): Result<AssessmentConsequencesProjectionV2, ProjectionError>;
|
|
18
19
|
export declare function getLatestAssessmentConsequenceForNode(projection: AssessmentConsequencesProjectionV2, nodeId: string): AppliedAssessmentConsequenceViewV2 | undefined;
|
|
19
20
|
export {};
|
|
@@ -5,11 +5,6 @@ exports.getLatestAssessmentConsequenceForNode = getLatestAssessmentConsequenceFo
|
|
|
5
5
|
const neverthrow_1 = require("neverthrow");
|
|
6
6
|
const constants_js_1 = require("../durable-core/constants.js");
|
|
7
7
|
function projectAssessmentConsequencesV2(events) {
|
|
8
|
-
for (let i = 1; i < events.length; i++) {
|
|
9
|
-
if (events[i].eventIndex < events[i - 1].eventIndex) {
|
|
10
|
-
return (0, neverthrow_1.err)({ code: 'PROJECTION_INVARIANT_VIOLATION', message: 'Events must be sorted by eventIndex ascending' });
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
8
|
const byNodeId = {};
|
|
14
9
|
for (const event of events) {
|
|
15
10
|
if (event.kind !== constants_js_1.EVENT_KIND.ASSESSMENT_CONSEQUENCE_APPLIED)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Result } from 'neverthrow';
|
|
2
2
|
import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
|
|
3
|
+
import type { SortedEventLog } from '../durable-core/sorted-event-log.js';
|
|
3
4
|
import type { ProjectionError } from './projection-error.js';
|
|
4
5
|
type AssessmentRecordedEventV1 = Extract<DomainEventV1, {
|
|
5
6
|
kind: 'assessment_recorded';
|
|
@@ -16,6 +17,6 @@ export interface RecordedAssessmentViewV2 {
|
|
|
16
17
|
export interface AssessmentsProjectionV2 {
|
|
17
18
|
readonly byNodeId: Readonly<Record<string, readonly RecordedAssessmentViewV2[]>>;
|
|
18
19
|
}
|
|
19
|
-
export declare function projectAssessmentsV2(events:
|
|
20
|
+
export declare function projectAssessmentsV2(events: SortedEventLog): Result<AssessmentsProjectionV2, ProjectionError>;
|
|
20
21
|
export declare function getLatestAssessmentForNode(projection: AssessmentsProjectionV2, nodeId: string): RecordedAssessmentViewV2 | undefined;
|
|
21
22
|
export {};
|
|
@@ -5,10 +5,8 @@ exports.getLatestAssessmentForNode = getLatestAssessmentForNode;
|
|
|
5
5
|
const neverthrow_1 = require("neverthrow");
|
|
6
6
|
const constants_js_1 = require("../durable-core/constants.js");
|
|
7
7
|
function projectAssessmentsV2(events) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return (0, neverthrow_1.err)({ code: 'PROJECTION_INVARIANT_VIOLATION', message: 'Events must be sorted by eventIndex ascending' });
|
|
11
|
-
}
|
|
8
|
+
if (!events.some((e) => e.kind === constants_js_1.EVENT_KIND.ASSESSMENT_RECORDED)) {
|
|
9
|
+
return (0, neverthrow_1.ok)({ byNodeId: {} });
|
|
12
10
|
}
|
|
13
11
|
const byNodeId = {};
|
|
14
12
|
for (const event of events) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Result } from 'neverthrow';
|
|
2
2
|
import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
|
|
3
|
+
import type { SortedEventLog } from '../durable-core/sorted-event-log.js';
|
|
3
4
|
import type { ProjectionError } from './projection-error.js';
|
|
4
5
|
type GapRecordedEventV1 = Extract<DomainEventV1, {
|
|
5
6
|
kind: 'gap_recorded';
|
|
@@ -19,5 +20,5 @@ export interface GapsProjectionV2 {
|
|
|
19
20
|
readonly resolvedGapIds: ReadonlySet<string>;
|
|
20
21
|
readonly unresolvedCriticalByRunId: Readonly<Record<string, readonly GapV2[]>>;
|
|
21
22
|
}
|
|
22
|
-
export declare function projectGapsV2(events:
|
|
23
|
+
export declare function projectGapsV2(events: SortedEventLog): Result<GapsProjectionV2, ProjectionError>;
|
|
23
24
|
export {};
|
|
@@ -4,11 +4,6 @@ exports.projectGapsV2 = projectGapsV2;
|
|
|
4
4
|
const neverthrow_1 = require("neverthrow");
|
|
5
5
|
const constants_js_1 = require("../durable-core/constants.js");
|
|
6
6
|
function projectGapsV2(events) {
|
|
7
|
-
for (let i = 1; i < events.length; i++) {
|
|
8
|
-
if (events[i].eventIndex < events[i - 1].eventIndex) {
|
|
9
|
-
return (0, neverthrow_1.err)({ code: 'PROJECTION_INVARIANT_VIOLATION', message: 'Events must be sorted by eventIndex ascending' });
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
7
|
const byGapId = {};
|
|
13
8
|
const resolved = new Set();
|
|
14
9
|
for (const e of events) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Result } from 'neverthrow';
|
|
2
2
|
import type { DomainEventV1 } from '../durable-core/schemas/session/index.js';
|
|
3
|
+
import type { SortedEventLog } from '../durable-core/sorted-event-log.js';
|
|
3
4
|
import type { AutonomyV2, RiskPolicyV2 } from '../durable-core/schemas/session/preferences.js';
|
|
4
5
|
import type { ProjectionError } from './projection-error.js';
|
|
5
6
|
type PreferencesChangedEventV1 = Extract<DomainEventV1, {
|
|
@@ -17,5 +18,5 @@ export interface NodePreferencesV2 {
|
|
|
17
18
|
export interface PreferencesProjectionV2 {
|
|
18
19
|
readonly byNodeId: Readonly<Record<string, NodePreferencesV2>>;
|
|
19
20
|
}
|
|
20
|
-
export declare function projectPreferencesV2(events:
|
|
21
|
+
export declare function projectPreferencesV2(events: SortedEventLog, parentByNodeId: Readonly<Record<string, string | null>>): Result<PreferencesProjectionV2, ProjectionError>;
|
|
21
22
|
export {};
|