@m4ike1/ion-agent-core 0.1.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 +622 -0
- package/dist/agent-loop.d.ts +24 -0
- package/dist/agent-loop.d.ts.map +1 -0
- package/dist/agent-loop.js +560 -0
- package/dist/agent-loop.js.map +1 -0
- package/dist/agent.d.ts +122 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +422 -0
- package/dist/agent.js.map +1 -0
- package/dist/harness/agent-harness.d.ts +716 -0
- package/dist/harness/agent-harness.d.ts.map +1 -0
- package/dist/harness/agent-harness.js +6 -0
- package/dist/harness/agent-harness.js.map +1 -0
- package/dist/harness/compaction/branch-summarization.d.ts +68 -0
- package/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/harness/compaction/branch-summarization.js +176 -0
- package/dist/harness/compaction/branch-summarization.js.map +1 -0
- package/dist/harness/compaction/compaction.d.ts +126 -0
- package/dist/harness/compaction/compaction.d.ts.map +1 -0
- package/dist/harness/compaction/compaction.js +560 -0
- package/dist/harness/compaction/compaction.js.map +1 -0
- package/dist/harness/compaction/utils.d.ts +25 -0
- package/dist/harness/compaction/utils.d.ts.map +1 -0
- package/dist/harness/compaction/utils.js +120 -0
- package/dist/harness/compaction/utils.js.map +1 -0
- package/dist/harness/config.d.ts +9 -0
- package/dist/harness/config.d.ts.map +1 -0
- package/dist/harness/config.js +35 -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 +43 -0
- package/dist/harness/env/nodejs.d.ts.map +1 -0
- package/dist/harness/env/nodejs.js +865 -0
- package/dist/harness/env/nodejs.js.map +1 -0
- package/dist/harness/events.d.ts +25 -0
- package/dist/harness/events.d.ts.map +1 -0
- package/dist/harness/events.js +250 -0
- package/dist/harness/events.js.map +1 -0
- 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 +51 -0
- package/dist/harness/messages.d.ts.map +1 -0
- package/dist/harness/messages.js +102 -0
- package/dist/harness/messages.js.map +1 -0
- package/dist/harness/prompt-templates.d.ts +49 -0
- package/dist/harness/prompt-templates.d.ts.map +1 -0
- package/dist/harness/prompt-templates.js +225 -0
- package/dist/harness/prompt-templates.js.map +1 -0
- package/dist/harness/result.d.ts +133 -0
- package/dist/harness/result.d.ts.map +1 -0
- package/dist/harness/result.js +81 -0
- package/dist/harness/result.js.map +1 -0
- 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 +173 -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 +34 -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 +10 -0
- package/dist/harness/session/context.d.ts.map +1 -0
- package/dist/harness/session/context.js +49 -0
- package/dist/harness/session/context.js.map +1 -0
- package/dist/harness/session/fork-policy.d.ts +21 -0
- package/dist/harness/session/fork-policy.d.ts.map +1 -0
- package/dist/harness/session/fork-policy.js +55 -0
- package/dist/harness/session/fork-policy.js.map +1 -0
- package/dist/harness/session/fork.d.ts +16 -0
- package/dist/harness/session/fork.d.ts.map +1 -0
- package/dist/harness/session/fork.js +79 -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 +276 -0
- package/dist/harness/session/in-memory-storage-state.js.map +1 -0
- package/dist/harness/session/index.d.ts +11 -0
- package/dist/harness/session/index.d.ts.map +1 -0
- package/dist/harness/session/index.js +9 -0
- package/dist/harness/session/index.js.map +1 -0
- package/dist/harness/session/jsonl/codec.d.ts +21 -0
- package/dist/harness/session/jsonl/codec.d.ts.map +1 -0
- package/dist/harness/session/jsonl/codec.js +45 -0
- package/dist/harness/session/jsonl/codec.js.map +1 -0
- package/dist/harness/session/jsonl/fork.d.ts +38 -0
- package/dist/harness/session/jsonl/fork.d.ts.map +1 -0
- package/dist/harness/session/jsonl/fork.js +264 -0
- package/dist/harness/session/jsonl/fork.js.map +1 -0
- 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/io.d.ts +14 -0
- package/dist/harness/session/jsonl/io.d.ts.map +1 -0
- package/dist/harness/session/jsonl/io.js +86 -0
- package/dist/harness/session/jsonl/io.js.map +1 -0
- package/dist/harness/session/jsonl/legacy-v3.d.ts +43 -0
- package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -0
- package/dist/harness/session/jsonl/legacy-v3.js +462 -0
- package/dist/harness/session/jsonl/legacy-v3.js.map +1 -0
- package/dist/harness/session/jsonl/repo.d.ts +35 -0
- package/dist/harness/session/jsonl/repo.d.ts.map +1 -0
- package/dist/harness/session/jsonl/repo.js +301 -0
- package/dist/harness/session/jsonl/repo.js.map +1 -0
- package/dist/harness/session/jsonl/storage.d.ts +40 -0
- package/dist/harness/session/jsonl/storage.d.ts.map +1 -0
- package/dist/harness/session/jsonl/storage.js +200 -0
- package/dist/harness/session/jsonl/storage.js.map +1 -0
- package/dist/harness/session/jsonl/types.d.ts +39 -0
- package/dist/harness/session/jsonl/types.d.ts.map +1 -0
- package/dist/harness/session/jsonl/types.js +3 -0
- package/dist/harness/session/jsonl/types.js.map +1 -0
- package/dist/harness/session/memory.d.ts +48 -0
- package/dist/harness/session/memory.d.ts.map +1 -0
- package/dist/harness/session/memory.js +369 -0
- package/dist/harness/session/memory.js.map +1 -0
- 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 +82 -0
- package/dist/harness/session/session.d.ts.map +1 -0
- package/dist/harness/session/session.js +361 -0
- package/dist/harness/session/session.js.map +1 -0
- 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 +23 -0
- package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -0
- package/dist/harness/session/testing/conformance/session-repo.js +809 -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 +10 -0
- package/dist/harness/session/testing/index.d.ts.map +1 -0
- package/dist/harness/session/testing/index.js +9 -0
- package/dist/harness/session/testing/index.js.map +1 -0
- 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 +12 -0
- package/dist/harness/session/testing/types.d.ts.map +1 -0
- package/dist/harness/session/testing/types.js +2 -0
- package/dist/harness/session/testing/types.js.map +1 -0
- package/dist/harness/session/types.d.ts +512 -0
- package/dist/harness/session/types.d.ts.map +1 -0
- package/dist/harness/session/types.js +9 -0
- package/dist/harness/session/types.js.map +1 -0
- 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 +46 -0
- package/dist/harness/skills.d.ts.map +1 -0
- package/dist/harness/skills.js +326 -0
- package/dist/harness/skills.js.map +1 -0
- package/dist/harness/system-prompt.d.ts +3 -0
- package/dist/harness/system-prompt.d.ts.map +1 -0
- package/dist/harness/system-prompt.js +30 -0
- package/dist/harness/system-prompt.js.map +1 -0
- package/dist/harness/telemetry.d.ts +1242 -0
- package/dist/harness/telemetry.d.ts.map +1 -0
- package/dist/harness/telemetry.js +527 -0
- package/dist/harness/telemetry.js.map +1 -0
- package/dist/harness/tools/bash.d.ts +27 -0
- package/dist/harness/tools/bash.d.ts.map +1 -0
- package/dist/harness/tools/bash.js +105 -0
- package/dist/harness/tools/bash.js.map +1 -0
- package/dist/harness/tools/edit-diff.d.ts +89 -0
- package/dist/harness/tools/edit-diff.d.ts.map +1 -0
- package/dist/harness/tools/edit-diff.js +386 -0
- package/dist/harness/tools/edit-diff.js.map +1 -0
- package/dist/harness/tools/edit.d.ts +19 -0
- package/dist/harness/tools/edit.d.ts.map +1 -0
- package/dist/harness/tools/edit.js +108 -0
- package/dist/harness/tools/edit.js.map +1 -0
- package/dist/harness/tools/file-mutation-queue.d.ts +5 -0
- package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/harness/tools/file-mutation-queue.js +46 -0
- package/dist/harness/tools/file-mutation-queue.js.map +1 -0
- package/dist/harness/tools/image.d.ts +3 -0
- package/dist/harness/tools/image.d.ts.map +1 -0
- package/dist/harness/tools/image.js +106 -0
- package/dist/harness/tools/image.js.map +1 -0
- package/dist/harness/tools/index.d.ts +6 -0
- package/dist/harness/tools/index.d.ts.map +1 -0
- package/dist/harness/tools/index.js +5 -0
- package/dist/harness/tools/index.js.map +1 -0
- package/dist/harness/tools/path-utils.d.ts +5 -0
- package/dist/harness/tools/path-utils.d.ts.map +1 -0
- package/dist/harness/tools/path-utils.js +26 -0
- package/dist/harness/tools/path-utils.js.map +1 -0
- package/dist/harness/tools/read.d.ts +35 -0
- package/dist/harness/tools/read.d.ts.map +1 -0
- package/dist/harness/tools/read.js +108 -0
- package/dist/harness/tools/read.js.map +1 -0
- package/dist/harness/tools/tool-context.d.ts +6 -0
- package/dist/harness/tools/tool-context.d.ts.map +1 -0
- package/dist/harness/tools/tool-context.js +2 -0
- package/dist/harness/tools/tool-context.js.map +1 -0
- package/dist/harness/tools/write.d.ts +11 -0
- package/dist/harness/tools/write.d.ts.map +1 -0
- package/dist/harness/tools/write.js +31 -0
- package/dist/harness/tools/write.js.map +1 -0
- package/dist/harness/types.d.ts +305 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +75 -0
- package/dist/harness/types.js.map +1 -0
- 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 +29 -0
- package/dist/harness/utils/shell-output.d.ts.map +1 -0
- package/dist/harness/utils/shell-output.js +71 -0
- package/dist/harness/utils/shell-output.js.map +1 -0
- package/dist/harness/utils/truncate.d.ts +71 -0
- package/dist/harness/utils/truncate.d.ts.map +1 -0
- package/dist/harness/utils/truncate.js +296 -0
- package/dist/harness/utils/truncate.js.map +1 -0
- 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 +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +3 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +3 -0
- package/dist/node.js.map +1 -0
- package/dist/proxy.d.ts +72 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +310 -0
- package/dist/proxy.js.map +1 -0
- package/dist/search/index.d.ts +29 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +2 -0
- package/dist/search/index.js.map +1 -0
- package/dist/stream-fn.d.ts +10 -0
- package/dist/stream-fn.d.ts.map +1 -0
- package/dist/stream-fn.js +17 -0
- package/dist/stream-fn.js.map +1 -0
- package/dist/types.d.ts +416 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { uuidv7 } from "@m4ike1/ion-ai/utils/uuid";
|
|
2
|
+
import { insertUsage } from "../commit.js";
|
|
3
|
+
import { InMemoryStorageState } from "../in-memory-storage-state.js";
|
|
4
|
+
import { fileValue, parseJsonlTransaction, publishFileAtomically, publishJsonl, readJsonlHeader, serializeJsonlTransaction, } from "./io.js";
|
|
5
|
+
import { LegacyV3Source } from "./legacy-v3.js";
|
|
6
|
+
import { JSONL_STORAGE_VERSION } from "./types.js";
|
|
7
|
+
function splitCompleteLines(content) {
|
|
8
|
+
if (content.endsWith("\n"))
|
|
9
|
+
return { lines: content.slice(0, -1).split("\n"), torn: false };
|
|
10
|
+
const lastNewline = content.lastIndexOf("\n");
|
|
11
|
+
if (lastNewline === -1)
|
|
12
|
+
return { lines: [], torn: true };
|
|
13
|
+
return { lines: content.slice(0, lastNewline).split("\n"), torn: true };
|
|
14
|
+
}
|
|
15
|
+
/** JSONL storage backed by an injected filesystem capability. */
|
|
16
|
+
export class JsonlStorage {
|
|
17
|
+
fileSystem;
|
|
18
|
+
path;
|
|
19
|
+
now;
|
|
20
|
+
header;
|
|
21
|
+
backing;
|
|
22
|
+
storageState = new InMemoryStorageState();
|
|
23
|
+
commitQueue = Promise.resolve();
|
|
24
|
+
state = "open";
|
|
25
|
+
closePromise;
|
|
26
|
+
constructor(options, header, backing) {
|
|
27
|
+
this.fileSystem = options.fileSystem;
|
|
28
|
+
this.path = options.path;
|
|
29
|
+
this.now = options.now ?? Date.now;
|
|
30
|
+
this.header = header;
|
|
31
|
+
this.backing = backing;
|
|
32
|
+
}
|
|
33
|
+
static async create(options, header, initialWrites, context) {
|
|
34
|
+
const storage = new JsonlStorage(options, header, { kind: "v4" });
|
|
35
|
+
const prepared = storage.storageState.prepareCommit(initialWrites, storage.now());
|
|
36
|
+
await publishJsonl(options.fileSystem, options.path, header, context, async (append) => {
|
|
37
|
+
if (prepared.writes.length !== 0)
|
|
38
|
+
await append(prepared.writes);
|
|
39
|
+
});
|
|
40
|
+
storage.storageState.applyValidated(prepared.writes);
|
|
41
|
+
return storage;
|
|
42
|
+
}
|
|
43
|
+
static async open(options, context) {
|
|
44
|
+
const reader = fileValue(await options.fileSystem.openTextLineReader(options.path, context), `Failed to read JSONL storage ${options.path}`);
|
|
45
|
+
const parsed = await readJsonlHeader(reader, options.path, context).finally(() => reader.close(context));
|
|
46
|
+
return parsed.format === "v3-legacy"
|
|
47
|
+
? JsonlStorage.openLegacyV3(options, context)
|
|
48
|
+
: JsonlStorage.openV4(options, parsed.header, context);
|
|
49
|
+
}
|
|
50
|
+
static async openV4(options, header, context) {
|
|
51
|
+
const content = fileValue(await options.fileSystem.readTextFile(options.path, context), `Failed to read JSONL storage ${options.path}`);
|
|
52
|
+
const { lines, torn } = splitCompleteLines(content);
|
|
53
|
+
if (header.storageVersion !== JSONL_STORAGE_VERSION) {
|
|
54
|
+
throw new Error(`Session ${header.id} uses unsupported storage version ${header.storageVersion}`);
|
|
55
|
+
}
|
|
56
|
+
const storage = new JsonlStorage(options, header, { kind: "v4" });
|
|
57
|
+
for (let index = 1; index < lines.length; index++) {
|
|
58
|
+
const line = lines[index];
|
|
59
|
+
try {
|
|
60
|
+
storage.replayCommitted(parseJsonlTransaction(line));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
throw new Error(`Invalid JSONL storage ${options.path}: line ${index + 1}`, { cause: error });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (header.nextSeq !== undefined)
|
|
67
|
+
storage.storageState.advanceNextSeq(header.nextSeq);
|
|
68
|
+
if (torn) {
|
|
69
|
+
await publishFileAtomically(options.fileSystem, options.path, context, (append) => append(`${lines.join("\n")}\n`));
|
|
70
|
+
}
|
|
71
|
+
return storage;
|
|
72
|
+
}
|
|
73
|
+
static async openLegacyV3(options, context) {
|
|
74
|
+
const source = await LegacyV3Source.read(options.fileSystem, options.path, context);
|
|
75
|
+
const storage = new JsonlStorage(options, { ...source.header, nextSeq: source.nextSeq }, { kind: "v3", source });
|
|
76
|
+
for await (const write of source.writes(context))
|
|
77
|
+
storage.replayCommitted([write]);
|
|
78
|
+
return storage;
|
|
79
|
+
}
|
|
80
|
+
replayCommitted(writes) {
|
|
81
|
+
this.storageState.validateCommitted(writes);
|
|
82
|
+
this.storageState.applyValidated(writes);
|
|
83
|
+
}
|
|
84
|
+
async commit(writes, context) {
|
|
85
|
+
if (this.state !== "open")
|
|
86
|
+
throw new Error("JsonlStorage is closed");
|
|
87
|
+
const result = this.commitQueue.then(() => this.applyCommit(writes, context));
|
|
88
|
+
this.commitQueue = result.then(() => undefined, () => undefined);
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
async applyCommit(writes, context) {
|
|
92
|
+
if (this.backing.kind === "v3" && writes.length !== 0) {
|
|
93
|
+
return this.upgradeLegacyV3ToV4(this.backing.source, writes, context);
|
|
94
|
+
}
|
|
95
|
+
const prepared = this.storageState.prepareCommit(writes, this.now());
|
|
96
|
+
if (prepared.writes.length !== 0) {
|
|
97
|
+
fileValue(await this.fileSystem.appendFile(this.path, `${serializeJsonlTransaction(prepared.writes)}\n`, context), `Failed to append JSONL storage ${this.path}`);
|
|
98
|
+
}
|
|
99
|
+
const stats = this.storageState.applyValidated(prepared.writes);
|
|
100
|
+
return { ...prepared.result, stats: this.withImportedUsage(stats) };
|
|
101
|
+
}
|
|
102
|
+
/** Atomically upgrade legacy v3 backing and preserve the first caller write as a v4 transaction. */
|
|
103
|
+
async upgradeLegacyV3ToV4(source, callerWrites, context) {
|
|
104
|
+
const timestamp = this.now();
|
|
105
|
+
const prepared = this.storageState.prepareCommit([
|
|
106
|
+
insertUsage({
|
|
107
|
+
id: uuidv7(timestamp),
|
|
108
|
+
usage: source.importedUsage,
|
|
109
|
+
adjustment: true,
|
|
110
|
+
details: { source: "v3-import" },
|
|
111
|
+
}),
|
|
112
|
+
...callerWrites,
|
|
113
|
+
], timestamp);
|
|
114
|
+
const nextSeq = prepared.result.firstSeq + prepared.writes.length;
|
|
115
|
+
const upgradedHeader = { ...this.header, nextSeq };
|
|
116
|
+
await publishJsonl(this.fileSystem, this.path, upgradedHeader, context, async (append) => {
|
|
117
|
+
for await (const write of source.writes(context))
|
|
118
|
+
await append([write]);
|
|
119
|
+
await append(prepared.writes);
|
|
120
|
+
});
|
|
121
|
+
const stats = this.storageState.applyValidated(prepared.writes);
|
|
122
|
+
this.backing = { kind: "v4" };
|
|
123
|
+
// The first sequence belongs to the internal usage adjustment; return only caller-write sequences.
|
|
124
|
+
return {
|
|
125
|
+
...prepared.result,
|
|
126
|
+
firstSeq: prepared.result.firstSeq + 1,
|
|
127
|
+
seqs: prepared.result.seqs.slice(1),
|
|
128
|
+
stats,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
getEntries(ids, _context) {
|
|
132
|
+
if (this.state !== "open")
|
|
133
|
+
return Promise.reject(new Error("JsonlStorage is closed"));
|
|
134
|
+
return Promise.resolve(this.storageState.getEntries(ids));
|
|
135
|
+
}
|
|
136
|
+
getValue(address, _context) {
|
|
137
|
+
if (this.state !== "open")
|
|
138
|
+
return Promise.reject(new Error("JsonlStorage is closed"));
|
|
139
|
+
return Promise.resolve(this.storageState.getValue(address));
|
|
140
|
+
}
|
|
141
|
+
scanValues(prefix, _context) {
|
|
142
|
+
if (this.state !== "open")
|
|
143
|
+
return Promise.reject(new Error("JsonlStorage is closed"));
|
|
144
|
+
return Promise.resolve(this.storageState.scanValues(prefix));
|
|
145
|
+
}
|
|
146
|
+
async readList(address, options, _context) {
|
|
147
|
+
if (this.state !== "open")
|
|
148
|
+
throw new Error("JsonlStorage is closed");
|
|
149
|
+
return this.storageState.readList(address, options);
|
|
150
|
+
}
|
|
151
|
+
async scanBranch(query, _context) {
|
|
152
|
+
if (this.state !== "open")
|
|
153
|
+
throw new Error("JsonlStorage is closed");
|
|
154
|
+
return this.storageState.scanBranch(query);
|
|
155
|
+
}
|
|
156
|
+
async scanBranchStructure(query, _context) {
|
|
157
|
+
if (this.state !== "open")
|
|
158
|
+
throw new Error("JsonlStorage is closed");
|
|
159
|
+
return this.storageState.scanBranchStructure(query);
|
|
160
|
+
}
|
|
161
|
+
scanEntries(query, _context) {
|
|
162
|
+
if (this.state !== "open")
|
|
163
|
+
return Promise.reject(new Error("JsonlStorage is closed"));
|
|
164
|
+
return Promise.resolve(this.storageState.scanEntries(query));
|
|
165
|
+
}
|
|
166
|
+
scanUsage(query, _context) {
|
|
167
|
+
if (this.state !== "open")
|
|
168
|
+
return Promise.reject(new Error("JsonlStorage is closed"));
|
|
169
|
+
return Promise.resolve(this.storageState.scanUsage(query));
|
|
170
|
+
}
|
|
171
|
+
getStats(_context) {
|
|
172
|
+
if (this.state !== "open")
|
|
173
|
+
return Promise.reject(new Error("JsonlStorage is closed"));
|
|
174
|
+
return Promise.resolve(this.withImportedUsage(this.storageState.getStats()));
|
|
175
|
+
}
|
|
176
|
+
withImportedUsage(stats) {
|
|
177
|
+
return this.backing.kind === "v4" ? stats : { ...stats, usage: this.backing.source.importedUsage };
|
|
178
|
+
}
|
|
179
|
+
isLegacyV3() {
|
|
180
|
+
return this.backing.kind === "v3";
|
|
181
|
+
}
|
|
182
|
+
/** Capture the first sequence a later source commit would use. */
|
|
183
|
+
captureForkNextSeq(_context) {
|
|
184
|
+
if (this.state !== "open")
|
|
185
|
+
return Promise.reject(new Error("JsonlStorage is closed"));
|
|
186
|
+
const result = this.commitQueue.then(() => this.storageState.getNextSeq());
|
|
187
|
+
this.commitQueue = result.then(() => undefined, () => undefined);
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
close(_context) {
|
|
191
|
+
if (this.closePromise !== undefined)
|
|
192
|
+
return this.closePromise;
|
|
193
|
+
this.state = "closing";
|
|
194
|
+
this.closePromise = this.commitQueue.then(() => {
|
|
195
|
+
this.state = "closed";
|
|
196
|
+
});
|
|
197
|
+
return this.closePromise;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { uuidv7 } from \"@m4ike1/ion-ai/utils/uuid\";\nimport type { Context } from \"../../context.ts\";\nimport type { FileSystem } from \"../../types.ts\";\nimport { insertUsage } from \"../commit.ts\";\nimport { type CommittedWrite, InMemoryStorageState } from \"../in-memory-storage-state.ts\";\nimport type {\n\tCommitResult,\n\tEntry,\n\tEntryScan,\n\tEntryStructure,\n\tSessionStats,\n\tStorage,\n\tStorageBranchScan,\n\tUsageRow,\n\tUsageScan,\n\tWrite,\n} from \"../types.ts\";\nimport type { ListElement, ListReadOptions, StoredValue, Value, ValueList } from \"../values.ts\";\nimport {\n\tfileValue,\n\tparseJsonlTransaction,\n\tpublishFileAtomically,\n\tpublishJsonl,\n\treadJsonlHeader,\n\tserializeJsonlTransaction,\n} from \"./io.ts\";\nimport { LegacyV3Source } from \"./legacy-v3.ts\";\nimport { JSONL_STORAGE_VERSION, type JsonlStorageHeader, type JsonlStorageOptions } from \"./types.ts\";\n\nfunction splitCompleteLines(content: string): { lines: string[]; torn: boolean } {\n\tif (content.endsWith(\"\\n\")) return { lines: content.slice(0, -1).split(\"\\n\"), torn: false };\n\tconst lastNewline = content.lastIndexOf(\"\\n\");\n\tif (lastNewline === -1) return { lines: [], torn: true };\n\treturn { lines: content.slice(0, lastNewline).split(\"\\n\"), torn: true };\n}\n\ntype JsonlBacking = { kind: \"v4\" } | { kind: \"v3\"; source: LegacyV3Source };\n\n/** JSONL storage backed by an injected filesystem capability. */\nexport class JsonlStorage implements Storage {\n\tprivate readonly fileSystem: FileSystem;\n\tprivate readonly path: string;\n\tprivate readonly now: () => number;\n\treadonly header: JsonlStorageHeader;\n\tprivate backing: JsonlBacking;\n\tprivate readonly storageState = new InMemoryStorageState();\n\tprivate commitQueue: Promise<void> = Promise.resolve();\n\tprivate state: \"open\" | \"closing\" | \"closed\" = \"open\";\n\tprivate closePromise: Promise<void> | undefined;\n\n\tprivate constructor(options: JsonlStorageOptions, header: JsonlStorageHeader, backing: JsonlBacking) {\n\t\tthis.fileSystem = options.fileSystem;\n\t\tthis.path = options.path;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.header = header;\n\t\tthis.backing = backing;\n\t}\n\n\tstatic async create(\n\t\toptions: JsonlStorageOptions,\n\t\theader: JsonlStorageHeader,\n\t\tinitialWrites: Write[],\n\t\tcontext: Context,\n\t): Promise<JsonlStorage> {\n\t\tconst storage = new JsonlStorage(options, header, { kind: \"v4\" });\n\t\tconst prepared = storage.storageState.prepareCommit(initialWrites, storage.now());\n\t\tawait publishJsonl(options.fileSystem, options.path, header, context, async (append) => {\n\t\t\tif (prepared.writes.length !== 0) await append(prepared.writes);\n\t\t});\n\t\tstorage.storageState.applyValidated(prepared.writes);\n\t\treturn storage;\n\t}\n\n\tstatic async open(options: JsonlStorageOptions, context: Context): Promise<JsonlStorage> {\n\t\tconst reader = fileValue(\n\t\t\tawait options.fileSystem.openTextLineReader(options.path, context),\n\t\t\t`Failed to read JSONL storage ${options.path}`,\n\t\t);\n\t\tconst parsed = await readJsonlHeader(reader, options.path, context).finally(() => reader.close(context));\n\t\treturn parsed.format === \"v3-legacy\"\n\t\t\t? JsonlStorage.openLegacyV3(options, context)\n\t\t\t: JsonlStorage.openV4(options, parsed.header, context);\n\t}\n\n\tprivate static async openV4(\n\t\toptions: JsonlStorageOptions,\n\t\theader: JsonlStorageHeader,\n\t\tcontext: Context,\n\t): Promise<JsonlStorage> {\n\t\tconst content = fileValue(\n\t\t\tawait options.fileSystem.readTextFile(options.path, context),\n\t\t\t`Failed to read JSONL storage ${options.path}`,\n\t\t);\n\t\tconst { lines, torn } = splitCompleteLines(content);\n\t\tif (header.storageVersion !== JSONL_STORAGE_VERSION) {\n\t\t\tthrow new Error(`Session ${header.id} uses unsupported storage version ${header.storageVersion}`);\n\t\t}\n\t\tconst storage = new JsonlStorage(options, header, { kind: \"v4\" });\n\t\tfor (let index = 1; index < lines.length; index++) {\n\t\t\tconst line = lines[index]!;\n\t\t\ttry {\n\t\t\t\tstorage.replayCommitted(parseJsonlTransaction(line));\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(`Invalid JSONL storage ${options.path}: line ${index + 1}`, { cause: error });\n\t\t\t}\n\t\t}\n\t\tif (header.nextSeq !== undefined) storage.storageState.advanceNextSeq(header.nextSeq);\n\t\tif (torn) {\n\t\t\tawait publishFileAtomically(options.fileSystem, options.path, context, (append) =>\n\t\t\t\tappend(`${lines.join(\"\\n\")}\\n`),\n\t\t\t);\n\t\t}\n\t\treturn storage;\n\t}\n\n\tprivate static async openLegacyV3(options: JsonlStorageOptions, context: Context): Promise<JsonlStorage> {\n\t\tconst source = await LegacyV3Source.read(options.fileSystem, options.path, context);\n\t\tconst storage = new JsonlStorage(options, { ...source.header, nextSeq: source.nextSeq }, { kind: \"v3\", source });\n\t\tfor await (const write of source.writes(context)) storage.replayCommitted([write]);\n\t\treturn storage;\n\t}\n\n\tprivate replayCommitted(writes: readonly CommittedWrite[]): void {\n\t\tthis.storageState.validateCommitted(writes);\n\t\tthis.storageState.applyValidated(writes);\n\t}\n\n\tasync commit(writes: Write[], context: Context): Promise<CommitResult> {\n\t\tif (this.state !== \"open\") throw new Error(\"JsonlStorage is closed\");\n\t\tconst result = this.commitQueue.then(() => this.applyCommit(writes, context));\n\t\tthis.commitQueue = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tprivate async applyCommit(writes: Write[], context: Context): Promise<CommitResult> {\n\t\tif (this.backing.kind === \"v3\" && writes.length !== 0) {\n\t\t\treturn this.upgradeLegacyV3ToV4(this.backing.source, writes, context);\n\t\t}\n\t\tconst prepared = this.storageState.prepareCommit(writes, this.now());\n\t\tif (prepared.writes.length !== 0) {\n\t\t\tfileValue(\n\t\t\t\tawait this.fileSystem.appendFile(this.path, `${serializeJsonlTransaction(prepared.writes)}\\n`, context),\n\t\t\t\t`Failed to append JSONL storage ${this.path}`,\n\t\t\t);\n\t\t}\n\t\tconst stats = this.storageState.applyValidated(prepared.writes);\n\t\treturn { ...prepared.result, stats: this.withImportedUsage(stats) };\n\t}\n\n\t/** Atomically upgrade legacy v3 backing and preserve the first caller write as a v4 transaction. */\n\tprivate async upgradeLegacyV3ToV4(\n\t\tsource: LegacyV3Source,\n\t\tcallerWrites: Write[],\n\t\tcontext: Context,\n\t): Promise<CommitResult> {\n\t\tconst timestamp = this.now();\n\t\tconst prepared = this.storageState.prepareCommit(\n\t\t\t[\n\t\t\t\tinsertUsage({\n\t\t\t\t\tid: uuidv7(timestamp),\n\t\t\t\t\tusage: source.importedUsage,\n\t\t\t\t\tadjustment: true,\n\t\t\t\t\tdetails: { source: \"v3-import\" },\n\t\t\t\t}),\n\t\t\t\t...callerWrites,\n\t\t\t],\n\t\t\ttimestamp,\n\t\t);\n\n\t\tconst nextSeq = prepared.result.firstSeq + prepared.writes.length;\n\t\tconst upgradedHeader = { ...this.header, nextSeq };\n\t\tawait publishJsonl(this.fileSystem, this.path, upgradedHeader, context, async (append) => {\n\t\t\tfor await (const write of source.writes(context)) await append([write]);\n\t\t\tawait append(prepared.writes);\n\t\t});\n\n\t\tconst stats = this.storageState.applyValidated(prepared.writes);\n\t\tthis.backing = { kind: \"v4\" };\n\t\t// The first sequence belongs to the internal usage adjustment; return only caller-write sequences.\n\t\treturn {\n\t\t\t...prepared.result,\n\t\t\tfirstSeq: prepared.result.firstSeq + 1,\n\t\t\tseqs: prepared.result.seqs.slice(1),\n\t\t\tstats,\n\t\t};\n\t}\n\n\tgetEntries(ids: string[], _context: Context): Promise<Map<string, Entry>> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.getEntries(ids));\n\t}\n\n\tgetValue<T>(address: Value<T>, _context: Context): Promise<StoredValue<T> | undefined> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.getValue(address));\n\t}\n\n\tscanValues<T>(prefix: Value<T>, _context: Context): Promise<StoredValue<T>[]> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.scanValues(prefix));\n\t}\n\n\tasync readList<T>(\n\t\taddress: ValueList<T>,\n\t\toptions: ListReadOptions | undefined,\n\t\t_context: Context,\n\t): Promise<ListElement<T>[]> {\n\t\tif (this.state !== \"open\") throw new Error(\"JsonlStorage is closed\");\n\t\treturn this.storageState.readList(address, options);\n\t}\n\n\tasync scanBranch(query: StorageBranchScan, _context: Context): Promise<Entry[]> {\n\t\tif (this.state !== \"open\") throw new Error(\"JsonlStorage is closed\");\n\t\treturn this.storageState.scanBranch(query);\n\t}\n\n\tasync scanBranchStructure(query: StorageBranchScan, _context: Context): Promise<EntryStructure[]> {\n\t\tif (this.state !== \"open\") throw new Error(\"JsonlStorage is closed\");\n\t\treturn this.storageState.scanBranchStructure(query);\n\t}\n\n\tscanEntries(query: EntryScan, _context: Context): Promise<Entry[]> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.scanEntries(query));\n\t}\n\n\tscanUsage(query: UsageScan, _context: Context): Promise<UsageRow[]> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.scanUsage(query));\n\t}\n\n\tgetStats(_context: Context): Promise<SessionStats> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.withImportedUsage(this.storageState.getStats()));\n\t}\n\n\tprivate withImportedUsage(stats: SessionStats): SessionStats {\n\t\treturn this.backing.kind === \"v4\" ? stats : { ...stats, usage: this.backing.source.importedUsage };\n\t}\n\n\tisLegacyV3(): boolean {\n\t\treturn this.backing.kind === \"v3\";\n\t}\n\n\t/** Capture the first sequence a later source commit would use. */\n\tcaptureForkNextSeq(_context: Context): Promise<number> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\tconst result = this.commitQueue.then(() => this.storageState.getNextSeq());\n\t\tthis.commitQueue = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tclose(_context: Context): Promise<void> {\n\t\tif (this.closePromise !== undefined) return this.closePromise;\n\t\tthis.state = \"closing\";\n\t\tthis.closePromise = this.commitQueue.then(() => {\n\t\t\tthis.state = \"closed\";\n\t\t});\n\t\treturn this.closePromise;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { FileSystem } from "../../types.ts";
|
|
2
|
+
import type { SessionCreateOptions, SessionMetadata } from "../types.ts";
|
|
3
|
+
export declare const JSONL_FORMAT_VERSION = 4;
|
|
4
|
+
export declare const JSONL_STORAGE_VERSION = 1;
|
|
5
|
+
export interface JsonlStorageHeader {
|
|
6
|
+
v: typeof JSONL_FORMAT_VERSION;
|
|
7
|
+
kind: "header";
|
|
8
|
+
id: string;
|
|
9
|
+
storageVersion: number;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
cwd: string;
|
|
12
|
+
parentSessionId?: string;
|
|
13
|
+
legacyParentSessionPath?: string;
|
|
14
|
+
/** Sequence high-water mark written by snapshot rewrites. */
|
|
15
|
+
nextSeq?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface JsonlStorageOptions {
|
|
18
|
+
fileSystem: FileSystem;
|
|
19
|
+
path: string;
|
|
20
|
+
now?: () => number;
|
|
21
|
+
}
|
|
22
|
+
export interface JsonlSessionMetadata extends SessionMetadata {
|
|
23
|
+
cwd: string;
|
|
24
|
+
path: string;
|
|
25
|
+
/** Filesystem modification time as milliseconds since Unix epoch. */
|
|
26
|
+
modifiedAt: number;
|
|
27
|
+
}
|
|
28
|
+
export interface JsonlSessionCreateOptions extends SessionCreateOptions {
|
|
29
|
+
cwd: string;
|
|
30
|
+
}
|
|
31
|
+
export interface JsonlSessionListOptions {
|
|
32
|
+
cwd?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface JsonlSessionRepoOptions {
|
|
35
|
+
fileSystem: FileSystem;
|
|
36
|
+
sessionsRoot: string;
|
|
37
|
+
now?: () => number;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEzE,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,MAAM,WAAW,kBAAkB;IAClC,CAAC,EAAE,OAAO,oBAAoB,CAAC;IAC/B,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IACnC,UAAU,EAAE,UAAU,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAqB,SAAQ,eAAe;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,yBAA0B,SAAQ,oBAAoB;IACtE,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,uBAAuB;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,uBAAuB;IACvC,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB","sourcesContent":["import type { FileSystem } from \"../../types.ts\";\nimport type { SessionCreateOptions, SessionMetadata } from \"../types.ts\";\n\nexport const JSONL_FORMAT_VERSION = 4;\nexport const JSONL_STORAGE_VERSION = 1;\n\nexport interface JsonlStorageHeader {\n\tv: typeof JSONL_FORMAT_VERSION;\n\tkind: \"header\";\n\tid: string;\n\tstorageVersion: number;\n\tcreatedAt: number;\n\tcwd: string;\n\tparentSessionId?: string;\n\tlegacyParentSessionPath?: string;\n\t/** Sequence high-water mark written by snapshot rewrites. */\n\tnextSeq?: number;\n}\n\nexport interface JsonlStorageOptions {\n\tfileSystem: FileSystem;\n\tpath: string;\n\tnow?: () => number;\n}\n\nexport interface JsonlSessionMetadata extends SessionMetadata {\n\tcwd: string;\n\tpath: string;\n\t/** Filesystem modification time as milliseconds since Unix epoch. */\n\tmodifiedAt: number;\n}\n\nexport interface JsonlSessionCreateOptions extends SessionCreateOptions {\n\tcwd: string;\n}\n\nexport interface JsonlSessionListOptions {\n\tcwd?: string;\n}\n\nexport interface JsonlSessionRepoOptions {\n\tfileSystem: FileSystem;\n\tsessionsRoot: string;\n\tnow?: () => number;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/types.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC","sourcesContent":["import type { FileSystem } from \"../../types.ts\";\nimport type { SessionCreateOptions, SessionMetadata } from \"../types.ts\";\n\nexport const JSONL_FORMAT_VERSION = 4;\nexport const JSONL_STORAGE_VERSION = 1;\n\nexport interface JsonlStorageHeader {\n\tv: typeof JSONL_FORMAT_VERSION;\n\tkind: \"header\";\n\tid: string;\n\tstorageVersion: number;\n\tcreatedAt: number;\n\tcwd: string;\n\tparentSessionId?: string;\n\tlegacyParentSessionPath?: string;\n\t/** Sequence high-water mark written by snapshot rewrites. */\n\tnextSeq?: number;\n}\n\nexport interface JsonlStorageOptions {\n\tfileSystem: FileSystem;\n\tpath: string;\n\tnow?: () => number;\n}\n\nexport interface JsonlSessionMetadata extends SessionMetadata {\n\tcwd: string;\n\tpath: string;\n\t/** Filesystem modification time as milliseconds since Unix epoch. */\n\tmodifiedAt: number;\n}\n\nexport interface JsonlSessionCreateOptions extends SessionCreateOptions {\n\tcwd: string;\n}\n\nexport interface JsonlSessionListOptions {\n\tcwd?: string;\n}\n\nexport interface JsonlSessionRepoOptions {\n\tfileSystem: FileSystem;\n\tsessionsRoot: string;\n\tnow?: () => number;\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Context } from "../context.ts";
|
|
2
|
+
import type { CommitResult, Entry, EntryScan, EntryStructure, ForkOptions, Session, SessionCreateOptions, SessionMetadata, SessionRepo, SessionStats, Storage, StorageBranchScan, UsageRow, UsageScan, Write } from "./types.ts";
|
|
3
|
+
import type { ListElement, ListReadOptions, StoredValue, Value, ValueList } from "./values.ts";
|
|
4
|
+
export interface MemoryStorageOptions {
|
|
5
|
+
now?: () => number;
|
|
6
|
+
}
|
|
7
|
+
export interface MemorySessionRepoOptions {
|
|
8
|
+
now?: () => number;
|
|
9
|
+
}
|
|
10
|
+
export declare class MemoryStorage implements Storage {
|
|
11
|
+
private readonly now;
|
|
12
|
+
private storageState;
|
|
13
|
+
private commitQueue;
|
|
14
|
+
private state;
|
|
15
|
+
private closePromise;
|
|
16
|
+
constructor(options?: MemoryStorageOptions);
|
|
17
|
+
commit(writes: Write[], _context: Context): Promise<CommitResult>;
|
|
18
|
+
getEntries(ids: string[], _context: Context): Promise<Map<string, Entry>>;
|
|
19
|
+
getValue<T>(address: Value<T>, _context: Context): Promise<StoredValue<T> | undefined>;
|
|
20
|
+
scanValues<T>(prefix: Value<T>, _context: Context): Promise<StoredValue<T>[]>;
|
|
21
|
+
readList<T>(address: ValueList<T>, options: ListReadOptions | undefined, _context: Context): Promise<ListElement<T>[]>;
|
|
22
|
+
scanBranch(query: StorageBranchScan, _context: Context): Promise<Entry[]>;
|
|
23
|
+
scanBranchStructure(query: StorageBranchScan, _context: Context): Promise<EntryStructure[]>;
|
|
24
|
+
scanEntries(query: EntryScan, _context: Context): Promise<Entry[]>;
|
|
25
|
+
scanUsage(query: UsageScan, _context: Context): Promise<UsageRow[]>;
|
|
26
|
+
getStats(_context: Context): Promise<SessionStats>;
|
|
27
|
+
/** Construct a destination storage at one serialized boundary between source commits. */
|
|
28
|
+
fork(options: ForkOptions): Promise<MemoryStorage>;
|
|
29
|
+
close(_context: Context): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export declare class MemorySessionRepo implements SessionRepo {
|
|
32
|
+
private readonly now;
|
|
33
|
+
private readonly sessions;
|
|
34
|
+
private readonly pendingIds;
|
|
35
|
+
private closed;
|
|
36
|
+
private closePromise;
|
|
37
|
+
constructor(options?: MemorySessionRepoOptions);
|
|
38
|
+
create(options: SessionCreateOptions, context: Context): Promise<Session>;
|
|
39
|
+
open(metadata: SessionMetadata, _context: Context): Promise<Session>;
|
|
40
|
+
list(_options: undefined, _context: Context): Promise<SessionMetadata[]>;
|
|
41
|
+
delete(metadata: SessionMetadata, context: Context): Promise<void>;
|
|
42
|
+
fork(source: SessionMetadata, options: ForkOptions, _context: Context): Promise<Session>;
|
|
43
|
+
close(context: Context): Promise<void>;
|
|
44
|
+
private openRecord;
|
|
45
|
+
private reserveId;
|
|
46
|
+
private assertOpen;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/harness/session/memory.ts"],"names":[],"mappings":"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","sourcesContent":["import { uuidv7 } from \"@m4ike1/ion-ai/utils/uuid\";\nimport type { Context } from \"../context.ts\";\nimport { InMemoryStorageState } from \"./in-memory-storage-state.ts\";\nimport { StorageBackedSession } from \"./session.ts\";\nimport type {\n\tBranch,\n\tCommitResult,\n\tEntry,\n\tEntryQuery,\n\tEntryScan,\n\tEntryStructure,\n\tForkOptions,\n\tIdGenerator,\n\tSession,\n\tSessionCreateOptions,\n\tSessionMetadata,\n\tSessionMutation,\n\tSessionMutationCallback,\n\tSessionRepo,\n\tSessionStats,\n\tStorage,\n\tStorageBranchScan,\n\tUsageRow,\n\tUsageScan,\n\tWrite,\n} from \"./types.ts\";\nimport type { ListElement, ListReadOptions, StoredValue, Value, ValueList } from \"./values.ts\";\n\nexport interface MemoryStorageOptions {\n\tnow?: () => number;\n}\n\nexport interface MemorySessionRepoOptions {\n\tnow?: () => number;\n}\n\nexport class MemoryStorage implements Storage {\n\tprivate readonly now: () => number;\n\tprivate storageState = new InMemoryStorageState();\n\tprivate commitQueue: Promise<void> = Promise.resolve();\n\tprivate state: \"open\" | \"closing\" | \"closed\" = \"open\";\n\tprivate closePromise: Promise<void> | undefined;\n\n\tconstructor(options: MemoryStorageOptions = {}) {\n\t\tthis.now = options.now ?? Date.now;\n\t}\n\n\tasync commit(writes: Write[], _context: Context): Promise<CommitResult> {\n\t\tif (this.state !== \"open\") throw new Error(\"MemoryStorage is closed\");\n\t\tconst result = this.commitQueue.then(() => {\n\t\t\tconst prepared = this.storageState.prepareCommit(writes, this.now());\n\t\t\tconst stats = this.storageState.applyValidated(prepared.writes);\n\t\t\treturn { ...prepared.result, stats };\n\t\t});\n\t\tthis.commitQueue = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tgetEntries(ids: string[], _context: Context): Promise<Map<string, Entry>> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"MemoryStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.getEntries(ids));\n\t}\n\n\tgetValue<T>(address: Value<T>, _context: Context): Promise<StoredValue<T> | undefined> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"MemoryStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.getValue(address));\n\t}\n\n\tscanValues<T>(prefix: Value<T>, _context: Context): Promise<StoredValue<T>[]> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"MemoryStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.scanValues(prefix));\n\t}\n\n\tasync readList<T>(\n\t\taddress: ValueList<T>,\n\t\toptions: ListReadOptions | undefined,\n\t\t_context: Context,\n\t): Promise<ListElement<T>[]> {\n\t\tif (this.state !== \"open\") throw new Error(\"MemoryStorage is closed\");\n\t\treturn this.storageState.readList(address, options);\n\t}\n\n\tasync scanBranch(query: StorageBranchScan, _context: Context): Promise<Entry[]> {\n\t\tif (this.state !== \"open\") throw new Error(\"MemoryStorage is closed\");\n\t\treturn this.storageState.scanBranch(query);\n\t}\n\n\tasync scanBranchStructure(query: StorageBranchScan, _context: Context): Promise<EntryStructure[]> {\n\t\tif (this.state !== \"open\") throw new Error(\"MemoryStorage is closed\");\n\t\treturn this.storageState.scanBranchStructure(query);\n\t}\n\n\tscanEntries(query: EntryScan, _context: Context): Promise<Entry[]> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"MemoryStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.scanEntries(query));\n\t}\n\n\tscanUsage(query: UsageScan, _context: Context): Promise<UsageRow[]> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"MemoryStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.scanUsage(query));\n\t}\n\n\tgetStats(_context: Context): Promise<SessionStats> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"MemoryStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.getStats());\n\t}\n\n\t/** Construct a destination storage at one serialized boundary between source commits. */\n\tfork(options: ForkOptions): Promise<MemoryStorage> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"MemoryStorage is closed\"));\n\t\tconst result = this.commitQueue.then(() => {\n\t\t\tconst destination = new MemoryStorage({ now: this.now });\n\t\t\tdestination.storageState = this.storageState.createFork(options);\n\t\t\treturn destination;\n\t\t});\n\t\tthis.commitQueue = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tclose(_context: Context): Promise<void> {\n\t\tif (this.closePromise !== undefined) return this.closePromise;\n\t\tthis.state = \"closing\";\n\t\tthis.closePromise = this.commitQueue.then(() => {\n\t\t\tthis.state = \"closed\";\n\t\t});\n\t\treturn this.closePromise;\n\t}\n}\n\nconst MEMORY_STORAGE_VERSION = 1;\n\ninterface MemorySessionRecord {\n\tmetadata: SessionMetadata;\n\tstorage: MemoryStorage;\n\tsession: StorageBackedSession;\n\topen: boolean;\n}\n\nclass MemorySessionFacade implements Session {\n\treadonly metadata: SessionMetadata;\n\treadonly idGenerator: IdGenerator;\n\tprivate readonly session: StorageBackedSession;\n\tprivate readonly onClose: () => void;\n\tprivate readonly admitted = new Set<Promise<unknown>>();\n\tprivate readonly closedError = new Error(\"Session is closed\");\n\tprivate state: \"open\" | \"closing\" | \"closed\" = \"open\";\n\tprivate closePromise: Promise<void> | undefined;\n\n\tconstructor(session: StorageBackedSession, onClose: () => void) {\n\t\tthis.session = session;\n\t\tthis.metadata = session.metadata;\n\t\tthis.idGenerator = session.idGenerator;\n\t\tthis.onClose = onClose;\n\t}\n\n\tasync beginMutation(context: Context): Promise<SessionMutation> {\n\t\tlet resolveFinished!: () => void;\n\t\tconst finished = new Promise<void>((resolve) => {\n\t\t\tresolveFinished = resolve;\n\t\t});\n\t\tthis.admitted.add(finished);\n\t\tlet source: SessionMutation;\n\t\ttry {\n\t\t\tsource = await this.admit(() => this.session.beginMutation(context));\n\t\t} catch (error) {\n\t\t\tthis.admitted.delete(finished);\n\t\t\tresolveFinished();\n\t\t\tthrow error;\n\t\t}\n\t\tif (this.state !== \"open\") {\n\t\t\tawait source.end(context);\n\t\t\tthis.admitted.delete(finished);\n\t\t\tresolveFinished();\n\t\t\tthrow this.closedError;\n\t\t}\n\t\tlet ended = false;\n\t\treturn {\n\t\t\tcommit: (writes, commitContext) => source.commit(writes, commitContext),\n\t\t\tend: async (endContext) => {\n\t\t\t\ttry {\n\t\t\t\t\tawait source.end(endContext);\n\t\t\t\t} finally {\n\t\t\t\t\tif (!ended) {\n\t\t\t\t\t\tended = true;\n\t\t\t\t\t\tthis.admitted.delete(finished);\n\t\t\t\t\t\tresolveFinished();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tgetEntries: (ids, readContext) => source.getEntries(ids, readContext),\n\t\t\tgetStats: (readContext) => source.getStats(readContext),\n\t\t\tgetValue: (address, readContext) => source.getValue(address, readContext),\n\t\t\tscanValues: (prefix, readContext) => source.scanValues(prefix, readContext),\n\t\t\treadList: (address, options, readContext) => source.readList(address, options, readContext),\n\t\t\tscanBranch: (query, readContext) => source.scanBranch(query, readContext),\n\t\t};\n\t}\n\n\tmutate<T>(mutation: SessionMutationCallback<T>, context: Context): Promise<T> {\n\t\treturn this.admit(() =>\n\t\t\tthis.session.mutate((mutator, mutationContext) => {\n\t\t\t\tif (this.state !== \"open\") throw this.closedError;\n\t\t\t\treturn mutation(mutator, mutationContext);\n\t\t\t}, context),\n\t\t);\n\t}\n\n\tgetEntries(ids: string[], context: Context): Promise<Map<string, Entry>> {\n\t\treturn this.admit(() => this.session.getEntries(ids, context));\n\t}\n\n\tgetEntry(id: string, context: Context): Promise<Entry | undefined> {\n\t\treturn this.admit(() => this.session.getEntry(id, context));\n\t}\n\n\tgetValue<T>(address: Value<T>, context: Context): Promise<StoredValue<T> | undefined> {\n\t\treturn this.admit(() => this.session.getValue(address, context));\n\t}\n\n\tscanValues<T>(prefix: Value<T>, context: Context): Promise<StoredValue<T>[]> {\n\t\treturn this.admit(() => this.session.scanValues(prefix, context));\n\t}\n\n\treadList<T>(\n\t\taddress: ValueList<T>,\n\t\toptions: ListReadOptions | undefined,\n\t\tcontext: Context,\n\t): Promise<ListElement<T>[]> {\n\t\treturn this.admit(() => this.session.readList(address, options, context));\n\t}\n\n\tscanBranch(query: StorageBranchScan, context: Context): Promise<Entry[]> {\n\t\treturn this.admit(() => this.session.scanBranch(query, context));\n\t}\n\n\tgetStats(context: Context): Promise<SessionStats> {\n\t\treturn this.admit(() => this.session.getStats(context));\n\t}\n\n\tgetName(context: Context): Promise<string | undefined> {\n\t\treturn this.admit(() => this.session.getName(context));\n\t}\n\n\tgetLabel(targetId: string, context: Context): Promise<string | undefined> {\n\t\treturn this.admit(() => this.session.getLabel(targetId, context));\n\t}\n\n\tfindEntries(query: EntryQuery | undefined, context: Context): Promise<Entry[]> {\n\t\treturn this.admit(() => this.session.findEntries(query, context));\n\t}\n\n\tfindEntry(query: EntryQuery | undefined, context: Context): Promise<Entry | undefined> {\n\t\treturn this.admit(() => this.session.findEntry(query, context));\n\t}\n\n\tasync branch(name: string, context: Context): Promise<Branch | undefined> {\n\t\tconst branch = await this.admit(() => this.session.branch(name, context));\n\t\treturn branch === undefined ? undefined : this.wrapBranch(branch);\n\t}\n\n\tasync createBranch(name: string, at: string | null, context: Context): Promise<Branch> {\n\t\treturn this.wrapBranch(await this.admit(() => this.session.createBranch(name, at, context)));\n\t}\n\n\tsetValue<T>(address: Value<T>, next: NoInfer<T>, context: Context): Promise<void> {\n\t\treturn this.admit(() => this.session.setValue(address, next, context));\n\t}\n\n\tdeleteValue<T>(address: Value<T>, context: Context): Promise<void> {\n\t\treturn this.admit(() => this.session.deleteValue(address, context));\n\t}\n\n\tappendList<T>(address: ValueList<T>, element: NoInfer<T>, context: Context): Promise<void> {\n\t\treturn this.admit(() => this.session.appendList(address, element, context));\n\t}\n\n\tdeleteList<T>(address: ValueList<T>, context: Context): Promise<void> {\n\t\treturn this.admit(() => this.session.deleteList(address, context));\n\t}\n\n\tsetName(name: string | undefined, context: Context): Promise<void> {\n\t\treturn this.admit(() => this.session.setName(name, context));\n\t}\n\n\tsetLabel(targetId: string, label: string | undefined, context: Context): Promise<void> {\n\t\treturn this.admit(() => this.session.setLabel(targetId, label, context));\n\t}\n\n\tclose(_context: Context): Promise<void> {\n\t\tif (this.closePromise !== undefined) return this.closePromise;\n\t\tthis.state = \"closing\";\n\t\tthis.closePromise = Promise.allSettled([...this.admitted]).then(() => {\n\t\t\tthis.state = \"closed\";\n\t\t\tthis.onClose();\n\t\t});\n\t\treturn this.closePromise;\n\t}\n\n\tprivate wrapBranch(branch: Branch): Branch {\n\t\treturn {\n\t\t\tname: branch.name,\n\t\t\tgetTipId: (context) => this.admit(() => branch.getTipId(context)),\n\t\t\tfindEntries: (query, context) => this.admit(() => branch.findEntries(query, context)),\n\t\t\tfindEntry: (query, context) => this.admit(() => branch.findEntry(query, context)),\n\t\t\tappendMessage: (message, context) => this.admit(() => branch.appendMessage(message, context)),\n\t\t\tappendCustomEntry: (customType, data, context) =>\n\t\t\t\tthis.admit(() => branch.appendCustomEntry(customType, data, context)),\n\t\t};\n\t}\n\n\tprivate admit<T>(operation: () => Promise<T>): Promise<T> {\n\t\tif (this.state !== \"open\") return Promise.reject(this.closedError);\n\t\tlet result: Promise<T>;\n\t\ttry {\n\t\t\tresult = operation();\n\t\t} catch (error) {\n\t\t\tresult = Promise.reject(error);\n\t\t}\n\t\tthis.admitted.add(result);\n\t\tvoid result.then(\n\t\t\t() => this.admitted.delete(result),\n\t\t\t() => this.admitted.delete(result),\n\t\t);\n\t\treturn result;\n\t}\n}\n\nexport class MemorySessionRepo implements SessionRepo {\n\tprivate readonly now: () => number;\n\tprivate readonly sessions = new Map<string, MemorySessionRecord>();\n\tprivate readonly pendingIds = new Set<string>();\n\tprivate closed = false;\n\tprivate closePromise: Promise<void> | undefined;\n\n\tconstructor(options: MemorySessionRepoOptions = {}) {\n\t\tthis.now = options.now ?? Date.now;\n\t}\n\n\tasync create(options: SessionCreateOptions, context: Context): Promise<Session> {\n\t\tthis.assertOpen();\n\t\tconst createdAt = this.now();\n\t\tconst id = options.id ?? uuidv7(createdAt);\n\t\tthis.reserveId(id);\n\t\tconst metadata: SessionMetadata = {\n\t\t\tid,\n\t\t\tcreatedAt,\n\t\t\tstorageVersion: MEMORY_STORAGE_VERSION,\n\t\t\t...(options.parentSessionId === undefined ? {} : { parentSessionId: options.parentSessionId }),\n\t\t};\n\t\tconst storage = new MemoryStorage({ now: this.now });\n\t\tconst session = new StorageBackedSession(metadata, storage);\n\t\ttry {\n\t\t\tconst record: MemorySessionRecord = {\n\t\t\t\tmetadata,\n\t\t\t\tstorage,\n\t\t\t\tsession,\n\t\t\t\topen: true,\n\t\t\t};\n\t\t\tthis.sessions.set(id, record);\n\t\t\treturn this.openRecord(record);\n\t\t} catch (error) {\n\t\t\tawait session.close(context);\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tthis.pendingIds.delete(id);\n\t\t}\n\t}\n\n\topen(metadata: SessionMetadata, _context: Context): Promise<Session> {\n\t\t// Memory sessions are always created at the current storage version, so\n\t\t// persistent-backend version gating does not apply here.\n\t\tthis.assertOpen();\n\t\tconst record = this.sessions.get(metadata.id);\n\t\tif (record === undefined) return Promise.reject(new Error(`Unknown session: ${metadata.id}`));\n\t\tif (record.open) return Promise.reject(new Error(`Session is already open: ${metadata.id}`));\n\t\trecord.open = true;\n\t\treturn Promise.resolve(this.openRecord(record));\n\t}\n\n\tlist(_options: undefined, _context: Context): Promise<SessionMetadata[]> {\n\t\tthis.assertOpen();\n\t\treturn Promise.resolve([...this.sessions.values()].map(({ metadata }) => metadata));\n\t}\n\n\tasync delete(metadata: SessionMetadata, context: Context): Promise<void> {\n\t\tthis.assertOpen();\n\t\tconst record = this.sessions.get(metadata.id);\n\t\tif (record === undefined) throw new Error(`Unknown session: ${metadata.id}`);\n\t\tif (record.open) throw new Error(`Session is open: ${metadata.id}`);\n\t\tawait record.session.close(context);\n\t\tthis.sessions.delete(metadata.id);\n\t}\n\n\tasync fork(source: SessionMetadata, options: ForkOptions, _context: Context): Promise<Session> {\n\t\tthis.assertOpen();\n\t\tconst sourceRecord = this.sessions.get(source.id);\n\t\tif (sourceRecord === undefined) throw new Error(`Unknown session: ${source.id}`);\n\t\tconst createdAt = this.now();\n\t\tconst id = options.id ?? uuidv7(createdAt);\n\t\tthis.reserveId(id);\n\n\t\ttry {\n\t\t\tconst storage = await sourceRecord.storage.fork(options);\n\t\t\tconst metadata: SessionMetadata = {\n\t\t\t\tid,\n\t\t\t\tcreatedAt,\n\t\t\t\tstorageVersion: MEMORY_STORAGE_VERSION,\n\t\t\t\tparentSessionId: sourceRecord.metadata.id,\n\t\t\t};\n\t\t\tconst session = new StorageBackedSession(metadata, storage);\n\t\t\tconst record: MemorySessionRecord = {\n\t\t\t\tmetadata,\n\t\t\t\tstorage,\n\t\t\t\tsession,\n\t\t\t\topen: true,\n\t\t\t};\n\t\t\tthis.sessions.set(id, record);\n\t\t\treturn this.openRecord(record);\n\t\t} finally {\n\t\t\tthis.pendingIds.delete(id);\n\t\t}\n\t}\n\n\tclose(context: Context): Promise<void> {\n\t\tif (this.closePromise !== undefined) return this.closePromise;\n\t\tthis.closed = true;\n\t\tthis.closePromise = Promise.all([...this.sessions.values()].map(({ session }) => session.close(context))).then(\n\t\t\t() => undefined,\n\t\t);\n\t\treturn this.closePromise;\n\t}\n\n\tprivate openRecord(record: MemorySessionRecord): Session {\n\t\treturn new MemorySessionFacade(record.session, () => {\n\t\t\trecord.open = false;\n\t\t});\n\t}\n\n\tprivate reserveId(id: string): void {\n\t\tif (this.sessions.has(id) || this.pendingIds.has(id)) throw new Error(`Session already exists: ${id}`);\n\t\tthis.pendingIds.add(id);\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) throw new Error(\"MemorySessionRepo is closed\");\n\t}\n}\n"]}
|