@earendil-works/pi-agent-core 0.84.4 → 0.85.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/README.md +4 -0
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +9 -0
- package/dist/agent-loop.js.map +1 -1
- package/dist/harness/agent-harness.d.ts +639 -384
- package/dist/harness/agent-harness.d.ts.map +1 -1
- package/dist/harness/agent-harness.js +5 -251
- package/dist/harness/agent-harness.js.map +1 -1
- package/dist/harness/compaction/branch-summarization.d.ts +12 -6
- package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/harness/compaction/branch-summarization.js +33 -26
- package/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/dist/harness/compaction/compaction.d.ts +30 -8
- package/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/dist/harness/compaction/compaction.js +53 -63
- package/dist/harness/compaction/compaction.js.map +1 -1
- package/dist/harness/config.d.ts +9 -0
- package/dist/harness/config.d.ts.map +1 -0
- package/dist/harness/config.js +27 -0
- package/dist/harness/config.js.map +1 -0
- package/dist/harness/context.d.ts +9 -0
- package/dist/harness/context.d.ts.map +1 -0
- package/dist/harness/context.js +13 -0
- package/dist/harness/context.js.map +1 -0
- package/dist/harness/env/nodejs.d.ts +24 -28
- package/dist/harness/env/nodejs.d.ts.map +1 -1
- package/dist/harness/env/nodejs.js +239 -86
- package/dist/harness/env/nodejs.js.map +1 -1
- package/dist/harness/events.d.ts +20 -38
- package/dist/harness/events.d.ts.map +1 -1
- package/dist/harness/events.js +240 -55
- package/dist/harness/events.js.map +1 -1
- package/dist/harness/execution/assistant.d.ts +42 -0
- package/dist/harness/execution/assistant.d.ts.map +1 -0
- package/dist/harness/execution/assistant.js +82 -0
- package/dist/harness/execution/assistant.js.map +1 -0
- package/dist/harness/execution/effect-gate.d.ts +22 -0
- package/dist/harness/execution/effect-gate.d.ts.map +1 -0
- package/dist/harness/execution/effect-gate.js +49 -0
- package/dist/harness/execution/effect-gate.js.map +1 -0
- package/dist/harness/execution/tools.d.ts +67 -0
- package/dist/harness/execution/tools.d.ts.map +1 -0
- package/dist/harness/execution/tools.js +121 -0
- package/dist/harness/execution/tools.js.map +1 -0
- package/dist/harness/hooks.d.ts +38 -0
- package/dist/harness/hooks.d.ts.map +1 -0
- package/dist/harness/hooks.js +406 -0
- package/dist/harness/hooks.js.map +1 -0
- package/dist/harness/messages.d.ts +2 -2
- package/dist/harness/messages.d.ts.map +1 -1
- package/dist/harness/messages.js.map +1 -1
- package/dist/harness/prompt-templates.d.ts +3 -2
- package/dist/harness/prompt-templates.d.ts.map +1 -1
- package/dist/harness/prompt-templates.js +17 -17
- package/dist/harness/prompt-templates.js.map +1 -1
- package/dist/harness/result.d.ts +93 -0
- package/dist/harness/result.d.ts.map +1 -1
- package/dist/harness/result.js +40 -0
- package/dist/harness/result.js.map +1 -1
- package/dist/harness/runtime/drive/boundary.d.ts +28 -0
- package/dist/harness/runtime/drive/boundary.d.ts.map +1 -0
- package/dist/harness/runtime/drive/boundary.js +170 -0
- package/dist/harness/runtime/drive/boundary.js.map +1 -0
- package/dist/harness/runtime/drive/checkpoint.d.ts +8 -0
- package/dist/harness/runtime/drive/checkpoint.d.ts.map +1 -0
- package/dist/harness/runtime/drive/checkpoint.js +133 -0
- package/dist/harness/runtime/drive/checkpoint.js.map +1 -0
- package/dist/harness/runtime/drive/deferred.d.ts +15 -0
- package/dist/harness/runtime/drive/deferred.d.ts.map +1 -0
- package/dist/harness/runtime/drive/deferred.js +179 -0
- package/dist/harness/runtime/drive/deferred.js.map +1 -0
- package/dist/harness/runtime/drive/generation.d.ts +8 -0
- package/dist/harness/runtime/drive/generation.d.ts.map +1 -0
- package/dist/harness/runtime/drive/generation.js +205 -0
- package/dist/harness/runtime/drive/generation.js.map +1 -0
- package/dist/harness/runtime/drive/reconcile.d.ts +5 -0
- package/dist/harness/runtime/drive/reconcile.d.ts.map +1 -0
- package/dist/harness/runtime/drive/reconcile.js +140 -0
- package/dist/harness/runtime/drive/reconcile.js.map +1 -0
- package/dist/harness/runtime/drive/recovery.d.ts +8 -0
- package/dist/harness/runtime/drive/recovery.d.ts.map +1 -0
- package/dist/harness/runtime/drive/recovery.js +85 -0
- package/dist/harness/runtime/drive/recovery.js.map +1 -0
- package/dist/harness/runtime/drive/response.d.ts +23 -0
- package/dist/harness/runtime/drive/response.d.ts.map +1 -0
- package/dist/harness/runtime/drive/response.js +394 -0
- package/dist/harness/runtime/drive/response.js.map +1 -0
- package/dist/harness/runtime/drive/retry.d.ts +4 -0
- package/dist/harness/runtime/drive/retry.d.ts.map +1 -0
- package/dist/harness/runtime/drive/retry.js +37 -0
- package/dist/harness/runtime/drive/retry.js.map +1 -0
- package/dist/harness/runtime/drive/structural.d.ts +32 -0
- package/dist/harness/runtime/drive/structural.d.ts.map +1 -0
- package/dist/harness/runtime/drive/structural.js +929 -0
- package/dist/harness/runtime/drive/structural.js.map +1 -0
- package/dist/harness/runtime/drive/terminal.d.ts +7 -0
- package/dist/harness/runtime/drive/terminal.d.ts.map +1 -0
- package/dist/harness/runtime/drive/terminal.js +49 -0
- package/dist/harness/runtime/drive/terminal.js.map +1 -0
- package/dist/harness/runtime/drive/tool-placement.d.ts +14 -0
- package/dist/harness/runtime/drive/tool-placement.d.ts.map +1 -0
- package/dist/harness/runtime/drive/tool-placement.js +244 -0
- package/dist/harness/runtime/drive/tool-placement.js.map +1 -0
- package/dist/harness/runtime/drive/tools.d.ts +6 -0
- package/dist/harness/runtime/drive/tools.d.ts.map +1 -0
- package/dist/harness/runtime/drive/tools.js +449 -0
- package/dist/harness/runtime/drive/tools.js.map +1 -0
- package/dist/harness/runtime/drive.d.ts +6 -0
- package/dist/harness/runtime/drive.d.ts.map +1 -0
- package/dist/harness/runtime/drive.js +93 -0
- package/dist/harness/runtime/drive.js.map +1 -0
- package/dist/harness/runtime/harness.d.ts +59 -0
- package/dist/harness/runtime/harness.d.ts.map +1 -0
- package/dist/harness/runtime/harness.js +311 -0
- package/dist/harness/runtime/harness.js.map +1 -0
- package/dist/harness/runtime/index.d.ts +2 -0
- package/dist/harness/runtime/index.d.ts.map +1 -0
- package/dist/harness/runtime/index.js +2 -0
- package/dist/harness/runtime/index.js.map +1 -0
- package/dist/harness/runtime/lane.d.ts +101 -0
- package/dist/harness/runtime/lane.d.ts.map +1 -0
- package/dist/harness/runtime/lane.js +1575 -0
- package/dist/harness/runtime/lane.js.map +1 -0
- package/dist/harness/runtime/progress.d.ts +15 -0
- package/dist/harness/runtime/progress.d.ts.map +1 -0
- package/dist/harness/runtime/progress.js +66 -0
- package/dist/harness/runtime/progress.js.map +1 -0
- package/dist/harness/runtime/reducer.d.ts +5 -0
- package/dist/harness/runtime/reducer.d.ts.map +1 -0
- package/dist/harness/runtime/reducer.js +240 -0
- package/dist/harness/runtime/reducer.js.map +1 -0
- package/dist/harness/runtime/restore.d.ts +24 -0
- package/dist/harness/runtime/restore.d.ts.map +1 -0
- package/dist/harness/runtime/restore.js +117 -0
- package/dist/harness/runtime/restore.js.map +1 -0
- package/dist/harness/runtime/transcript.d.ts +21 -0
- package/dist/harness/runtime/transcript.d.ts.map +1 -0
- package/dist/harness/runtime/transcript.js +72 -0
- package/dist/harness/runtime/transcript.js.map +1 -0
- package/dist/harness/runtime/types.d.ts +105 -0
- package/dist/harness/runtime/types.d.ts.map +1 -0
- package/dist/harness/runtime/types.js +61 -0
- package/dist/harness/runtime/types.js.map +1 -0
- package/dist/harness/session/commit.d.ts +53 -0
- package/dist/harness/session/commit.d.ts.map +1 -0
- package/dist/harness/session/commit.js +57 -0
- package/dist/harness/session/commit.js.map +1 -0
- package/dist/harness/session/context.d.ts +6 -18
- package/dist/harness/session/context.d.ts.map +1 -1
- package/dist/harness/session/context.js +31 -49
- package/dist/harness/session/context.js.map +1 -1
- package/dist/harness/session/fork-policy.d.ts +13 -0
- package/dist/harness/session/fork-policy.d.ts.map +1 -0
- package/dist/harness/session/fork-policy.js +22 -0
- package/dist/harness/session/fork-policy.js.map +1 -0
- package/dist/harness/session/fork.d.ts +18 -0
- package/dist/harness/session/fork.d.ts.map +1 -0
- package/dist/harness/session/fork.js +127 -0
- package/dist/harness/session/fork.js.map +1 -0
- package/dist/harness/session/in-memory-storage-state.d.ts +39 -0
- package/dist/harness/session/in-memory-storage-state.d.ts.map +1 -0
- package/dist/harness/session/in-memory-storage-state.js +216 -0
- package/dist/harness/session/in-memory-storage-state.js.map +1 -0
- package/dist/harness/session/index.d.ts +9 -5
- package/dist/harness/session/index.d.ts.map +1 -1
- package/dist/harness/session/index.js +7 -4
- package/dist/harness/session/index.js.map +1 -1
- package/dist/harness/session/jsonl/codec.d.ts +19 -8
- package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
- package/dist/harness/session/jsonl/codec.js +34 -218
- package/dist/harness/session/jsonl/codec.js.map +1 -1
- package/dist/harness/session/jsonl/index.d.ts +4 -0
- package/dist/harness/session/jsonl/index.d.ts.map +1 -0
- package/dist/harness/session/jsonl/index.js +4 -0
- package/dist/harness/session/jsonl/index.js.map +1 -0
- package/dist/harness/session/jsonl/legacy-v3.d.ts +18 -0
- package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -0
- package/dist/harness/session/jsonl/legacy-v3.js +383 -0
- package/dist/harness/session/jsonl/legacy-v3.js.map +1 -0
- package/dist/harness/session/jsonl/repo.d.ts +26 -19
- package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
- package/dist/harness/session/jsonl/repo.js +266 -156
- package/dist/harness/session/jsonl/repo.js.map +1 -1
- package/dist/harness/session/jsonl/storage.d.ts +39 -41
- package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
- package/dist/harness/session/jsonl/storage.js +238 -181
- package/dist/harness/session/jsonl/storage.js.map +1 -1
- package/dist/harness/session/jsonl/types.d.ts +23 -22
- package/dist/harness/session/jsonl/types.d.ts.map +1 -1
- package/dist/harness/session/jsonl/types.js +2 -1
- package/dist/harness/session/jsonl/types.js.map +1 -1
- package/dist/harness/session/memory.d.ts +46 -40
- package/dist/harness/session/memory.d.ts.map +1 -1
- package/dist/harness/session/memory.js +346 -113
- package/dist/harness/session/memory.js.map +1 -1
- package/dist/harness/session/mutation-line.d.ts +8 -0
- package/dist/harness/session/mutation-line.d.ts.map +1 -0
- package/dist/harness/session/mutation-line.js +21 -0
- package/dist/harness/session/mutation-line.js.map +1 -0
- package/dist/harness/session/session.d.ts +78 -46
- package/dist/harness/session/session.d.ts.map +1 -1
- package/dist/harness/session/session.js +343 -209
- package/dist/harness/session/session.js.map +1 -1
- package/dist/harness/session/testing/benchmark/datasets.d.ts +12 -0
- package/dist/harness/session/testing/benchmark/datasets.d.ts.map +1 -0
- package/dist/harness/session/testing/benchmark/datasets.js +24 -0
- package/dist/harness/session/testing/benchmark/datasets.js.map +1 -0
- package/dist/harness/session/testing/benchmark/session-repo.d.ts +42 -0
- package/dist/harness/session/testing/benchmark/session-repo.d.ts.map +1 -0
- package/dist/harness/session/testing/benchmark/session-repo.js +127 -0
- package/dist/harness/session/testing/benchmark/session-repo.js.map +1 -0
- package/dist/harness/session/testing/benchmark/storage.d.ts +25 -0
- package/dist/harness/session/testing/benchmark/storage.d.ts.map +1 -0
- package/dist/harness/session/testing/benchmark/storage.js +114 -0
- package/dist/harness/session/testing/benchmark/storage.js.map +1 -0
- package/dist/harness/session/testing/conformance/session-repo.d.ts +21 -0
- package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -0
- package/dist/harness/session/testing/conformance/session-repo.js +590 -0
- package/dist/harness/session/testing/conformance/session-repo.js.map +1 -0
- package/dist/harness/session/testing/conformance/storage.d.ts +4 -0
- package/dist/harness/session/testing/conformance/storage.d.ts.map +1 -0
- package/dist/harness/session/testing/conformance/storage.js +600 -0
- package/dist/harness/session/testing/conformance/storage.js.map +1 -0
- package/dist/harness/session/testing/gating-storage.d.ts +26 -0
- package/dist/harness/session/testing/gating-storage.d.ts.map +1 -0
- package/dist/harness/session/testing/gating-storage.js +100 -0
- package/dist/harness/session/testing/gating-storage.js.map +1 -0
- package/dist/harness/session/testing/index.d.ts +9 -2
- package/dist/harness/session/testing/index.d.ts.map +1 -1
- package/dist/harness/session/testing/index.js +8 -1
- package/dist/harness/session/testing/index.js.map +1 -1
- package/dist/harness/session/testing/instrumented-storage.d.ts +11 -0
- package/dist/harness/session/testing/instrumented-storage.d.ts.map +1 -0
- package/dist/harness/session/testing/instrumented-storage.js +16 -0
- package/dist/harness/session/testing/instrumented-storage.js.map +1 -0
- package/dist/harness/session/testing/storage-decorator.d.ts +20 -0
- package/dist/harness/session/testing/storage-decorator.d.ts.map +1 -0
- package/dist/harness/session/testing/storage-decorator.js +41 -0
- package/dist/harness/session/testing/storage-decorator.js.map +1 -0
- package/dist/harness/session/testing/types.d.ts +5 -7
- package/dist/harness/session/testing/types.d.ts.map +1 -1
- package/dist/harness/session/testing/types.js.map +1 -1
- package/dist/harness/session/types.d.ts +424 -259
- package/dist/harness/session/types.d.ts.map +1 -1
- package/dist/harness/session/types.js +7 -8
- package/dist/harness/session/types.js.map +1 -1
- package/dist/harness/session/values.d.ts +95 -0
- package/dist/harness/session/values.d.ts.map +1 -0
- package/dist/harness/session/values.js +81 -0
- package/dist/harness/session/values.js.map +1 -0
- package/dist/harness/skills.d.ts +3 -2
- package/dist/harness/skills.d.ts.map +1 -1
- package/dist/harness/skills.js +29 -26
- package/dist/harness/skills.js.map +1 -1
- package/dist/harness/telemetry.d.ts +64 -43
- package/dist/harness/telemetry.d.ts.map +1 -1
- package/dist/harness/telemetry.js +45 -26
- package/dist/harness/telemetry.js.map +1 -1
- package/dist/harness/tools/bash.d.ts +4 -4
- package/dist/harness/tools/bash.d.ts.map +1 -1
- package/dist/harness/tools/bash.js +66 -93
- package/dist/harness/tools/bash.js.map +1 -1
- package/dist/harness/tools/edit.d.ts.map +1 -1
- package/dist/harness/tools/edit.js +10 -10
- package/dist/harness/tools/edit.js.map +1 -1
- package/dist/harness/tools/file-mutation-queue.d.ts +2 -1
- package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/harness/tools/file-mutation-queue.js +5 -5
- package/dist/harness/tools/file-mutation-queue.js.map +1 -1
- package/dist/harness/tools/path-utils.d.ts +3 -2
- package/dist/harness/tools/path-utils.d.ts.map +1 -1
- package/dist/harness/tools/path-utils.js +5 -5
- package/dist/harness/tools/path-utils.js.map +1 -1
- package/dist/harness/tools/read.d.ts +2 -1
- package/dist/harness/tools/read.d.ts.map +1 -1
- package/dist/harness/tools/read.js +4 -6
- package/dist/harness/tools/read.js.map +1 -1
- package/dist/harness/tools/write.d.ts.map +1 -1
- package/dist/harness/tools/write.js +7 -7
- package/dist/harness/tools/write.js.map +1 -1
- package/dist/harness/types.d.ts +108 -45
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/utils/adaptive-publisher.d.ts +24 -0
- package/dist/harness/utils/adaptive-publisher.d.ts.map +1 -0
- package/dist/harness/utils/adaptive-publisher.js +79 -0
- package/dist/harness/utils/adaptive-publisher.js.map +1 -0
- package/dist/harness/utils/output-capture.d.ts +31 -0
- package/dist/harness/utils/output-capture.d.ts.map +1 -0
- package/dist/harness/utils/output-capture.js +217 -0
- package/dist/harness/utils/output-capture.js.map +1 -0
- package/dist/harness/utils/shell-output.d.ts +12 -5
- package/dist/harness/utils/shell-output.d.ts.map +1 -1
- package/dist/harness/utils/shell-output.js +64 -174
- package/dist/harness/utils/shell-output.js.map +1 -1
- package/dist/harness/utils/truncate.d.ts +1 -0
- package/dist/harness/utils/truncate.d.ts.map +1 -1
- package/dist/harness/utils/truncate.js +1 -1
- package/dist/harness/utils/truncate.js.map +1 -1
- package/dist/harness/utils/usage.d.ts +4 -0
- package/dist/harness/utils/usage.d.ts.map +1 -0
- package/dist/harness/utils/usage.js +33 -0
- package/dist/harness/utils/usage.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -6
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +2 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +40 -10
- package/dist/proxy.js.map +1 -1
- package/dist/search/index.d.ts +24 -16
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +1 -1
- package/dist/search/index.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +25 -4
- package/dist/harness/reducer.d.ts +0 -100
- package/dist/harness/reducer.d.ts.map +0 -1
- package/dist/harness/reducer.js +0 -415
- package/dist/harness/reducer.js.map +0 -1
- package/dist/harness/session/jsonl/errors.d.ts +0 -9
- package/dist/harness/session/jsonl/errors.d.ts.map +0 -1
- package/dist/harness/session/jsonl/errors.js +0 -19
- package/dist/harness/session/jsonl/errors.js.map +0 -1
- package/dist/harness/session/jsonl.d.ts +0 -3
- package/dist/harness/session/jsonl.d.ts.map +0 -1
- package/dist/harness/session/jsonl.js +0 -2
- package/dist/harness/session/jsonl.js.map +0 -1
- package/dist/harness/session/state.d.ts +0 -65
- package/dist/harness/session/state.d.ts.map +0 -1
- package/dist/harness/session/state.js +0 -319
- package/dist/harness/session/state.js.map +0 -1
- package/dist/harness/session/testing/conformance.d.ts +0 -4
- package/dist/harness/session/testing/conformance.d.ts.map +0 -1
- package/dist/harness/session/testing/conformance.js +0 -762
- package/dist/harness/session/testing/conformance.js.map +0 -1
- package/dist/search/scanning.d.ts +0 -29
- package/dist/search/scanning.d.ts.map +0 -1
- package/dist/search/scanning.js +0 -103
- package/dist/search/scanning.js.map +0 -1
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { deepStrictEqual, rejects, strictEqual } from "node:assert/strict";
|
|
2
|
+
import { BACKGROUND_CONTEXT } from "../../../context.js";
|
|
3
|
+
import { insertEntry, insertUsage } from "../../commit.js";
|
|
4
|
+
import { appendList, branchTip, entryLabel, laneConfig, laneState, list, operationMeta, operationPreparation, operationResult, operationState, operationToolArgs, pendingEntry, sessionName, setValue, value, } from "../../values.js";
|
|
5
|
+
const ROOT_ID = "00000000-0000-7000-8000-000000000001";
|
|
6
|
+
const CHILD_ID = "00000000-0000-7000-8000-000000000002";
|
|
7
|
+
const SIBLING_ID = "00000000-0000-7000-8000-000000000003";
|
|
8
|
+
const USAGE_ID = "00000000-0000-7000-8000-000000000004";
|
|
9
|
+
const OPERATION_ID = "00000000-0000-7000-8000-000000000005";
|
|
10
|
+
const PENDING_ID = "00000000-0000-7000-8000-000000000006";
|
|
11
|
+
const UNKNOWN_ID = "00000000-0000-7000-8000-000000000007";
|
|
12
|
+
const idleLaneState = {
|
|
13
|
+
currentOperationId: null,
|
|
14
|
+
lastOperationId: null,
|
|
15
|
+
inbox: [],
|
|
16
|
+
};
|
|
17
|
+
const applicationValue = value("test.application.value");
|
|
18
|
+
const applicationList = list("test.application.list");
|
|
19
|
+
const configuration = {
|
|
20
|
+
model: { provider: "provider", modelId: "model" },
|
|
21
|
+
thinkingLevel: "off",
|
|
22
|
+
activeToolNames: ["read"],
|
|
23
|
+
};
|
|
24
|
+
async function getBranchTip(session, name = "main") {
|
|
25
|
+
return (await session.branch(name, BACKGROUND_CONTEXT))?.getTipId(BACKGROUND_CONTEXT);
|
|
26
|
+
}
|
|
27
|
+
function assistantMessage(stopReason) {
|
|
28
|
+
return {
|
|
29
|
+
role: "assistant",
|
|
30
|
+
content: stopReason === "toolUse"
|
|
31
|
+
? [{ type: "toolCall", id: "call", name: "read", arguments: {} }]
|
|
32
|
+
: [{ type: "text", text: stopReason }],
|
|
33
|
+
api: "anthropic-messages",
|
|
34
|
+
provider: "anthropic",
|
|
35
|
+
model: "claude-sonnet-4-5",
|
|
36
|
+
usage: {
|
|
37
|
+
input: 0,
|
|
38
|
+
output: 0,
|
|
39
|
+
cacheRead: 0,
|
|
40
|
+
cacheWrite: 0,
|
|
41
|
+
totalTokens: 0,
|
|
42
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
43
|
+
},
|
|
44
|
+
stopReason,
|
|
45
|
+
...(stopReason === "deferred"
|
|
46
|
+
? {
|
|
47
|
+
deferred: {
|
|
48
|
+
provider: "anthropic",
|
|
49
|
+
modelId: "claude-sonnet-4-5",
|
|
50
|
+
api: "anthropic-messages",
|
|
51
|
+
id: "job",
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
: {}),
|
|
55
|
+
timestamp: 1,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function usageRow() {
|
|
59
|
+
return {
|
|
60
|
+
id: USAGE_ID,
|
|
61
|
+
adjustment: true,
|
|
62
|
+
usage: {
|
|
63
|
+
input: 1,
|
|
64
|
+
output: 2,
|
|
65
|
+
cacheRead: 0,
|
|
66
|
+
cacheWrite: 0,
|
|
67
|
+
totalTokens: 3,
|
|
68
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function prepareRepoCaseFactory(factory, onClose) {
|
|
73
|
+
return async () => ({
|
|
74
|
+
repo: await factory(),
|
|
75
|
+
...(onClose === undefined ? {} : { close: onClose }),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function createCase(factory, group, name, test) {
|
|
79
|
+
return {
|
|
80
|
+
group,
|
|
81
|
+
name,
|
|
82
|
+
async run() {
|
|
83
|
+
const context = await factory();
|
|
84
|
+
try {
|
|
85
|
+
await test(context);
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
await context.close?.();
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Creates lifecycle cases for repositories that support creation, discovery, open, and deletion. */
|
|
94
|
+
export function createSessionRepoLifecycleConformance(
|
|
95
|
+
// Require only the methods this group exercises so partial backends can run it independently.
|
|
96
|
+
backendFactory, onClose) {
|
|
97
|
+
const factory = prepareRepoCaseFactory(backendFactory, onClose);
|
|
98
|
+
return [
|
|
99
|
+
createCase(factory, "lifecycle", "creates a session with no implicit branch and rejects duplicate ids", async ({ repo }) => {
|
|
100
|
+
const session = await repo.create({ id: "session" }, BACKGROUND_CONTEXT);
|
|
101
|
+
strictEqual(session.metadata.id, "session");
|
|
102
|
+
strictEqual(Number.isSafeInteger(session.metadata.createdAt), true);
|
|
103
|
+
strictEqual(session.metadata.storageVersion, 1);
|
|
104
|
+
strictEqual(await session.branch("main", BACKGROUND_CONTEXT), undefined);
|
|
105
|
+
strictEqual(await session.getValue(laneState("main"), BACKGROUND_CONTEXT), undefined);
|
|
106
|
+
strictEqual(await session.getValue(laneConfig("main"), BACKGROUND_CONTEXT), undefined);
|
|
107
|
+
await rejects(repo.create({ id: "session" }, BACKGROUND_CONTEXT));
|
|
108
|
+
await session.close(BACKGROUND_CONTEXT);
|
|
109
|
+
}),
|
|
110
|
+
createCase(factory, "lifecycle", "close drains an acquired scope and rejects a queued mutation callback", async ({ repo }) => {
|
|
111
|
+
const session = await repo.create({ id: "session" }, BACKGROUND_CONTEXT);
|
|
112
|
+
const active = await session.beginMutation(BACKGROUND_CONTEXT);
|
|
113
|
+
let queuedStarted = false;
|
|
114
|
+
const queued = session.mutate(() => {
|
|
115
|
+
queuedStarted = true;
|
|
116
|
+
}, BACKGROUND_CONTEXT);
|
|
117
|
+
void queued.catch(() => { });
|
|
118
|
+
const closing = session.close(BACKGROUND_CONTEXT);
|
|
119
|
+
await active.end(BACKGROUND_CONTEXT);
|
|
120
|
+
await rejects(queued);
|
|
121
|
+
await closing;
|
|
122
|
+
strictEqual(queuedStarted, false);
|
|
123
|
+
}),
|
|
124
|
+
createCase(factory, "lifecycle", "lists metadata and preserves state across close and reopen", async ({ repo }) => {
|
|
125
|
+
const first = await repo.create({ id: "first" }, BACKGROUND_CONTEXT);
|
|
126
|
+
await first.setName("preserved", BACKGROUND_CONTEXT);
|
|
127
|
+
const second = await repo.create({ id: "second", parentSessionId: "parent" }, BACKGROUND_CONTEXT);
|
|
128
|
+
await second.close(BACKGROUND_CONTEXT);
|
|
129
|
+
const listed = await repo.list(undefined, BACKGROUND_CONTEXT);
|
|
130
|
+
deepStrictEqual(listed
|
|
131
|
+
.map(({ id, parentSessionId }) => ({ id, parentSessionId }))
|
|
132
|
+
.sort((left, right) => left.id.localeCompare(right.id)), [
|
|
133
|
+
{ id: "first", parentSessionId: undefined },
|
|
134
|
+
{ id: "second", parentSessionId: "parent" },
|
|
135
|
+
]);
|
|
136
|
+
await first.close(BACKGROUND_CONTEXT);
|
|
137
|
+
await rejects(first.getName(BACKGROUND_CONTEXT));
|
|
138
|
+
const reopened = await repo.open(first.metadata, BACKGROUND_CONTEXT);
|
|
139
|
+
strictEqual(reopened === first, false);
|
|
140
|
+
strictEqual(await reopened.getName(BACKGROUND_CONTEXT), "preserved");
|
|
141
|
+
await reopened.close(BACKGROUND_CONTEXT);
|
|
142
|
+
}),
|
|
143
|
+
createCase(factory, "lifecycle", "deletes closed sessions without affecting other sessions", async ({ repo }) => {
|
|
144
|
+
const removed = await repo.create({ id: "removed" }, BACKGROUND_CONTEXT);
|
|
145
|
+
const retained = await repo.create({ id: "retained" }, BACKGROUND_CONTEXT);
|
|
146
|
+
await Promise.all([removed.close(BACKGROUND_CONTEXT), retained.close(BACKGROUND_CONTEXT)]);
|
|
147
|
+
await repo.delete(removed.metadata, BACKGROUND_CONTEXT);
|
|
148
|
+
deepStrictEqual((await repo.list(undefined, BACKGROUND_CONTEXT)).map(({ id }) => id), ["retained"]);
|
|
149
|
+
await rejects(repo.open(removed.metadata, BACKGROUND_CONTEXT));
|
|
150
|
+
await rejects(repo.delete(removed.metadata, BACKGROUND_CONTEXT));
|
|
151
|
+
}),
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
/** Creates exclusive-open cases for repositories that own active session handles. */
|
|
155
|
+
export function createSessionRepoOwnershipConformance(backendFactory, onClose) {
|
|
156
|
+
const factory = prepareRepoCaseFactory(backendFactory, onClose);
|
|
157
|
+
return [
|
|
158
|
+
createCase(factory, "ownership", "rejects opening an already-open session", async ({ repo }) => {
|
|
159
|
+
const session = await repo.create({ id: "session" }, BACKGROUND_CONTEXT);
|
|
160
|
+
await rejects(repo.open(session.metadata, BACKGROUND_CONTEXT));
|
|
161
|
+
await session.close(BACKGROUND_CONTEXT);
|
|
162
|
+
const reopened = await repo.open(session.metadata, BACKGROUND_CONTEXT);
|
|
163
|
+
await rejects(repo.open(session.metadata, BACKGROUND_CONTEXT));
|
|
164
|
+
await reopened.close(BACKGROUND_CONTEXT);
|
|
165
|
+
}),
|
|
166
|
+
];
|
|
167
|
+
}
|
|
168
|
+
/** Creates message cases for repositories that support session creation. */
|
|
169
|
+
export function createSessionRepoMessageConformance(backendFactory, onClose) {
|
|
170
|
+
const factory = prepareRepoCaseFactory(backendFactory, onClose);
|
|
171
|
+
return [
|
|
172
|
+
createCase(factory, "messages", "rejects pending assistant messages without changing the tree", async ({ repo }) => {
|
|
173
|
+
const session = await repo.create({ id: "session" }, BACKGROUND_CONTEXT);
|
|
174
|
+
const branch = await session.createBranch("main", null, BACKGROUND_CONTEXT);
|
|
175
|
+
await rejects(branch.appendMessage(assistantMessage("pending"), BACKGROUND_CONTEXT));
|
|
176
|
+
strictEqual(await getBranchTip(session), null);
|
|
177
|
+
deepStrictEqual(await session.findEntries(undefined, BACKGROUND_CONTEXT), []);
|
|
178
|
+
await session.close(BACKGROUND_CONTEXT);
|
|
179
|
+
}),
|
|
180
|
+
createCase(factory, "messages", "preserves every settled assistant stop reason", async ({ repo }) => {
|
|
181
|
+
const session = await repo.create({ id: "session" }, BACKGROUND_CONTEXT);
|
|
182
|
+
const messagesByStopReason = {
|
|
183
|
+
stop: assistantMessage("stop"),
|
|
184
|
+
length: assistantMessage("length"),
|
|
185
|
+
toolUse: assistantMessage("toolUse"),
|
|
186
|
+
error: assistantMessage("error"),
|
|
187
|
+
aborted: assistantMessage("aborted"),
|
|
188
|
+
deferred: assistantMessage("deferred"),
|
|
189
|
+
};
|
|
190
|
+
const branch = await session.createBranch("main", null, BACKGROUND_CONTEXT);
|
|
191
|
+
const messages = Object.values(messagesByStopReason);
|
|
192
|
+
const ids = [];
|
|
193
|
+
for (const message of messages)
|
|
194
|
+
ids.push(await branch.appendMessage(message, BACKGROUND_CONTEXT));
|
|
195
|
+
const entries = await session.findEntries({ order: "asc", type: "message" }, BACKGROUND_CONTEXT);
|
|
196
|
+
deepStrictEqual(entries.map((entry) => entry.id), ids);
|
|
197
|
+
for (const [index, entry] of entries.entries()) {
|
|
198
|
+
if (entry.type !== "message")
|
|
199
|
+
throw new Error("Expected message entry");
|
|
200
|
+
deepStrictEqual(entry.message, messages[index]);
|
|
201
|
+
}
|
|
202
|
+
strictEqual(await getBranchTip(session), ids.at(-1));
|
|
203
|
+
await session.close(BACKGROUND_CONTEXT);
|
|
204
|
+
}),
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
/** Creates fork-content cases that do not require concurrent repository coordination. */
|
|
208
|
+
export function createSessionRepoForkBehaviorConformance(backendFactory, onClose) {
|
|
209
|
+
const factory = prepareRepoCaseFactory(backendFactory, onClose);
|
|
210
|
+
return [
|
|
211
|
+
createCase(factory, "forks", "tree-forks a fresh session before first attachment", async ({ repo }) => {
|
|
212
|
+
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
|
|
213
|
+
const fork = await repo.fork(source.metadata, { id: "fork", scope: "tree" }, BACKGROUND_CONTEXT);
|
|
214
|
+
strictEqual(fork.metadata.id, "fork");
|
|
215
|
+
strictEqual(fork.metadata.parentSessionId, "source");
|
|
216
|
+
strictEqual(await fork.branch("main", BACKGROUND_CONTEXT), undefined);
|
|
217
|
+
strictEqual(await fork.getValue(laneConfig("main"), BACKGROUND_CONTEXT), undefined);
|
|
218
|
+
strictEqual(await fork.getValue(laneState("main"), BACKGROUND_CONTEXT), undefined);
|
|
219
|
+
deepStrictEqual(await fork.findEntries(undefined, BACKGROUND_CONTEXT), []);
|
|
220
|
+
deepStrictEqual(await fork.getStats(BACKGROUND_CONTEXT), {
|
|
221
|
+
messageCount: 0,
|
|
222
|
+
usage: {
|
|
223
|
+
input: 0,
|
|
224
|
+
output: 0,
|
|
225
|
+
cacheRead: 0,
|
|
226
|
+
cacheWrite: 0,
|
|
227
|
+
totalTokens: 0,
|
|
228
|
+
cost: {
|
|
229
|
+
input: 0,
|
|
230
|
+
output: 0,
|
|
231
|
+
cacheRead: 0,
|
|
232
|
+
cacheWrite: 0,
|
|
233
|
+
total: 0,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
await Promise.all([source.close(BACKGROUND_CONTEXT), fork.close(BACKGROUND_CONTEXT)]);
|
|
238
|
+
}),
|
|
239
|
+
createCase(factory, "forks", "rejects a data-only branch and releases its destination id", async ({ repo }) => {
|
|
240
|
+
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
|
|
241
|
+
await source.createBranch("data", null, BACKGROUND_CONTEXT);
|
|
242
|
+
await rejects(repo.fork(source.metadata, { id: "destination", scope: "branch", branch: "data" }, BACKGROUND_CONTEXT));
|
|
243
|
+
deepStrictEqual((await repo.list(undefined, BACKGROUND_CONTEXT)).map(({ id }) => id), ["source"]);
|
|
244
|
+
const destination = await repo.create({ id: "destination" }, BACKGROUND_CONTEXT);
|
|
245
|
+
await Promise.all([source.close(BACKGROUND_CONTEXT), destination.close(BACKGROUND_CONTEXT)]);
|
|
246
|
+
}),
|
|
247
|
+
createCase(factory, "forks", "forks one named configured branch with scoped values and a zero ledger", async ({ repo }) => {
|
|
248
|
+
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
|
|
249
|
+
await source.mutate((mutator) => mutator.commit([
|
|
250
|
+
insertEntry({
|
|
251
|
+
id: ROOT_ID,
|
|
252
|
+
parentId: null,
|
|
253
|
+
type: "custom",
|
|
254
|
+
customType: "root",
|
|
255
|
+
}),
|
|
256
|
+
insertEntry({
|
|
257
|
+
id: CHILD_ID,
|
|
258
|
+
parentId: ROOT_ID,
|
|
259
|
+
type: "message",
|
|
260
|
+
message: { role: "user", content: "child", timestamp: 1 },
|
|
261
|
+
}),
|
|
262
|
+
insertEntry({
|
|
263
|
+
id: SIBLING_ID,
|
|
264
|
+
parentId: ROOT_ID,
|
|
265
|
+
type: "custom",
|
|
266
|
+
customType: "sibling",
|
|
267
|
+
}),
|
|
268
|
+
setValue(branchTip("main"), SIBLING_ID),
|
|
269
|
+
setValue(branchTip("review"), CHILD_ID),
|
|
270
|
+
setValue(laneConfig("review"), configuration),
|
|
271
|
+
setValue(laneState("review"), {
|
|
272
|
+
currentOperationId: OPERATION_ID,
|
|
273
|
+
lastOperationId: "previous",
|
|
274
|
+
inbox: [{ entryId: PENDING_ID, kind: "write" }],
|
|
275
|
+
}),
|
|
276
|
+
setValue(operationResult("previous"), {
|
|
277
|
+
operationId: "previous",
|
|
278
|
+
kind: "navigation",
|
|
279
|
+
status: "completed",
|
|
280
|
+
fromTipId: ROOT_ID,
|
|
281
|
+
tipId: CHILD_ID,
|
|
282
|
+
startedAt: 1,
|
|
283
|
+
endedAt: 2,
|
|
284
|
+
}),
|
|
285
|
+
setValue(sessionName, "source name"),
|
|
286
|
+
setValue(applicationValue, { copied: false }),
|
|
287
|
+
appendList(applicationList, { copied: false }),
|
|
288
|
+
setValue(entryLabel(ROOT_ID), "root label"),
|
|
289
|
+
setValue(entryLabel(SIBLING_ID), "sibling label"),
|
|
290
|
+
setValue(pendingEntry(PENDING_ID), {
|
|
291
|
+
type: "custom",
|
|
292
|
+
customType: "pending",
|
|
293
|
+
}),
|
|
294
|
+
setValue(operationMeta(OPERATION_ID), {
|
|
295
|
+
operationId: OPERATION_ID,
|
|
296
|
+
lane: "review",
|
|
297
|
+
sourceTipId: CHILD_ID,
|
|
298
|
+
startedAt: 1,
|
|
299
|
+
intent: { kind: "compaction" },
|
|
300
|
+
}),
|
|
301
|
+
setValue(operationState(OPERATION_ID), {
|
|
302
|
+
at: "summary.deciding",
|
|
303
|
+
control: { status: "running" },
|
|
304
|
+
settings: {
|
|
305
|
+
compaction: { enabled: true, reserveTokens: 1, keepRecentTokens: 1 },
|
|
306
|
+
steeringMode: "all",
|
|
307
|
+
followUpMode: "all",
|
|
308
|
+
toolExecution: "sequential",
|
|
309
|
+
},
|
|
310
|
+
latestAssistantEntryId: null,
|
|
311
|
+
task: { taskId: OPERATION_ID, reason: "manual", boundary: { kind: "finish" } },
|
|
312
|
+
}),
|
|
313
|
+
setValue(operationToolArgs(OPERATION_ID, ROOT_ID, 0), {
|
|
314
|
+
argument: true,
|
|
315
|
+
}),
|
|
316
|
+
setValue(operationPreparation(OPERATION_ID, OPERATION_ID), {
|
|
317
|
+
kind: "compaction",
|
|
318
|
+
messagesToSummarize: [],
|
|
319
|
+
turnPrefixMessages: [],
|
|
320
|
+
retainedTail: [],
|
|
321
|
+
isSplitTurn: false,
|
|
322
|
+
tokensBefore: 0,
|
|
323
|
+
fileOps: { read: [], written: [], edited: [] },
|
|
324
|
+
settings: {
|
|
325
|
+
enabled: true,
|
|
326
|
+
reserveTokens: 1,
|
|
327
|
+
keepRecentTokens: 1,
|
|
328
|
+
},
|
|
329
|
+
}),
|
|
330
|
+
insertUsage(usageRow()),
|
|
331
|
+
], BACKGROUND_CONTEXT), BACKGROUND_CONTEXT);
|
|
332
|
+
const fork = await repo.fork(source.metadata, { id: "fork", scope: "branch", branch: "review", entryId: CHILD_ID, position: "at" }, BACKGROUND_CONTEXT);
|
|
333
|
+
deepStrictEqual((await fork.findEntries({ order: "asc" }, BACKGROUND_CONTEXT)).map(({ id }) => id), [ROOT_ID, CHILD_ID]);
|
|
334
|
+
strictEqual(await fork.branch("main", BACKGROUND_CONTEXT), undefined);
|
|
335
|
+
strictEqual(await getBranchTip(fork, "review"), CHILD_ID);
|
|
336
|
+
deepStrictEqual((await fork.getValue(laneConfig("review"), BACKGROUND_CONTEXT))?.value, configuration);
|
|
337
|
+
deepStrictEqual((await fork.getValue(laneState("review"), BACKGROUND_CONTEXT))?.value, idleLaneState);
|
|
338
|
+
strictEqual(await fork.getValue(laneConfig("main"), BACKGROUND_CONTEXT), undefined);
|
|
339
|
+
strictEqual(await fork.getValue(laneState("main"), BACKGROUND_CONTEXT), undefined);
|
|
340
|
+
strictEqual(await fork.getName(BACKGROUND_CONTEXT), "source name");
|
|
341
|
+
strictEqual(await fork.getValue(applicationValue, BACKGROUND_CONTEXT), undefined);
|
|
342
|
+
deepStrictEqual(await fork.readList(applicationList, undefined, BACKGROUND_CONTEXT), []);
|
|
343
|
+
strictEqual(await fork.getLabel(ROOT_ID, BACKGROUND_CONTEXT), "root label");
|
|
344
|
+
strictEqual(await fork.getLabel(SIBLING_ID, BACKGROUND_CONTEXT), undefined);
|
|
345
|
+
strictEqual(await fork.getValue(operationResult("previous"), BACKGROUND_CONTEXT), undefined);
|
|
346
|
+
strictEqual(await fork.getValue(pendingEntry(PENDING_ID), BACKGROUND_CONTEXT), undefined);
|
|
347
|
+
strictEqual(await fork.getValue(operationMeta(OPERATION_ID), BACKGROUND_CONTEXT), undefined);
|
|
348
|
+
strictEqual(await fork.getValue(operationState(OPERATION_ID), BACKGROUND_CONTEXT), undefined);
|
|
349
|
+
strictEqual(await fork.getValue(operationToolArgs(OPERATION_ID, ROOT_ID, 0), BACKGROUND_CONTEXT), undefined);
|
|
350
|
+
strictEqual(await fork.getValue(operationPreparation(OPERATION_ID, OPERATION_ID), BACKGROUND_CONTEXT), undefined);
|
|
351
|
+
const stats = await fork.getStats(BACKGROUND_CONTEXT);
|
|
352
|
+
strictEqual(stats.messageCount, 1);
|
|
353
|
+
deepStrictEqual(stats.usage, {
|
|
354
|
+
input: 0,
|
|
355
|
+
output: 0,
|
|
356
|
+
cacheRead: 0,
|
|
357
|
+
cacheWrite: 0,
|
|
358
|
+
totalTokens: 0,
|
|
359
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
360
|
+
});
|
|
361
|
+
await Promise.all([source.close(BACKGROUND_CONTEXT), fork.close(BACKGROUND_CONTEXT)]);
|
|
362
|
+
}),
|
|
363
|
+
createCase(factory, "forks", "enforces branch ancestry for at and before placement", async ({ repo }) => {
|
|
364
|
+
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
|
|
365
|
+
await source.mutate((mutator) => mutator.commit([
|
|
366
|
+
insertEntry({ id: ROOT_ID, parentId: null, type: "custom", customType: "root" }),
|
|
367
|
+
insertEntry({ id: CHILD_ID, parentId: ROOT_ID, type: "custom", customType: "child" }),
|
|
368
|
+
insertEntry({ id: SIBLING_ID, parentId: ROOT_ID, type: "custom", customType: "sibling" }),
|
|
369
|
+
setValue(branchTip("main"), CHILD_ID),
|
|
370
|
+
setValue(laneConfig("main"), configuration),
|
|
371
|
+
setValue(laneState("main"), idleLaneState),
|
|
372
|
+
setValue(branchTip("empty"), null),
|
|
373
|
+
setValue(laneConfig("empty"), configuration),
|
|
374
|
+
setValue(laneState("empty"), idleLaneState),
|
|
375
|
+
], BACKGROUND_CONTEXT), BACKGROUND_CONTEXT);
|
|
376
|
+
const before = await repo.fork(source.metadata, { id: "before", scope: "branch", branch: "main", entryId: CHILD_ID, position: "before" }, BACKGROUND_CONTEXT);
|
|
377
|
+
strictEqual(await getBranchTip(before), ROOT_ID);
|
|
378
|
+
deepStrictEqual((await before.findEntries({ order: "asc" }, BACKGROUND_CONTEXT)).map(({ id }) => id), [ROOT_ID]);
|
|
379
|
+
const mid = await repo.fork(source.metadata, { id: "mid", scope: "branch", branch: "main", entryId: ROOT_ID, position: "at" }, BACKGROUND_CONTEXT);
|
|
380
|
+
strictEqual(await getBranchTip(mid), ROOT_ID);
|
|
381
|
+
deepStrictEqual((await mid.findEntries({ order: "asc" }, BACKGROUND_CONTEXT)).map(({ id }) => id), [ROOT_ID]);
|
|
382
|
+
const beforeRoot = await repo.fork(source.metadata, { id: "before-root", scope: "branch", branch: "main", entryId: ROOT_ID, position: "before" }, BACKGROUND_CONTEXT);
|
|
383
|
+
strictEqual(await getBranchTip(beforeRoot), null);
|
|
384
|
+
deepStrictEqual(await beforeRoot.findEntries({ order: "asc" }, BACKGROUND_CONTEXT), []);
|
|
385
|
+
const empty = await repo.fork(source.metadata, { id: "empty", scope: "branch", branch: "empty" }, BACKGROUND_CONTEXT);
|
|
386
|
+
strictEqual(await getBranchTip(empty, "empty"), null);
|
|
387
|
+
deepStrictEqual(await empty.findEntries({ order: "asc" }, BACKGROUND_CONTEXT), []);
|
|
388
|
+
for (const [id, branch, entryId] of [
|
|
389
|
+
["off-branch", "main", SIBLING_ID],
|
|
390
|
+
["unknown", "main", UNKNOWN_ID],
|
|
391
|
+
["null-tip", "empty", ROOT_ID],
|
|
392
|
+
]) {
|
|
393
|
+
await rejects(repo.fork(source.metadata, { id, scope: "branch", branch, entryId }, BACKGROUND_CONTEXT));
|
|
394
|
+
}
|
|
395
|
+
deepStrictEqual((await repo.list(undefined, BACKGROUND_CONTEXT)).map(({ id }) => id).sort(), [
|
|
396
|
+
"before",
|
|
397
|
+
"before-root",
|
|
398
|
+
"empty",
|
|
399
|
+
"mid",
|
|
400
|
+
"source",
|
|
401
|
+
]);
|
|
402
|
+
await Promise.all([
|
|
403
|
+
source.close(BACKGROUND_CONTEXT),
|
|
404
|
+
before.close(BACKGROUND_CONTEXT),
|
|
405
|
+
beforeRoot.close(BACKGROUND_CONTEXT),
|
|
406
|
+
empty.close(BACKGROUND_CONTEXT),
|
|
407
|
+
mid.close(BACKGROUND_CONTEXT),
|
|
408
|
+
]);
|
|
409
|
+
}),
|
|
410
|
+
createCase(factory, "forks", "forks a closed source session", async ({ repo }) => {
|
|
411
|
+
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
|
|
412
|
+
await source.mutate((mutator) => mutator.commit([
|
|
413
|
+
insertEntry({
|
|
414
|
+
id: ROOT_ID,
|
|
415
|
+
parentId: null,
|
|
416
|
+
type: "custom",
|
|
417
|
+
customType: "root",
|
|
418
|
+
}),
|
|
419
|
+
setValue(branchTip("main"), ROOT_ID),
|
|
420
|
+
setValue(laneConfig("main"), configuration),
|
|
421
|
+
setValue(laneState("main"), idleLaneState),
|
|
422
|
+
setValue(applicationValue, "excluded"),
|
|
423
|
+
appendList(applicationList, "excluded"),
|
|
424
|
+
], BACKGROUND_CONTEXT), BACKGROUND_CONTEXT);
|
|
425
|
+
await source.close(BACKGROUND_CONTEXT);
|
|
426
|
+
const fork = await repo.fork(source.metadata, { id: "fork", scope: "branch", branch: "main" }, BACKGROUND_CONTEXT);
|
|
427
|
+
strictEqual(await getBranchTip(fork), ROOT_ID);
|
|
428
|
+
deepStrictEqual((await fork.getValue(laneConfig("main"), BACKGROUND_CONTEXT))?.value, configuration);
|
|
429
|
+
deepStrictEqual((await fork.getValue(laneState("main"), BACKGROUND_CONTEXT))?.value, idleLaneState);
|
|
430
|
+
strictEqual(await fork.getValue(applicationValue, BACKGROUND_CONTEXT), undefined);
|
|
431
|
+
deepStrictEqual(await fork.readList(applicationList, undefined, BACKGROUND_CONTEXT), []);
|
|
432
|
+
await fork.close(BACKGROUND_CONTEXT);
|
|
433
|
+
}),
|
|
434
|
+
createCase(factory, "forks", "forks the whole configured tree with fresh lane state", async ({ repo }) => {
|
|
435
|
+
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
|
|
436
|
+
await source.mutate((mutator) => mutator.commit([
|
|
437
|
+
insertEntry({
|
|
438
|
+
id: ROOT_ID,
|
|
439
|
+
parentId: null,
|
|
440
|
+
type: "custom",
|
|
441
|
+
customType: "root",
|
|
442
|
+
}),
|
|
443
|
+
insertEntry({
|
|
444
|
+
id: CHILD_ID,
|
|
445
|
+
parentId: ROOT_ID,
|
|
446
|
+
type: "custom",
|
|
447
|
+
customType: "child",
|
|
448
|
+
}),
|
|
449
|
+
insertEntry({
|
|
450
|
+
id: SIBLING_ID,
|
|
451
|
+
parentId: ROOT_ID,
|
|
452
|
+
type: "custom",
|
|
453
|
+
customType: "sibling",
|
|
454
|
+
}),
|
|
455
|
+
setValue(branchTip("main"), CHILD_ID),
|
|
456
|
+
setValue(laneConfig("main"), configuration),
|
|
457
|
+
setValue(laneState("main"), idleLaneState),
|
|
458
|
+
setValue(branchTip("review"), SIBLING_ID),
|
|
459
|
+
setValue(laneConfig("review"), configuration),
|
|
460
|
+
setValue(laneState("review"), idleLaneState),
|
|
461
|
+
setValue(branchTip("notes"), ROOT_ID),
|
|
462
|
+
setValue(applicationValue, { copied: true }),
|
|
463
|
+
], BACKGROUND_CONTEXT), BACKGROUND_CONTEXT);
|
|
464
|
+
const fork = await repo.fork(source.metadata, { id: "fork", scope: "tree" }, BACKGROUND_CONTEXT);
|
|
465
|
+
deepStrictEqual((await fork.findEntries({ order: "asc" }, BACKGROUND_CONTEXT)).map(({ id }) => id), [ROOT_ID, CHILD_ID, SIBLING_ID]);
|
|
466
|
+
strictEqual(await getBranchTip(fork), CHILD_ID);
|
|
467
|
+
strictEqual(await getBranchTip(fork, "review"), SIBLING_ID);
|
|
468
|
+
strictEqual(await getBranchTip(fork, "notes"), ROOT_ID);
|
|
469
|
+
strictEqual(await fork.getValue(laneConfig("notes"), BACKGROUND_CONTEXT), undefined);
|
|
470
|
+
strictEqual(await fork.getValue(laneState("notes"), BACKGROUND_CONTEXT), undefined);
|
|
471
|
+
deepStrictEqual((await fork.getValue(laneConfig("review"), BACKGROUND_CONTEXT))?.value, configuration);
|
|
472
|
+
deepStrictEqual((await fork.getValue(laneState("review"), BACKGROUND_CONTEXT))?.value, idleLaneState);
|
|
473
|
+
deepStrictEqual((await fork.getValue(applicationValue, BACKGROUND_CONTEXT))?.value, { copied: true });
|
|
474
|
+
await Promise.all([source.close(BACKGROUND_CONTEXT), fork.close(BACKGROUND_CONTEXT)]);
|
|
475
|
+
}),
|
|
476
|
+
createCase(factory, "forks", "rejects only surviving unknown reserved scalar state", async ({ repo }) => {
|
|
477
|
+
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
|
|
478
|
+
await source.createBranch("main", null, BACKGROUND_CONTEXT);
|
|
479
|
+
await source.mutate((mutator) => mutator.commit([setValue(laneConfig("main"), configuration), setValue(laneState("main"), idleLaneState)], BACKGROUND_CONTEXT), BACKGROUND_CONTEXT);
|
|
480
|
+
for (const namespace of ["pi", "pi.unknown"]) {
|
|
481
|
+
const address = value(namespace);
|
|
482
|
+
await source.setValue(address, true, BACKGROUND_CONTEXT);
|
|
483
|
+
await rejects(repo.fork(source.metadata, { id: "tree", scope: "tree" }, BACKGROUND_CONTEXT));
|
|
484
|
+
await rejects(repo.fork(source.metadata, { id: "branch", scope: "branch", branch: "main" }, BACKGROUND_CONTEXT));
|
|
485
|
+
await source.deleteValue(address, BACKGROUND_CONTEXT);
|
|
486
|
+
}
|
|
487
|
+
await source.close(BACKGROUND_CONTEXT);
|
|
488
|
+
const tree = await repo.fork(source.metadata, { id: "tree", scope: "tree" }, BACKGROUND_CONTEXT);
|
|
489
|
+
const branch = await repo.fork(source.metadata, { id: "branch", scope: "branch", branch: "main" }, BACKGROUND_CONTEXT);
|
|
490
|
+
await Promise.all([tree.close(BACKGROUND_CONTEXT), branch.close(BACKGROUND_CONTEXT)]);
|
|
491
|
+
}),
|
|
492
|
+
];
|
|
493
|
+
}
|
|
494
|
+
/** Creates fork cases that require destination reservation across create and fork. */
|
|
495
|
+
export function createSessionRepoForkDestinationReservationConformance(backendFactory, onClose) {
|
|
496
|
+
const factory = prepareRepoCaseFactory(backendFactory, onClose);
|
|
497
|
+
return [
|
|
498
|
+
createCase(factory, "fork coordination", "publishes create when it reserves a shared destination id first", async ({ repo }) => {
|
|
499
|
+
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
|
|
500
|
+
const results = await Promise.allSettled([
|
|
501
|
+
repo.create({ id: "destination" }, BACKGROUND_CONTEXT),
|
|
502
|
+
repo.fork(source.metadata, { id: "destination", scope: "tree" }, BACKGROUND_CONTEXT),
|
|
503
|
+
]);
|
|
504
|
+
strictEqual(results[0].status, "fulfilled");
|
|
505
|
+
strictEqual(results[1].status, "rejected");
|
|
506
|
+
if (results[0].status === "fulfilled")
|
|
507
|
+
await results[0].value.close(BACKGROUND_CONTEXT);
|
|
508
|
+
await source.close(BACKGROUND_CONTEXT);
|
|
509
|
+
}),
|
|
510
|
+
createCase(factory, "fork coordination", "publishes fork when it reserves a shared destination id first", async ({ repo }) => {
|
|
511
|
+
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
|
|
512
|
+
const results = await Promise.allSettled([
|
|
513
|
+
repo.fork(source.metadata, { id: "destination", scope: "tree" }, BACKGROUND_CONTEXT),
|
|
514
|
+
repo.create({ id: "destination" }, BACKGROUND_CONTEXT),
|
|
515
|
+
]);
|
|
516
|
+
strictEqual(results[0].status, "fulfilled");
|
|
517
|
+
strictEqual(results[1].status, "rejected");
|
|
518
|
+
if (results[0].status === "fulfilled")
|
|
519
|
+
await results[0].value.close(BACKGROUND_CONTEXT);
|
|
520
|
+
await source.close(BACKGROUND_CONTEXT);
|
|
521
|
+
}),
|
|
522
|
+
];
|
|
523
|
+
}
|
|
524
|
+
/** Creates fork cases that require a snapshot boundary on an active source storage queue. */
|
|
525
|
+
export function createSessionRepoForkSourceSnapshotConformance(backendFactory, onClose) {
|
|
526
|
+
const factory = prepareRepoCaseFactory(backendFactory, onClose);
|
|
527
|
+
return [
|
|
528
|
+
createCase(factory, "fork coordination", "captures one coherent boundary between source commits", async ({ repo }) => {
|
|
529
|
+
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
|
|
530
|
+
const firstMutation = await source.beginMutation(BACKGROUND_CONTEXT);
|
|
531
|
+
const firstCommit = firstMutation.commit([
|
|
532
|
+
insertEntry({
|
|
533
|
+
id: ROOT_ID,
|
|
534
|
+
parentId: null,
|
|
535
|
+
type: "custom",
|
|
536
|
+
customType: "first",
|
|
537
|
+
}),
|
|
538
|
+
setValue(branchTip("main"), ROOT_ID),
|
|
539
|
+
setValue(laneConfig("main"), configuration),
|
|
540
|
+
setValue(laneState("main"), idleLaneState),
|
|
541
|
+
setValue(sessionName, "first name"),
|
|
542
|
+
setValue(entryLabel(ROOT_ID), "first label"),
|
|
543
|
+
], BACKGROUND_CONTEXT);
|
|
544
|
+
const fork = repo.fork(source.metadata, { id: "fork", scope: "branch", branch: "main" }, BACKGROUND_CONTEXT);
|
|
545
|
+
const secondCommit = source.mutate((mutator) => mutator.commit([
|
|
546
|
+
insertEntry({
|
|
547
|
+
id: CHILD_ID,
|
|
548
|
+
parentId: ROOT_ID,
|
|
549
|
+
type: "custom",
|
|
550
|
+
customType: "second",
|
|
551
|
+
}),
|
|
552
|
+
setValue(branchTip("main"), CHILD_ID),
|
|
553
|
+
setValue(sessionName, "second name"),
|
|
554
|
+
setValue(entryLabel(ROOT_ID), "second label"),
|
|
555
|
+
], BACKGROUND_CONTEXT), BACKGROUND_CONTEXT);
|
|
556
|
+
const [, forked] = await Promise.all([firstCommit, fork]);
|
|
557
|
+
await firstMutation.end(BACKGROUND_CONTEXT);
|
|
558
|
+
await secondCommit;
|
|
559
|
+
strictEqual(await getBranchTip(forked), ROOT_ID);
|
|
560
|
+
deepStrictEqual((await forked.findEntries({ order: "asc" }, BACKGROUND_CONTEXT)).map(({ id }) => id), [ROOT_ID]);
|
|
561
|
+
strictEqual(await forked.getName(BACKGROUND_CONTEXT), "first name");
|
|
562
|
+
strictEqual(await forked.getLabel(ROOT_ID, BACKGROUND_CONTEXT), "first label");
|
|
563
|
+
await Promise.all([source.close(BACKGROUND_CONTEXT), forked.close(BACKGROUND_CONTEXT)]);
|
|
564
|
+
}),
|
|
565
|
+
];
|
|
566
|
+
}
|
|
567
|
+
/** Creates every fork coordination case. */
|
|
568
|
+
export function createSessionRepoForkCoordinationConformance(backendFactory, onClose) {
|
|
569
|
+
return [
|
|
570
|
+
...createSessionRepoForkDestinationReservationConformance(backendFactory, onClose),
|
|
571
|
+
...createSessionRepoForkSourceSnapshotConformance(backendFactory, onClose),
|
|
572
|
+
];
|
|
573
|
+
}
|
|
574
|
+
/** Creates every fork conformance case. */
|
|
575
|
+
export function createSessionRepoForkConformance(backendFactory, onClose) {
|
|
576
|
+
return [
|
|
577
|
+
...createSessionRepoForkBehaviorConformance(backendFactory, onClose),
|
|
578
|
+
...createSessionRepoForkCoordinationConformance(backendFactory, onClose),
|
|
579
|
+
];
|
|
580
|
+
}
|
|
581
|
+
/** Creates every SessionRepo conformance case. */
|
|
582
|
+
export function createSessionRepoConformance(factory, onClose) {
|
|
583
|
+
return [
|
|
584
|
+
...createSessionRepoLifecycleConformance(factory, onClose),
|
|
585
|
+
...createSessionRepoOwnershipConformance(factory, onClose),
|
|
586
|
+
...createSessionRepoMessageConformance(factory, onClose),
|
|
587
|
+
...createSessionRepoForkConformance(factory, onClose),
|
|
588
|
+
];
|
|
589
|
+
}
|
|
590
|
+
//# sourceMappingURL=session-repo.js.map
|