@myagentroam/agent 0.11.33 → 0.11.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/catalog-file.d.ts +78 -0
- package/dist/cli/catalog-file.js +102 -0
- package/dist/cli/codex-auth-file.d.ts +1 -1
- package/dist/cli/codex-auth-file.js +9 -10
- package/dist/cli/main.js +44 -54
- package/dist/error.d.ts +15 -0
- package/dist/error.js +74 -0
- package/dist/extensions/agents.d.ts +100 -0
- package/dist/extensions/agents.js +121 -0
- package/dist/extensions/capabilities.d.ts +4 -0
- package/dist/extensions/commands.d.ts +23 -0
- package/dist/extensions/commands.js +45 -0
- package/dist/extensions/compaction.d.ts +36 -0
- package/dist/extensions/contracts.d.ts +147 -0
- package/dist/extensions/data.d.ts +81 -0
- package/dist/extensions/data.js +78 -0
- package/dist/extensions/environment.d.ts +39 -0
- package/dist/extensions/environment.js +52 -0
- package/dist/extensions/files.d.ts +68 -0
- package/dist/extensions/files.js +26 -0
- package/dist/extensions/hooks.d.ts +32 -0
- package/dist/extensions/host-ports.d.ts +24 -0
- package/dist/extensions/manager.d.ts +42 -0
- package/dist/extensions/manager.js +457 -0
- package/dist/extensions/mcp.d.ts +116 -0
- package/dist/extensions/mcp.js +434 -0
- package/dist/extensions/model-request.d.ts +65 -0
- package/dist/extensions/model-request.js +197 -0
- package/dist/extensions/model-stream.d.ts +18 -0
- package/dist/extensions/model-stream.js +108 -0
- package/dist/extensions/models.d.ts +46 -0
- package/dist/extensions/models.js +7 -0
- package/dist/extensions/network.d.ts +40 -0
- package/dist/extensions/network.js +97 -0
- package/dist/extensions/operation.d.ts +32 -0
- package/dist/extensions/processes.d.ts +64 -0
- package/dist/extensions/processes.js +284 -0
- package/dist/extensions/prompt-composition.d.ts +38 -0
- package/dist/extensions/prompt-composition.js +100 -0
- package/dist/extensions/prompt-input.d.ts +30 -0
- package/dist/extensions/prompt-input.js +48 -0
- package/dist/extensions/prompts.d.ts +31 -0
- package/dist/extensions/prompts.js +88 -0
- package/dist/extensions/resources.d.ts +26 -0
- package/dist/extensions/resources.js +221 -0
- package/dist/extensions/services.d.ts +31 -0
- package/dist/extensions/services.js +61 -0
- package/dist/extensions/session-api.d.ts +6 -0
- package/dist/extensions/session-api.js +52 -0
- package/dist/extensions/session-input.d.ts +184 -0
- package/dist/extensions/session-input.js +422 -0
- package/dist/extensions/session-messages.d.ts +41 -0
- package/dist/extensions/session-messages.js +76 -0
- package/dist/extensions/session-tools.d.ts +32 -0
- package/dist/extensions/session-tools.js +208 -0
- package/dist/extensions/session.d.ts +60 -0
- package/dist/extensions/session.js +265 -0
- package/dist/extensions/settings.d.ts +76 -0
- package/dist/extensions/settings.js +115 -0
- package/dist/extensions/store.d.ts +10 -0
- package/dist/extensions/store.js +131 -0
- package/dist/extensions/tool-runtime.d.ts +16 -0
- package/dist/extensions/tools.d.ts +80 -0
- package/dist/extensions/tools.js +270 -0
- package/dist/extensions.d.ts +22 -0
- package/dist/host/contracts.d.ts +58 -0
- package/dist/host/contracts.js +18 -0
- package/dist/host/description.d.ts +19 -0
- package/dist/host/local-agent-host.d.ts +96 -0
- package/dist/host/local-agent-host.js +429 -0
- package/dist/host/local-exec-tool.d.ts +26 -0
- package/dist/host/local-exec-tool.js +686 -0
- package/dist/host/local-file-access.d.ts +30 -0
- package/dist/host/local-file-access.js +107 -0
- package/dist/host/local-network-access.d.ts +6 -0
- package/dist/host/local-network-access.js +101 -0
- package/dist/host/local-process-executor.d.ts +5 -0
- package/dist/host/local-process-executor.js +17 -0
- package/dist/host/local-session-environment.d.ts +18 -0
- package/dist/host/local-shell.d.ts +3 -0
- package/dist/host/local-tool-registry.d.ts +34 -0
- package/dist/host/local-tool-registry.js +134 -0
- package/dist/host/network.d.ts +5 -0
- package/dist/host/network.js +42 -0
- package/dist/host/process-control.d.ts +2 -0
- package/dist/host/process-control.js +26 -0
- package/dist/host/skill-contract.d.ts +6 -0
- package/dist/host.d.ts +17 -0
- package/dist/host.js +3 -0
- package/dist/index.d.ts +26 -51
- package/dist/index.js +9 -12
- package/dist/model/anthropic-messages.d.ts +10 -0
- package/dist/model/anthropic-messages.js +692 -0
- package/dist/model/anthropic-thinking.js +33 -0
- package/dist/model/configuration.d.ts +111 -0
- package/dist/model/configuration.js +201 -0
- package/dist/model/contracts.d.ts +229 -0
- package/dist/model/http.d.ts +13 -0
- package/dist/model/http.js +119 -0
- package/dist/model/image-contract.d.ts +10 -0
- package/dist/model/image.d.ts +9 -0
- package/dist/model/inline-media-budget.d.ts +7 -0
- package/dist/model/inline-media-budget.js +36 -0
- package/dist/model/input-images.d.ts +3 -0
- package/dist/model/input-images.js +16 -0
- package/dist/model/openai-responses.d.ts +59 -0
- package/dist/model/openai-responses.js +1586 -0
- package/dist/model/recovery.js +80 -0
- package/dist/model/request-configuration.d.ts +17 -0
- package/dist/model/request-configuration.js +47 -0
- package/dist/model/responses-event-projection.d.ts +24 -0
- package/dist/model/responses-event-projection.js +194 -0
- package/dist/model/responses-request.d.ts +5 -0
- package/dist/model/responses-request.js +363 -0
- package/dist/model/responses-session-cache.d.ts +33 -0
- package/dist/model/responses-session-cache.js +64 -0
- package/dist/model/runtime-credential.d.ts +1 -0
- package/dist/model/runtime-credential.js +1 -0
- package/dist/model/sse.js +281 -0
- package/dist/model/text-blocks.js +40 -0
- package/dist/model/tool-declarations.d.ts +4 -0
- package/dist/model/tool-definition-projection.d.ts +6 -0
- package/dist/model/tool-definition-projection.js +11 -0
- package/dist/model/tool-schema.d.ts +2 -0
- package/dist/model/tool-schema.js +17 -0
- package/dist/model/transport-test.d.ts +10 -0
- package/dist/model/transport-test.js +27 -0
- package/dist/model/usage-observer.d.ts +5 -0
- package/dist/model/usage.d.ts +40 -0
- package/dist/network/credential-runtime.d.ts +8 -0
- package/dist/network/credential-runtime.js +72 -0
- package/dist/network/credentials.d.ts +20 -0
- package/dist/network/credentials.js +18 -0
- package/dist/network/http.d.ts +20 -0
- package/dist/network/http.js +421 -0
- package/dist/network/product-user-agent.d.ts +5 -0
- package/dist/network/product-user-agent.js +22 -0
- package/dist/network/transport-error.d.ts +5 -0
- package/dist/network/transport-error.js +31 -0
- package/dist/process/contracts.d.ts +26 -0
- package/dist/prompts/index.d.ts +20 -0
- package/dist/prompts/index.js +49 -0
- package/dist/prompts/prompt-sections.d.ts +6 -0
- package/dist/prompts/prompt-sections.js +6 -0
- package/dist/prompts/read-context.js +152 -0
- package/dist/runtime/compaction/contracts.d.ts +23 -0
- package/dist/runtime/compaction/coordinator.js +88 -0
- package/dist/runtime/compaction/generate.d.ts +31 -0
- package/dist/runtime/compaction/generate.js +84 -0
- package/dist/runtime/compaction/history.d.ts +16 -0
- package/dist/runtime/compaction/operation.d.ts +30 -0
- package/dist/runtime/compaction/operation.js +131 -0
- package/dist/runtime/compaction/requests.d.ts +16 -0
- package/dist/runtime/compaction/requests.js +114 -0
- package/dist/runtime/compaction/retained-context.d.ts +6 -0
- package/dist/runtime/compaction/retained-context.js +48 -0
- package/dist/runtime/compaction/smart.d.ts +14 -0
- package/dist/runtime/compaction/smart.js +59 -0
- package/dist/runtime/context/contextual-blocks.d.ts +31 -0
- package/dist/runtime/context/contextual-blocks.js +158 -0
- package/dist/runtime/context/environment.d.ts +5 -0
- package/dist/runtime/context/gc-candidates-code.d.ts +6 -0
- package/dist/runtime/context/gc-candidates-code.js +102 -0
- package/dist/runtime/context/gc-candidates-exec.d.ts +6 -0
- package/dist/runtime/context/gc-candidates-exec.js +131 -0
- package/dist/runtime/context/gc-candidates-read-patch.d.ts +7 -0
- package/dist/runtime/context/gc-candidates-read-patch.js +118 -0
- package/dist/runtime/context/gc-candidates-subagent.d.ts +6 -0
- package/dist/runtime/context/gc-candidates-subagent.js +100 -0
- package/dist/runtime/context/gc-candidates-web.d.ts +6 -0
- package/dist/runtime/context/gc-candidates-web.js +58 -0
- package/dist/runtime/context/gc-candidates.d.ts +12 -0
- package/dist/runtime/context/gc-candidates.js +192 -0
- package/dist/runtime/context/gc-contracts.d.ts +57 -0
- package/dist/runtime/context/gc-economics.d.ts +48 -0
- package/dist/runtime/context/gc-economics.js +161 -0
- package/dist/runtime/context/gc-runtime.d.ts +43 -0
- package/dist/runtime/context/gc-tokens.d.ts +26 -0
- package/dist/runtime/context/gc-tokens.js +106 -0
- package/dist/runtime/context/gc.d.ts +7 -0
- package/dist/runtime/context/gc.js +552 -0
- package/dist/runtime/context/instructions.d.ts +37 -0
- package/dist/runtime/context/instructions.js +297 -0
- package/dist/runtime/context/normalize.d.ts +5 -0
- package/dist/runtime/context/projection.d.ts +23 -0
- package/dist/runtime/context/projection.js +284 -0
- package/dist/runtime/context/request-budget.d.ts +5 -0
- package/dist/runtime/context/request-budget.js +25 -0
- package/dist/runtime/context/restore.d.ts +27 -0
- package/dist/runtime/context/restore.js +186 -0
- package/dist/runtime/context/usage.d.ts +17 -0
- package/dist/runtime/context/usage.js +124 -0
- package/dist/runtime/context/world-state.d.ts +34 -0
- package/dist/runtime/context/world-state.js +232 -0
- package/dist/runtime/contracts.d.ts +29 -0
- package/dist/runtime/event-projection.d.ts +14 -0
- package/dist/runtime/event-projection.js +87 -0
- package/dist/runtime/events.d.ts +195 -0
- package/dist/runtime/execution-handle.d.ts +12 -0
- package/dist/runtime/execution-handle.js +1 -0
- package/dist/runtime/execution.d.ts +55 -0
- package/dist/runtime/execution.js +444 -0
- package/dist/runtime/model-progress.js +85 -0
- package/dist/runtime/resource-publication.d.ts +27 -0
- package/dist/runtime/resource-publication.js +60 -0
- package/dist/runtime/tool-round-contract.d.ts +19 -0
- package/dist/runtime/tool-round-contract.js +1 -0
- package/dist/runtime/wait.js +82 -0
- package/dist/sandbox/linux/bubblewrap-sandbox-executor.d.ts +1 -1
- package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +8 -8
- package/dist/sdk/agent.d.ts +7 -0
- package/dist/sdk/agent.js +321 -0
- package/dist/sdk/execution-code-mode.d.ts +18 -0
- package/dist/sdk/execution-code-mode.js +51 -0
- package/dist/sdk/execution-compact-commit.d.ts +23 -0
- package/dist/sdk/execution-compact-commit.js +41 -0
- package/dist/sdk/execution-compact-extension.d.ts +12 -0
- package/dist/sdk/execution-compact-extension.js +46 -0
- package/dist/sdk/execution-compact-generation.d.ts +17 -0
- package/dist/sdk/execution-compact-generation.js +30 -0
- package/dist/sdk/execution-compaction-flight.d.ts +31 -0
- package/dist/sdk/execution-compaction-flight.js +50 -0
- package/dist/sdk/execution-compaction.d.ts +47 -0
- package/dist/sdk/execution-compaction.js +121 -0
- package/dist/sdk/execution-context-coordinator.d.ts +56 -0
- package/dist/sdk/execution-context-coordinator.js +102 -0
- package/dist/sdk/execution-input.d.ts +58 -0
- package/dist/sdk/execution-input.js +266 -0
- package/dist/sdk/execution-model-context.d.ts +52 -0
- package/dist/sdk/execution-model-context.js +156 -0
- package/dist/sdk/execution-model-lifecycle.d.ts +39 -0
- package/dist/sdk/execution-model-lifecycle.js +73 -0
- package/dist/sdk/execution-model-request.d.ts +40 -0
- package/dist/sdk/execution-model-request.js +45 -0
- package/dist/sdk/execution-round-preparation.d.ts +11 -0
- package/dist/sdk/execution-round-preparation.js +18 -0
- package/dist/sdk/execution-round-request.d.ts +30 -0
- package/dist/sdk/execution-round-request.js +31 -0
- package/dist/sdk/execution-tool-catalog.d.ts +46 -0
- package/dist/sdk/execution-tool-catalog.js +103 -0
- package/dist/sdk/execution-tool-scope.d.ts +67 -0
- package/dist/sdk/execution-tool-scope.js +147 -0
- package/dist/sdk/extension-agent-controller.d.ts +3 -0
- package/dist/sdk/extension-agent-controller.js +69 -0
- package/dist/sdk/extension-agents.d.ts +9 -0
- package/dist/sdk/extension-agents.js +58 -0
- package/dist/sdk/extension-models.d.ts +17 -0
- package/dist/sdk/extension-models.js +142 -0
- package/dist/sdk/extension-session-launch.d.ts +18 -0
- package/dist/sdk/extension-session-launch.js +73 -0
- package/dist/sdk/extension-session.d.ts +30 -0
- package/dist/sdk/extension-session.js +107 -0
- package/dist/sdk/extension-tool-runtime.d.ts +2 -0
- package/dist/sdk/extension-tool-runtime.js +14 -0
- package/dist/sdk/extension-tools.d.ts +16 -0
- package/dist/sdk/extension-tools.js +126 -0
- package/dist/sdk/model-input-projection.d.ts +4 -0
- package/dist/sdk/model-input-projection.js +27 -0
- package/dist/sdk/model-runtime.d.ts +21 -0
- package/dist/sdk/model-runtime.js +91 -0
- package/dist/sdk/native-steer.d.ts +20 -0
- package/dist/sdk/native-steer.js +84 -0
- package/dist/sdk/request-preparation.d.ts +9 -0
- package/dist/sdk/resource-input.d.ts +33 -0
- package/dist/sdk/resource-input.js +402 -0
- package/dist/sdk/session-catalog.d.ts +2 -0
- package/dist/sdk/session-catalog.js +12 -0
- package/dist/sdk/session-composition.d.ts +54 -0
- package/dist/sdk/session-composition.js +1 -0
- package/dist/sdk/session-execution-factory.d.ts +43 -0
- package/dist/sdk/session-execution-factory.js +937 -0
- package/dist/sdk/session-execution.d.ts +163 -0
- package/dist/sdk/session-execution.js +388 -0
- package/dist/sdk/session-extension-messages.d.ts +16 -0
- package/dist/sdk/session-extension-messages.js +64 -0
- package/dist/sdk/session-extension-prompt.d.ts +35 -0
- package/dist/sdk/session-extension-prompt.js +62 -0
- package/dist/sdk/session-maintenance.d.ts +25 -0
- package/dist/sdk/session-maintenance.js +205 -0
- package/dist/sdk/session-model-adapters.d.ts +28 -0
- package/dist/sdk/session-model-adapters.js +96 -0
- package/dist/sdk/session-open.d.ts +22 -0
- package/dist/sdk/session-open.js +44 -0
- package/dist/sdk/session-registry.d.ts +17 -0
- package/dist/sdk/session-registry.js +83 -0
- package/dist/sdk/session-runtime.d.ts +72 -0
- package/dist/sdk/session-runtime.js +205 -0
- package/dist/sdk/session.d.ts +3 -0
- package/dist/sdk/session.js +313 -0
- package/dist/sdk/subagent-backend.d.ts +3 -0
- package/dist/sdk/subagent-backend.js +49 -0
- package/dist/sdk/types.d.ts +80 -0
- package/dist/sdk/types.js +1 -0
- package/dist/session/catalog.d.ts +70 -0
- package/dist/session/catalog.js +194 -0
- package/dist/session/code-observation-contract.d.ts +40 -0
- package/dist/session/code-observation-contract.js +1 -0
- package/dist/session/code-observation-projection.d.ts +54 -0
- package/dist/session/code-observation-projection.js +165 -0
- package/dist/session/code-state.d.ts +27 -0
- package/dist/session/code-state.js +89 -0
- package/dist/session/context-gc-checkpoint.d.ts +26 -0
- package/dist/session/context-gc-checkpoint.js +935 -0
- package/dist/session/context-gc-contract.d.ts +141 -0
- package/dist/session/context-gc-contract.js +1 -0
- package/dist/session/context-gc-observations.d.ts +58 -0
- package/dist/session/context-gc-observations.js +187 -0
- package/dist/session/context-gc-replacements.d.ts +63 -0
- package/dist/session/context-gc-subagent.d.ts +54 -0
- package/dist/session/deferred-input.d.ts +5 -0
- package/dist/session/deferred-input.js +74 -0
- package/dist/session/extension-checkpoint.d.ts +8 -0
- package/dist/session/extension-checkpoint.js +30 -0
- package/dist/session/history-input-limits.d.ts +4 -0
- package/dist/session/history-input-limits.js +4 -0
- package/dist/session/inline-media-contract.d.ts +16 -0
- package/dist/session/inline-media-contract.js +1 -0
- package/dist/session/inline-media.d.ts +26 -0
- package/dist/session/inline-media.js +138 -0
- package/dist/session/model-request.d.ts +14 -0
- package/dist/session/model-request.js +1 -0
- package/dist/session/record-contract.d.ts +19 -0
- package/dist/session/record-contract.js +1 -0
- package/dist/session/record-projection.d.ts +6 -0
- package/dist/session/record-projection.js +80 -0
- package/dist/session/resource-contract.d.ts +9 -0
- package/dist/session/resource-contract.js +1 -0
- package/dist/session/rollback.d.ts +13 -0
- package/dist/session/rollback.js +46 -0
- package/dist/session/store/contracts.d.ts +160 -0
- package/dist/session/store/contracts.js +1 -0
- package/dist/session/store/current-state.d.ts +17 -0
- package/dist/session/store/current-state.js +53 -0
- package/dist/session/store/execution-state.d.ts +13 -0
- package/dist/session/store/execution-state.js +103 -0
- package/dist/session/store/index.d.ts +4 -0
- package/dist/session/store/index.js +2 -0
- package/dist/session/store/jsonl/directory-index.d.ts +26 -0
- package/dist/session/store/jsonl/directory-index.js +283 -0
- package/dist/session/store/jsonl/fork-session.d.ts +19 -0
- package/dist/session/store/jsonl/fork-session.js +142 -0
- package/dist/session/store/jsonl/fs-durability.d.ts +3 -0
- package/dist/session/store/jsonl/fs-durability.js +17 -0
- package/dist/session/store/jsonl/history-replacement.d.ts +48 -0
- package/dist/session/store/jsonl/history-replacement.js +147 -0
- package/dist/session/store/jsonl/history.d.ts +25 -0
- package/dist/session/store/jsonl/history.js +181 -0
- package/dist/session/store/jsonl/images.d.ts +19 -0
- package/dist/session/store/jsonl/images.js +122 -0
- package/dist/session/store/jsonl/index-transaction.d.ts +13 -0
- package/dist/session/store/jsonl/index-transaction.js +87 -0
- package/dist/session/store/jsonl/jsonl-admission.d.ts +10 -0
- package/dist/session/store/jsonl/jsonl-admission.js +205 -0
- package/dist/session/store/jsonl/jsonl-codec.d.ts +11 -0
- package/dist/session/store/jsonl/jsonl-codec.js +138 -0
- package/dist/session/store/jsonl/jsonl-migration.d.ts +12 -0
- package/dist/session/store/jsonl/jsonl-migration.js +105 -0
- package/dist/session/store/jsonl/jsonl-store.d.ts +171 -0
- package/dist/session/store/jsonl/jsonl-store.js +2395 -0
- package/dist/session/store/jsonl/open-recovery.d.ts +13 -0
- package/dist/session/store/jsonl/open-recovery.js +131 -0
- package/dist/session/store/jsonl/read-pins.d.ts +4 -0
- package/dist/session/store/jsonl/read-pins.js +131 -0
- package/dist/session/store/jsonl/record-append.d.ts +16 -0
- package/dist/session/store/jsonl/record-append.js +49 -0
- package/dist/session/store/jsonl/records.d.ts +30 -0
- package/dist/session/store/jsonl/records.js +313 -0
- package/dist/session/store/jsonl/resources.d.ts +19 -0
- package/dist/session/store/jsonl/resources.js +209 -0
- package/dist/session/store/jsonl/store-pool.d.ts +12 -0
- package/dist/session/store/jsonl/store-pool.js +55 -0
- package/dist/session/store/jsonl/transaction-recovery.d.ts +48 -0
- package/dist/session/store/jsonl/transaction-recovery.js +374 -0
- package/dist/session/store/jsonl/tree-maintenance.d.ts +12 -0
- package/dist/session/store/jsonl/tree-maintenance.js +59 -0
- package/dist/session/store/types.d.ts +69 -0
- package/dist/session/store/types.js +1 -0
- package/dist/session/summary.d.ts +18 -0
- package/dist/session/summary.js +1 -0
- package/dist/session/todo.d.ts +11 -0
- package/dist/session/todo.js +30 -0
- package/dist/session/tool-result-resources.d.ts +27 -0
- package/dist/session/tool-result-resources.js +255 -0
- package/dist/session/types.js +1 -0
- package/dist/session/user-history.d.ts +17 -0
- package/dist/session/user-history.js +1 -0
- package/dist/session-store.d.ts +2 -0
- package/dist/session-store.js +1 -0
- package/dist/shared/auto-title.d.ts +3 -3
- package/dist/shared/auto-title.js +6 -6
- package/dist/shared/model-text.d.ts +8 -8
- package/dist/shared/model-text.js +8 -7
- package/dist/storage/file-lock.js +83 -0
- package/dist/subagent/contracts.d.ts +107 -0
- package/dist/subagent/contracts.js +1 -0
- package/dist/subagent/limits.d.ts +17 -0
- package/dist/subagent/limits.js +17 -0
- package/dist/subagent/model-update.js +81 -0
- package/dist/subagent/session-controller.d.ts +87 -0
- package/dist/subagent/session-controller.js +1343 -0
- package/dist/subagent/task.d.ts +134 -0
- package/dist/subagent/task.js +436 -0
- package/dist/subagent/turn-contracts.d.ts +64 -0
- package/dist/subagent/turn-contracts.js +1 -0
- package/dist/subagent/wait-targets.d.ts +10 -0
- package/dist/subagent/wait-targets.js +26 -0
- package/dist/subagent/wait.d.ts +26 -0
- package/dist/subagent/wait.js +73 -0
- package/dist/tools/agent/agent-cancel.d.ts +7 -0
- package/dist/tools/agent/agent-cancel.js +29 -0
- package/dist/tools/agent/agent-message.d.ts +7 -0
- package/dist/tools/agent/agent-message.js +57 -0
- package/dist/tools/agent/agent-start.d.ts +12 -0
- package/dist/tools/agent/agent-start.js +106 -0
- package/dist/tools/agent/agent-wait.d.ts +7 -0
- package/dist/tools/agent/agent-wait.js +91 -0
- package/dist/tools/agent/question.d.ts +7 -0
- package/dist/tools/agent/question.js +125 -0
- package/dist/tools/agent/skill.d.ts +11 -0
- package/dist/tools/agent/skill.js +38 -0
- package/dist/tools/agent/sleep.d.ts +9 -0
- package/dist/tools/agent/sleep.js +54 -0
- package/dist/tools/agent/smart-compact.d.ts +9 -0
- package/dist/tools/agent/smart-compact.js +23 -0
- package/dist/tools/agent/todo-read.d.ts +14 -0
- package/dist/tools/agent/todo-write.d.ts +14 -0
- package/dist/tools/agent/todo-write.js +66 -0
- package/dist/tools/agent/todo.d.ts +25 -0
- package/dist/tools/agent/todo.js +32 -0
- package/dist/tools/callers.d.ts +7 -0
- package/dist/tools/callers.js +39 -0
- package/dist/tools/code-mode/cells.d.ts +29 -0
- package/dist/tools/code-mode/cells.js +338 -0
- package/dist/tools/code-mode/dispatch.d.ts +20 -0
- package/dist/tools/code-mode/dispatch.js +80 -0
- package/dist/tools/code-mode/engine.d.ts +60 -0
- package/dist/tools/code-mode/engine.js +1 -0
- package/dist/tools/code-mode/index.d.ts +31 -0
- package/dist/tools/code-mode/index.js +751 -0
- package/dist/tools/code-mode/observations.d.ts +8 -0
- package/dist/tools/code-mode/observations.js +64 -0
- package/dist/tools/code-mode/worker.d.ts +1 -0
- package/dist/tools/contracts.d.ts +193 -0
- package/dist/tools/contracts.js +1 -0
- package/dist/tools/definition-projection.d.ts +1 -0
- package/dist/tools/definition-projection.js +1 -0
- package/dist/tools/execution-limits.d.ts +69 -0
- package/dist/tools/execution-limits.js +58 -0
- package/dist/tools/execution.d.ts +30 -0
- package/dist/tools/execution.js +191 -0
- package/dist/tools/local/apply-patch-parser.js +177 -0
- package/dist/tools/local/apply-patch.d.ts +19 -0
- package/dist/tools/local/apply-patch.js +489 -0
- package/dist/tools/local/exec.d.ts +28 -0
- package/dist/tools/local/exec.js +194 -0
- package/dist/tools/local/image-processing.js +146 -0
- package/dist/tools/local/read.d.ts +43 -0
- package/dist/tools/local/read.js +470 -0
- package/dist/tools/local/view-image.d.ts +15 -0
- package/dist/tools/local/view-image.js +317 -0
- package/dist/tools/mcp/catalog.js +220 -0
- package/dist/tools/mcp/history.d.ts +4 -0
- package/dist/tools/mcp/tool-search.d.ts +45 -0
- package/dist/tools/mcp/tool-search.js +294 -0
- package/dist/tools/output-projection.d.ts +18 -0
- package/dist/tools/output-projection.js +341 -0
- package/dist/tools/permission-contract.d.ts +25 -0
- package/dist/tools/permission-contract.js +1 -0
- package/dist/tools/permissions.d.ts +61 -0
- package/dist/tools/permissions.js +237 -0
- package/dist/tools/registry.d.ts +7 -0
- package/dist/tools/result-extension-contract.d.ts +37 -0
- package/dist/tools/result-extension-contract.js +32 -0
- package/dist/tools/result-extensions.d.ts +12 -0
- package/dist/tools/result-extensions.js +104 -0
- package/dist/tools/runtime-context.d.ts +89 -0
- package/dist/tools/runtime-context.js +1 -0
- package/dist/tools/runtime-resource-handles.js +28 -0
- package/dist/tools/runtime.js +77 -0
- package/dist/tools/scheduler.d.ts +12 -0
- package/dist/tools/shared/path-resolver.js +133 -0
- package/dist/tools/wait.d.ts +1 -0
- package/dist/tools/wait.js +1 -0
- package/package.json +15 -3
- package/dist/agent-core/error.d.ts +0 -15
- package/dist/agent-core/error.js +0 -74
- package/dist/agent-core/extensions/agents.d.ts +0 -79
- package/dist/agent-core/extensions/agents.js +0 -186
- package/dist/agent-core/extensions/compaction.d.ts +0 -48
- package/dist/agent-core/extensions/contracts.d.ts +0 -265
- package/dist/agent-core/extensions/data.d.ts +0 -84
- package/dist/agent-core/extensions/data.js +0 -83
- package/dist/agent-core/extensions/environment.d.ts +0 -27
- package/dist/agent-core/extensions/environment.js +0 -52
- package/dist/agent-core/extensions/files.d.ts +0 -64
- package/dist/agent-core/extensions/files.js +0 -26
- package/dist/agent-core/extensions/hooks.d.ts +0 -32
- package/dist/agent-core/extensions/manager.d.ts +0 -39
- package/dist/agent-core/extensions/manager.js +0 -458
- package/dist/agent-core/extensions/mcp.d.ts +0 -99
- package/dist/agent-core/extensions/mcp.js +0 -433
- package/dist/agent-core/extensions/model-request.d.ts +0 -56
- package/dist/agent-core/extensions/model-request.js +0 -197
- package/dist/agent-core/extensions/model-stream.d.ts +0 -9
- package/dist/agent-core/extensions/model-stream.js +0 -108
- package/dist/agent-core/extensions/models.d.ts +0 -43
- package/dist/agent-core/extensions/network.d.ts +0 -32
- package/dist/agent-core/extensions/network.js +0 -97
- package/dist/agent-core/extensions/operation.d.ts +0 -30
- package/dist/agent-core/extensions/processes.d.ts +0 -52
- package/dist/agent-core/extensions/processes.js +0 -284
- package/dist/agent-core/extensions/prompt-composition.d.ts +0 -24
- package/dist/agent-core/extensions/prompt-composition.js +0 -106
- package/dist/agent-core/extensions/prompt-input.d.ts +0 -23
- package/dist/agent-core/extensions/prompt-input.js +0 -48
- package/dist/agent-core/extensions/resources.d.ts +0 -27
- package/dist/agent-core/extensions/resources.js +0 -221
- package/dist/agent-core/extensions/services.d.ts +0 -31
- package/dist/agent-core/extensions/services.js +0 -61
- package/dist/agent-core/extensions/session-input.d.ts +0 -163
- package/dist/agent-core/extensions/session-input.js +0 -411
- package/dist/agent-core/extensions/session-messages.d.ts +0 -41
- package/dist/agent-core/extensions/session-messages.js +0 -76
- package/dist/agent-core/extensions/session.d.ts +0 -69
- package/dist/agent-core/extensions/session.js +0 -607
- package/dist/agent-core/extensions/settings.d.ts +0 -59
- package/dist/agent-core/extensions/settings.js +0 -115
- package/dist/agent-core/extensions/store.d.ts +0 -10
- package/dist/agent-core/extensions/store.js +0 -131
- package/dist/agent-core/extensions/tools.d.ts +0 -61
- package/dist/agent-core/extensions/tools.js +0 -269
- package/dist/agent-core/host/contracts.d.ts +0 -100
- package/dist/agent-core/host/contracts.js +0 -18
- package/dist/agent-core/host/local-agent-host.d.ts +0 -96
- package/dist/agent-core/host/local-agent-host.js +0 -425
- package/dist/agent-core/host/local-file-access.d.ts +0 -30
- package/dist/agent-core/host/local-file-access.js +0 -107
- package/dist/agent-core/host/local-network-access.d.ts +0 -6
- package/dist/agent-core/host/local-network-access.js +0 -101
- package/dist/agent-core/host/local-process-executor.d.ts +0 -29
- package/dist/agent-core/host/local-process-executor.js +0 -41
- package/dist/agent-core/host/local-session-environment.d.ts +0 -18
- package/dist/agent-core/host/local-shell.d.ts +0 -3
- package/dist/agent-core/host/network.d.ts +0 -4
- package/dist/agent-core/host/network.js +0 -6
- package/dist/agent-core/model/anthropic-messages.d.ts +0 -10
- package/dist/agent-core/model/anthropic-messages.js +0 -707
- package/dist/agent-core/model/anthropic-thinking.js +0 -33
- package/dist/agent-core/model/catalog-file.d.ts +0 -80
- package/dist/agent-core/model/catalog-file.js +0 -103
- package/dist/agent-core/model/configuration.d.ts +0 -127
- package/dist/agent-core/model/configuration.js +0 -168
- package/dist/agent-core/model/contracts.d.ts +0 -229
- package/dist/agent-core/model/http.d.ts +0 -27
- package/dist/agent-core/model/http.js +0 -534
- package/dist/agent-core/model/image.d.ts +0 -17
- package/dist/agent-core/model/openai-responses.d.ts +0 -77
- package/dist/agent-core/model/openai-responses.js +0 -2123
- package/dist/agent-core/model/product-user-agent.d.ts +0 -5
- package/dist/agent-core/model/product-user-agent.js +0 -22
- package/dist/agent-core/model/recovery.js +0 -80
- package/dist/agent-core/model/request-configuration.d.ts +0 -17
- package/dist/agent-core/model/request-configuration.js +0 -47
- package/dist/agent-core/model/runtime-credential.d.ts +0 -8
- package/dist/agent-core/model/runtime-credential.js +0 -72
- package/dist/agent-core/model/sse.js +0 -281
- package/dist/agent-core/model/text-blocks.js +0 -40
- package/dist/agent-core/model/tool-declarations.d.ts +0 -4
- package/dist/agent-core/model/transport-test.d.ts +0 -3
- package/dist/agent-core/model/transport-test.js +0 -23
- package/dist/agent-core/model/usage-observer.d.ts +0 -31
- package/dist/agent-core/prompts/index.d.ts +0 -20
- package/dist/agent-core/prompts/index.js +0 -49
- package/dist/agent-core/runtime/compaction/coordinator.js +0 -88
- package/dist/agent-core/runtime/compaction/generate.d.ts +0 -28
- package/dist/agent-core/runtime/compaction/generate.js +0 -84
- package/dist/agent-core/runtime/compaction/history.d.ts +0 -16
- package/dist/agent-core/runtime/compaction/operation.d.ts +0 -29
- package/dist/agent-core/runtime/compaction/operation.js +0 -131
- package/dist/agent-core/runtime/compaction/requests.d.ts +0 -16
- package/dist/agent-core/runtime/compaction/requests.js +0 -114
- package/dist/agent-core/runtime/compaction/retained-context.d.ts +0 -6
- package/dist/agent-core/runtime/compaction/retained-context.js +0 -47
- package/dist/agent-core/runtime/compaction/smart.d.ts +0 -14
- package/dist/agent-core/runtime/compaction/smart.js +0 -59
- package/dist/agent-core/runtime/context/contextual-blocks.d.ts +0 -30
- package/dist/agent-core/runtime/context/contextual-blocks.js +0 -146
- package/dist/agent-core/runtime/context/environment.d.ts +0 -5
- package/dist/agent-core/runtime/context/gc-runtime.d.ts +0 -38
- package/dist/agent-core/runtime/context/gc-tokens.d.ts +0 -25
- package/dist/agent-core/runtime/context/gc-tokens.js +0 -103
- package/dist/agent-core/runtime/context/gc.d.ts +0 -75
- package/dist/agent-core/runtime/context/gc.js +0 -1386
- package/dist/agent-core/runtime/context/instructions.d.ts +0 -37
- package/dist/agent-core/runtime/context/instructions.js +0 -297
- package/dist/agent-core/runtime/context/normalize.d.ts +0 -5
- package/dist/agent-core/runtime/context/projection.d.ts +0 -23
- package/dist/agent-core/runtime/context/projection.js +0 -284
- package/dist/agent-core/runtime/context/request-budget.d.ts +0 -4
- package/dist/agent-core/runtime/context/request-budget.js +0 -25
- package/dist/agent-core/runtime/context/restore.d.ts +0 -25
- package/dist/agent-core/runtime/context/restore.js +0 -186
- package/dist/agent-core/runtime/context/usage.d.ts +0 -17
- package/dist/agent-core/runtime/context/usage.js +0 -124
- package/dist/agent-core/runtime/context/world-state.d.ts +0 -33
- package/dist/agent-core/runtime/context/world-state.js +0 -232
- package/dist/agent-core/runtime/contracts.d.ts +0 -41
- package/dist/agent-core/runtime/event-projection.d.ts +0 -14
- package/dist/agent-core/runtime/event-projection.js +0 -87
- package/dist/agent-core/runtime/events.d.ts +0 -197
- package/dist/agent-core/runtime/execution-handle.d.ts +0 -12
- package/dist/agent-core/runtime/execution.d.ts +0 -50
- package/dist/agent-core/runtime/execution.js +0 -445
- package/dist/agent-core/runtime/model-progress.js +0 -85
- package/dist/agent-core/runtime/resource-publication.d.ts +0 -8
- package/dist/agent-core/runtime/resource-publication.js +0 -60
- package/dist/agent-core/sdk/agent.d.ts +0 -11
- package/dist/agent-core/sdk/agent.js +0 -474
- package/dist/agent-core/sdk/extension-agents.d.ts +0 -9
- package/dist/agent-core/sdk/extension-agents.js +0 -58
- package/dist/agent-core/sdk/extension-models.d.ts +0 -13
- package/dist/agent-core/sdk/extension-models.js +0 -137
- package/dist/agent-core/sdk/extension-session.d.ts +0 -30
- package/dist/agent-core/sdk/extension-session.js +0 -105
- package/dist/agent-core/sdk/extension-tools.d.ts +0 -16
- package/dist/agent-core/sdk/extension-tools.js +0 -126
- package/dist/agent-core/sdk/model-runtime.d.ts +0 -18
- package/dist/agent-core/sdk/model-runtime.js +0 -93
- package/dist/agent-core/sdk/native-steer.d.ts +0 -20
- package/dist/agent-core/sdk/native-steer.js +0 -84
- package/dist/agent-core/sdk/request-preparation.d.ts +0 -7
- package/dist/agent-core/sdk/resource-input.d.ts +0 -32
- package/dist/agent-core/sdk/resource-input.js +0 -403
- package/dist/agent-core/sdk/session.d.ts +0 -94
- package/dist/agent-core/sdk/session.js +0 -2377
- package/dist/agent-core/sdk/subagent-backend.d.ts +0 -3
- package/dist/agent-core/sdk/subagent-backend.js +0 -49
- package/dist/agent-core/sdk/types.d.ts +0 -81
- package/dist/agent-core/session/catalog.d.ts +0 -99
- package/dist/agent-core/session/catalog.js +0 -200
- package/dist/agent-core/session/context-gc-checkpoint.d.ts +0 -165
- package/dist/agent-core/session/context-gc-checkpoint.js +0 -935
- package/dist/agent-core/session/context-gc-observations.d.ts +0 -60
- package/dist/agent-core/session/context-gc-observations.js +0 -187
- package/dist/agent-core/session/context-gc-replacements.d.ts +0 -62
- package/dist/agent-core/session/context-gc-subagent.d.ts +0 -53
- package/dist/agent-core/session/deferred-input.d.ts +0 -5
- package/dist/agent-core/session/deferred-input.js +0 -74
- package/dist/agent-core/session/inline-media.d.ts +0 -45
- package/dist/agent-core/session/inline-media.js +0 -163
- package/dist/agent-core/session/rollback.d.ts +0 -13
- package/dist/agent-core/session/rollback.js +0 -46
- package/dist/agent-core/session/store/contracts.d.ts +0 -158
- package/dist/agent-core/session/store/current-state.d.ts +0 -17
- package/dist/agent-core/session/store/current-state.js +0 -53
- package/dist/agent-core/session/store/directory-index.d.ts +0 -18
- package/dist/agent-core/session/store/directory-index.js +0 -169
- package/dist/agent-core/session/store/execution-state.d.ts +0 -18
- package/dist/agent-core/session/store/execution-state.js +0 -175
- package/dist/agent-core/session/store/history.d.ts +0 -25
- package/dist/agent-core/session/store/history.js +0 -181
- package/dist/agent-core/session/store/images.d.ts +0 -19
- package/dist/agent-core/session/store/images.js +0 -110
- package/dist/agent-core/session/store/index.d.ts +0 -4
- package/dist/agent-core/session/store/index.js +0 -2
- package/dist/agent-core/session/store/jsonl-store.d.ts +0 -171
- package/dist/agent-core/session/store/jsonl-store.js +0 -2078
- package/dist/agent-core/session/store/records.d.ts +0 -23
- package/dist/agent-core/session/store/records.js +0 -261
- package/dist/agent-core/session/store/resources.d.ts +0 -26
- package/dist/agent-core/session/store/resources.js +0 -197
- package/dist/agent-core/session/store/types.d.ts +0 -82
- package/dist/agent-core/session/tool-result-resources.d.ts +0 -26
- package/dist/agent-core/session/tool-result-resources.js +0 -256
- package/dist/agent-core/session/transaction-recovery.d.ts +0 -16
- package/dist/agent-core/session/transaction-recovery.js +0 -135
- package/dist/agent-core/storage/file-lock.js +0 -83
- package/dist/agent-core/subagent/contracts.d.ts +0 -117
- package/dist/agent-core/subagent/model-update.js +0 -81
- package/dist/agent-core/subagent/scheduler.d.ts +0 -135
- package/dist/agent-core/subagent/scheduler.js +0 -369
- package/dist/agent-core/subagent/session-controller.d.ts +0 -138
- package/dist/agent-core/subagent/session-controller.js +0 -1594
- package/dist/agent-core/subagent/wait.d.ts +0 -14
- package/dist/agent-core/subagent/wait.js +0 -32
- package/dist/agent-core/tools/agent/agent-cancel.d.ts +0 -7
- package/dist/agent-core/tools/agent/agent-cancel.js +0 -29
- package/dist/agent-core/tools/agent/agent-message.d.ts +0 -7
- package/dist/agent-core/tools/agent/agent-message.js +0 -57
- package/dist/agent-core/tools/agent/agent-start.d.ts +0 -12
- package/dist/agent-core/tools/agent/agent-start.js +0 -106
- package/dist/agent-core/tools/agent/agent-wait.d.ts +0 -7
- package/dist/agent-core/tools/agent/agent-wait.js +0 -91
- package/dist/agent-core/tools/agent/question.d.ts +0 -7
- package/dist/agent-core/tools/agent/question.js +0 -125
- package/dist/agent-core/tools/agent/skill.d.ts +0 -11
- package/dist/agent-core/tools/agent/skill.js +0 -38
- package/dist/agent-core/tools/agent/sleep.d.ts +0 -12
- package/dist/agent-core/tools/agent/sleep.js +0 -54
- package/dist/agent-core/tools/agent/smart-compact.d.ts +0 -9
- package/dist/agent-core/tools/agent/smart-compact.js +0 -23
- package/dist/agent-core/tools/agent/todo-read.d.ts +0 -14
- package/dist/agent-core/tools/agent/todo-write.d.ts +0 -14
- package/dist/agent-core/tools/agent/todo-write.js +0 -66
- package/dist/agent-core/tools/agent/todo.d.ts +0 -26
- package/dist/agent-core/tools/agent/todo.js +0 -60
- package/dist/agent-core/tools/callers.d.ts +0 -6
- package/dist/agent-core/tools/callers.js +0 -39
- package/dist/agent-core/tools/code-mode/cells.d.ts +0 -29
- package/dist/agent-core/tools/code-mode/cells.js +0 -337
- package/dist/agent-core/tools/code-mode/dispatch.d.ts +0 -20
- package/dist/agent-core/tools/code-mode/dispatch.js +0 -80
- package/dist/agent-core/tools/code-mode/engine.d.ts +0 -60
- package/dist/agent-core/tools/code-mode/index.d.ts +0 -29
- package/dist/agent-core/tools/code-mode/index.js +0 -749
- package/dist/agent-core/tools/code-mode/observations.d.ts +0 -98
- package/dist/agent-core/tools/code-mode/observations.js +0 -228
- package/dist/agent-core/tools/code-mode/state.d.ts +0 -9
- package/dist/agent-core/tools/code-mode/state.js +0 -82
- package/dist/agent-core/tools/contracts.d.ts +0 -211
- package/dist/agent-core/tools/definition-projection.d.ts +0 -7
- package/dist/agent-core/tools/definition-projection.js +0 -26
- package/dist/agent-core/tools/execution-limits.d.ts +0 -69
- package/dist/agent-core/tools/execution-limits.js +0 -69
- package/dist/agent-core/tools/execution.d.ts +0 -46
- package/dist/agent-core/tools/execution.js +0 -191
- package/dist/agent-core/tools/local/apply-patch-parser.js +0 -177
- package/dist/agent-core/tools/local/apply-patch.d.ts +0 -19
- package/dist/agent-core/tools/local/apply-patch.js +0 -489
- package/dist/agent-core/tools/local/exec.d.ts +0 -53
- package/dist/agent-core/tools/local/exec.js +0 -877
- package/dist/agent-core/tools/local/image-processing.js +0 -146
- package/dist/agent-core/tools/local/read-context.js +0 -152
- package/dist/agent-core/tools/local/read.d.ts +0 -43
- package/dist/agent-core/tools/local/read.js +0 -470
- package/dist/agent-core/tools/local/registry.d.ts +0 -34
- package/dist/agent-core/tools/local/registry.js +0 -132
- package/dist/agent-core/tools/local/view-image.d.ts +0 -15
- package/dist/agent-core/tools/local/view-image.js +0 -317
- package/dist/agent-core/tools/local/workspace-tools.d.ts +0 -12
- package/dist/agent-core/tools/local/workspace-tools.js +0 -28
- package/dist/agent-core/tools/mcp/catalog.js +0 -220
- package/dist/agent-core/tools/mcp/history.d.ts +0 -4
- package/dist/agent-core/tools/mcp/tool-search.d.ts +0 -45
- package/dist/agent-core/tools/mcp/tool-search.js +0 -294
- package/dist/agent-core/tools/output-projection.d.ts +0 -18
- package/dist/agent-core/tools/output-projection.js +0 -341
- package/dist/agent-core/tools/permissions.d.ts +0 -85
- package/dist/agent-core/tools/permissions.js +0 -237
- package/dist/agent-core/tools/registry.d.ts +0 -7
- package/dist/agent-core/tools/result-extensions.d.ts +0 -46
- package/dist/agent-core/tools/result-extensions.js +0 -134
- package/dist/agent-core/tools/runtime-context.d.ts +0 -74
- package/dist/agent-core/tools/runtime-resource-handles.js +0 -28
- package/dist/agent-core/tools/runtime.js +0 -77
- package/dist/agent-core/tools/scheduler.d.ts +0 -12
- package/dist/agent-core/tools/shared/path-resolver.js +0 -133
- package/dist/agent-core/tools/wait.js +0 -82
- /package/dist/{agent-core/extensions/models.js → extensions/capabilities.js} +0 -0
- /package/dist/{agent-core/extensions → extensions}/compaction.js +0 -0
- /package/dist/{agent-core/model → extensions}/contracts.js +0 -0
- /package/dist/{agent-core/extensions → extensions}/hooks.js +0 -0
- /package/dist/{agent-core/runtime/context/contracts.js → extensions/host-ports.js} +0 -0
- /package/dist/{agent-core/extensions → extensions}/operation.js +0 -0
- /package/dist/{agent-core/runtime/contracts.js → extensions/tool-runtime.js} +0 -0
- /package/dist/{agent-core/extensions/contracts.js → extensions.js} +0 -0
- /package/dist/{agent-core/runtime/events.js → host/description.js} +0 -0
- /package/dist/{agent-core/host → host}/local-session-environment.js +0 -0
- /package/dist/{agent-core/host → host}/local-shell.js +0 -0
- /package/dist/{agent-core/runtime/execution-handle.js → host/skill-contract.js} +0 -0
- /package/dist/{agent-core/model → model}/anthropic-thinking.d.ts +0 -0
- /package/dist/{agent-core/model → model}/attempt-diagnostics.d.ts +0 -0
- /package/dist/{agent-core/model → model}/attempt-diagnostics.js +0 -0
- /package/dist/{agent-core/session/store → model}/contracts.js +0 -0
- /package/dist/{agent-core/sdk/types.js → model/image-contract.js} +0 -0
- /package/dist/{agent-core/model → model}/image.js +0 -0
- /package/dist/{agent-core/model → model}/native-context.d.ts +0 -0
- /package/dist/{agent-core/model → model}/native-context.js +0 -0
- /package/dist/{agent-core/model → model}/recovery.d.ts +0 -0
- /package/dist/{agent-core/model → model}/sse.d.ts +0 -0
- /package/dist/{agent-core/model → model}/text-blocks.d.ts +0 -0
- /package/dist/{agent-core/model → model}/tool-declarations.js +0 -0
- /package/dist/{agent-core/model → model}/tool-input.d.ts +0 -0
- /package/dist/{agent-core/model → model}/tool-input.js +0 -0
- /package/dist/{agent-core/model → model}/usage-observer.js +0 -0
- /package/dist/{agent-core/session/store/types.js → model/usage.js} +0 -0
- /package/dist/{agent-core/subagent → process}/contracts.js +0 -0
- /package/dist/{agent-core/prompts → prompts}/compact.d.ts +0 -0
- /package/dist/{agent-core/prompts → prompts}/compact.js +0 -0
- /package/dist/{agent-core/prompts → prompts}/core.d.ts +0 -0
- /package/dist/{agent-core/prompts → prompts}/core.js +0 -0
- /package/dist/{agent-core/prompts → prompts}/markup.d.ts +0 -0
- /package/dist/{agent-core/prompts → prompts}/markup.js +0 -0
- /package/dist/{agent-core/prompts → prompts}/modes.d.ts +0 -0
- /package/dist/{agent-core/prompts → prompts}/modes.js +0 -0
- /package/dist/{agent-core/prompts → prompts}/output.d.ts +0 -0
- /package/dist/{agent-core/prompts → prompts}/output.js +0 -0
- /package/dist/{agent-core/prompts → prompts}/permissions.d.ts +0 -0
- /package/dist/{agent-core/prompts → prompts}/permissions.js +0 -0
- /package/dist/{agent-core/tools/local → prompts}/read-context.d.ts +0 -0
- /package/dist/{agent-core/prompts → prompts}/resources.d.ts +0 -0
- /package/dist/{agent-core/prompts → prompts}/resources.js +0 -0
- /package/dist/{agent-core/prompts → prompts}/subagent.d.ts +0 -0
- /package/dist/{agent-core/prompts → prompts}/subagent.js +0 -0
- /package/dist/{agent-core/prompts → prompts}/workflow.d.ts +0 -0
- /package/dist/{agent-core/prompts → prompts}/workflow.js +0 -0
- /package/dist/{agent-core/runtime → runtime}/async-queue.d.ts +0 -0
- /package/dist/{agent-core/runtime → runtime}/async-queue.js +0 -0
- /package/dist/{agent-core/tools → runtime/compaction}/contracts.js +0 -0
- /package/dist/{agent-core/runtime → runtime}/compaction/coordinator.d.ts +0 -0
- /package/dist/{agent-core/runtime → runtime}/compaction/history.js +0 -0
- /package/dist/{agent-core/runtime → runtime}/context/contracts.d.ts +0 -0
- /package/dist/{agent-core/session/types.js → runtime/context/contracts.js} +0 -0
- /package/dist/{agent-core/runtime → runtime}/context/environment.js +0 -0
- /package/dist/{agent-core/tools/code-mode/engine.js → runtime/context/gc-contracts.js} +0 -0
- /package/dist/{agent-core/runtime → runtime}/context/gc-runtime.js +0 -0
- /package/dist/{agent-core/runtime → runtime}/context/normalize.js +0 -0
- /package/dist/{agent-core/tools/code-mode/worker.d.ts → runtime/contracts.js} +0 -0
- /package/dist/{agent-core/tools/runtime-context.js → runtime/events.js} +0 -0
- /package/dist/{agent-core/runtime → runtime}/execution-policy.d.ts +0 -0
- /package/dist/{agent-core/runtime → runtime}/execution-policy.js +0 -0
- /package/dist/{agent-core/runtime → runtime}/model-generation-timing.d.ts +0 -0
- /package/dist/{agent-core/runtime → runtime}/model-generation-timing.js +0 -0
- /package/dist/{agent-core/runtime → runtime}/model-progress.d.ts +0 -0
- /package/dist/{agent-core/runtime → runtime}/plan-output.d.ts +0 -0
- /package/dist/{agent-core/runtime → runtime}/plan-output.js +0 -0
- /package/dist/{agent-core/tools/public-metadata.d.ts → runtime/public-tool-metadata.d.ts} +0 -0
- /package/dist/{agent-core/tools/public-metadata.js → runtime/public-tool-metadata.js} +0 -0
- /package/dist/{agent-core/runtime → runtime}/token-budget.d.ts +0 -0
- /package/dist/{agent-core/runtime → runtime}/token-budget.js +0 -0
- /package/dist/{agent-core/tools → runtime}/wait.d.ts +0 -0
- /package/dist/{agent-core/sdk → sdk}/request-preparation.js +0 -0
- /package/dist/{agent-core/session → session}/context-gc-markup.d.ts +0 -0
- /package/dist/{agent-core/session → session}/context-gc-markup.js +0 -0
- /package/dist/{agent-core/session → session}/context-gc-replacements.js +0 -0
- /package/dist/{agent-core/session → session}/context-gc-subagent.js +0 -0
- /package/dist/{agent-core/session → session}/types.d.ts +0 -0
- /package/dist/{agent-core/storage → storage}/file-lock.d.ts +0 -0
- /package/dist/{agent-core/subagent → subagent}/model-update.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/agent/todo-read.js +0 -0
- /package/dist/{agent-core/tools → tools}/code-mode/quickjs-engine.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/code-mode/quickjs-engine.js +0 -0
- /package/dist/{agent-core/tools → tools}/code-mode/text-output.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/code-mode/text-output.js +0 -0
- /package/dist/{agent-core/tools → tools}/code-mode/worker.js +0 -0
- /package/dist/{agent-core/tools → tools}/file-change-diff.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/file-change-diff.js +0 -0
- /package/dist/{agent-core/tools → tools}/input-diagnostics/source-preview.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/input-diagnostics/source-preview.js +0 -0
- /package/dist/{agent-core/tools → tools}/input.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/input.js +0 -0
- /package/dist/{agent-core/tools → tools}/local/apply-patch-parser.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/local/image-processing.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/mcp/catalog.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/mcp/history.js +0 -0
- /package/dist/{agent-core/tools → tools}/registry.js +0 -0
- /package/dist/{agent-core/tools → tools}/runtime-resource-handles.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/runtime.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/scheduler.js +0 -0
- /package/dist/{agent-core/tools → tools}/shared/patch-result.d.ts +0 -0
- /package/dist/{agent-core/tools → tools}/shared/patch-result.js +0 -0
- /package/dist/{agent-core/tools → tools}/shared/path-resolver.d.ts +0 -0
|
@@ -1,2078 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { rollbackBoundaryIndex } from '../rollback.js';
|
|
3
|
-
import { inlineMediaHistory } from '../inline-media.js';
|
|
4
|
-
import { lstat, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
5
|
-
import { basename, join } from 'node:path';
|
|
6
|
-
import { isDeepStrictEqual } from 'node:util';
|
|
7
|
-
import { MarAgentError } from '../../error.js';
|
|
8
|
-
import { isModelImageUrl } from '../../model/image.js';
|
|
9
|
-
import { cloneCoreJson } from '../../extensions/data.js';
|
|
10
|
-
import { acquireFileLock } from '../../storage/file-lock.js';
|
|
11
|
-
import { todoItemsFromSnapshot } from '../../tools/agent/todo.js';
|
|
12
|
-
import { applyCodeModeStateMutations, parseCodeModeStateMutations, restoreCodeModeState } from '../../tools/code-mode/state.js';
|
|
13
|
-
import { contextGcBaseFromRecords, contextGcCheckpointIntervalEligible, latestValidContextGcCheckpoint, normalizeContextGcReplacements, sameContextGcBase, validateContextGcCheckpoint } from '../context-gc-checkpoint.js';
|
|
14
|
-
import { bindToolResultResources, cleanupUnreferencedToolResources, completeToolResultResources, copyToolResultResources, readToolResultResource, toolResultResourceFiles, writeToolResultResource } from '../tool-result-resources.js';
|
|
15
|
-
import { beginSessionStorageTransaction, isUnjournaledTransactionFile, recoverSessionStorageTransaction } from '../transaction-recovery.js';
|
|
16
|
-
import { extensionCheckpointFromPayload, indexExtensionCheckpoint, restoreExtensionCheckpoint, updateSessionCurrentState } from './current-state.js';
|
|
17
|
-
import { decodeSessionListCursor, encodeSessionListCursor, mergeSessionDerivedCache, normalizeSessionSource, persistSessionIndex, readSessionIndex, sessionListFilter, sessionMatchesFilter, sessionTreeIds } from './directory-index.js';
|
|
18
|
-
import { deriveExecutionState, executionModelFromRecord, isCompactCheckpoint, isNativeUserTurn, sameResumeIndex, userMessagePreviewFromRecord } from './execution-state.js';
|
|
19
|
-
import { forkedPayload, renameIfExists, restoreRenamedFiles, retainedSessionTitle, stageSessionSegments, stageUnreferencedImages } from './history.js';
|
|
20
|
-
import { MAX_INPUT_IMAGES, copyGeneratedImages, generatedImageFileNames, inputImageReference, inputImageReferences, readInputImageBytes } from './images.js';
|
|
21
|
-
import { persistResource, setResourceUrl, readResource, copyResources, resourceFiles, completeResources, recoverResources } from './resources.js';
|
|
22
|
-
import { MAX_SEGMENT_BYTES, appendRecordBytes, readRecordPageFromTail, readRecords, readRecordsFromHead, readRecordsFromTail, repairSegmentTail, segmentNumberFromPath } from './records.js';
|
|
23
|
-
const MAX_CONTEXTUAL_WORLD_STATE_BYTES = 8 * 1024 * 1024;
|
|
24
|
-
const CONTEXTUAL_WORLD_STATE_FILE = 'context-world-state.json';
|
|
25
|
-
/** SDK-owned JSONL history with per-HOME in-process sharing and cross-process file locks. */
|
|
26
|
-
export class JsonlSessionStore {
|
|
27
|
-
static #instances = new Map();
|
|
28
|
-
static #opening = new Map();
|
|
29
|
-
#shared;
|
|
30
|
-
#maintenance = new WeakMap();
|
|
31
|
-
#leaseClosed = false;
|
|
32
|
-
constructor(shared) {
|
|
33
|
-
this.#shared = shared;
|
|
34
|
-
}
|
|
35
|
-
static async open(home) {
|
|
36
|
-
const root = join(home, '.mar', 'agent');
|
|
37
|
-
const existing = JsonlSessionStore.#instances.get(root);
|
|
38
|
-
if (existing) {
|
|
39
|
-
existing.references++;
|
|
40
|
-
return new JsonlSessionStore(existing);
|
|
41
|
-
}
|
|
42
|
-
const pending = JsonlSessionStore.#opening.get(root);
|
|
43
|
-
if (pending) {
|
|
44
|
-
const shared = await pending;
|
|
45
|
-
shared.references++;
|
|
46
|
-
return new JsonlSessionStore(shared);
|
|
47
|
-
}
|
|
48
|
-
const opening = JsonlSessionStore.#openExclusive(root);
|
|
49
|
-
JsonlSessionStore.#opening.set(root, opening);
|
|
50
|
-
try {
|
|
51
|
-
const shared = await opening;
|
|
52
|
-
JsonlSessionStore.#instances.set(root, shared);
|
|
53
|
-
return new JsonlSessionStore(shared);
|
|
54
|
-
}
|
|
55
|
-
finally {
|
|
56
|
-
JsonlSessionStore.#opening.delete(root);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
/** Create a missing index only without Session directories; recover journals and marked creations. */
|
|
60
|
-
static async #openExclusive(root) {
|
|
61
|
-
const directory = join(root, 'sessions');
|
|
62
|
-
const path = join(directory, 'index.json');
|
|
63
|
-
await mkdir(directory, { recursive: true });
|
|
64
|
-
const release = await acquireFileLock(join(directory, 'index.lock'));
|
|
65
|
-
let index;
|
|
66
|
-
try {
|
|
67
|
-
const disk = await readSessionIndex(path);
|
|
68
|
-
if (disk)
|
|
69
|
-
index = disk;
|
|
70
|
-
else {
|
|
71
|
-
const entries = await readdir(directory, { withFileTypes: true });
|
|
72
|
-
if (entries.some((entry) => entry.isDirectory()))
|
|
73
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session index is missing.');
|
|
74
|
-
index = { version: 1, revision: 0, sessions: [] };
|
|
75
|
-
await persistSessionIndex(path, index);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
finally {
|
|
79
|
-
await release();
|
|
80
|
-
}
|
|
81
|
-
for (const session of index.sessions) {
|
|
82
|
-
const directory = join(root, 'sessions', session.id);
|
|
83
|
-
let names;
|
|
84
|
-
try {
|
|
85
|
-
names = await readdir(directory);
|
|
86
|
-
}
|
|
87
|
-
catch (error) {
|
|
88
|
-
if (error.code === 'ENOENT')
|
|
89
|
-
continue;
|
|
90
|
-
throw error;
|
|
91
|
-
}
|
|
92
|
-
if (!names.includes('storage-transaction.json') &&
|
|
93
|
-
!names.some(isUnjournaledTransactionFile) &&
|
|
94
|
-
!names.some((name) => /^context-world-state\.json\.\d+\.[0-9a-f-]+\.(?:rollback|clear)-backup$/u.test(name)))
|
|
95
|
-
continue;
|
|
96
|
-
let releaseSession;
|
|
97
|
-
try {
|
|
98
|
-
releaseSession = await acquireFileLock(join(directory, 'lock'), 'Session already has a writer.', 0);
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
if (error instanceof MarAgentError && error.code === 'MAR_AGENT_SESSION_LOCKED')
|
|
102
|
-
continue;
|
|
103
|
-
throw error;
|
|
104
|
-
}
|
|
105
|
-
try {
|
|
106
|
-
// The index may have changed while waiting for the Session lock.
|
|
107
|
-
const latest = await readSessionIndex(path);
|
|
108
|
-
const current = latest?.sessions.find((item) => item.id === session.id);
|
|
109
|
-
if (!current)
|
|
110
|
-
continue;
|
|
111
|
-
await recoverSessionStorageTransaction(directory, current.storageTransactionId);
|
|
112
|
-
await recoverContextualWorldStateTransactions(root, current);
|
|
113
|
-
}
|
|
114
|
-
finally {
|
|
115
|
-
await releaseSession();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// A creation marker distinguishes our unpublished directories from unrelated files.
|
|
119
|
-
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
120
|
-
if (!entry.isDirectory() || !/^[0-9a-f-]{36}$/iu.test(entry.name))
|
|
121
|
-
continue;
|
|
122
|
-
const candidate = join(directory, entry.name);
|
|
123
|
-
const marker = join(candidate, 'publication-pending');
|
|
124
|
-
try {
|
|
125
|
-
if (!(await lstat(marker)).isFile())
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
if (error.code === 'ENOENT')
|
|
130
|
-
continue;
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
133
|
-
let releaseSession;
|
|
134
|
-
try {
|
|
135
|
-
releaseSession = await acquireFileLock(join(candidate, 'lock'), 'Session already has a writer.', 0);
|
|
136
|
-
}
|
|
137
|
-
catch (error) {
|
|
138
|
-
if (error instanceof MarAgentError && error.code === 'MAR_AGENT_SESSION_LOCKED')
|
|
139
|
-
continue;
|
|
140
|
-
throw error;
|
|
141
|
-
}
|
|
142
|
-
let unpublished = false;
|
|
143
|
-
try {
|
|
144
|
-
const latest = await readSessionIndex(path);
|
|
145
|
-
if (!latest)
|
|
146
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session index is missing.');
|
|
147
|
-
unpublished = !latest.sessions.some((session) => session.id === entry.name);
|
|
148
|
-
if (!unpublished)
|
|
149
|
-
await rm(marker, { force: true });
|
|
150
|
-
}
|
|
151
|
-
finally {
|
|
152
|
-
await releaseSession();
|
|
153
|
-
}
|
|
154
|
-
if (unpublished)
|
|
155
|
-
await rm(candidate, { recursive: true, force: true });
|
|
156
|
-
}
|
|
157
|
-
return {
|
|
158
|
-
root,
|
|
159
|
-
index,
|
|
160
|
-
indexOperations: Promise.resolve(),
|
|
161
|
-
pendingSessions: new Map(),
|
|
162
|
-
references: 1,
|
|
163
|
-
closed: false,
|
|
164
|
-
indexDirty: false,
|
|
165
|
-
dirtySessionVersions: new Map(),
|
|
166
|
-
derivedOnlyDirtySessions: new Set(),
|
|
167
|
-
pendingPublishIds: new Set(),
|
|
168
|
-
codeStateWrites: new Map(),
|
|
169
|
-
recordWrites: new Map(),
|
|
170
|
-
writerLeases: new Map(),
|
|
171
|
-
segments: new Map(),
|
|
172
|
-
executionModelReads: new Map(),
|
|
173
|
-
historyPagePositions: new Map()
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
/** Merge dirty metadata with disk membership; only createdIds publish locally new entries. */
|
|
177
|
-
async #saveIndex(deletedIds, createdIds = []) {
|
|
178
|
-
return this.#serializeIndexOperation(() => this.#commitIndex(deletedIds, createdIds));
|
|
179
|
-
}
|
|
180
|
-
async #serializeIndexOperation(operation) {
|
|
181
|
-
const next = this.#shared.indexOperations.then(operation, operation);
|
|
182
|
-
this.#shared.indexOperations = next.then(() => undefined, () => undefined);
|
|
183
|
-
return next;
|
|
184
|
-
}
|
|
185
|
-
async #commitIndex(deletedIds, createdIds = []) {
|
|
186
|
-
const release = await acquireFileLock(join(this.#shared.root, 'sessions', 'index.lock'));
|
|
187
|
-
const path = join(this.#shared.root, 'sessions', 'index.json');
|
|
188
|
-
try {
|
|
189
|
-
const disk = await readSessionIndex(path);
|
|
190
|
-
if (!disk)
|
|
191
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session index is missing.');
|
|
192
|
-
const dirty = new Map(this.#shared.dirtySessionVersions);
|
|
193
|
-
const derivedOnly = new Set(this.#shared.derivedOnlyDirtySessions);
|
|
194
|
-
const sessions = new Map(disk.sessions.map((session) => [session.id, session]));
|
|
195
|
-
const created = new Set(createdIds);
|
|
196
|
-
for (const session of this.#shared.index.sessions) {
|
|
197
|
-
const current = sessions.get(session.id);
|
|
198
|
-
// Disk membership is authoritative: a stale in-memory snapshot cannot revive a deletion.
|
|
199
|
-
if (created.has(session.id) || (current && dirty.has(session.id)))
|
|
200
|
-
sessions.set(session.id, current && derivedOnly.has(session.id)
|
|
201
|
-
? mergeSessionDerivedCache(current, session)
|
|
202
|
-
: structuredClone(session));
|
|
203
|
-
}
|
|
204
|
-
for (const deletedId of typeof deletedIds === 'string' ? [deletedIds] : (deletedIds ?? []))
|
|
205
|
-
sessions.delete(deletedId);
|
|
206
|
-
const updated = {
|
|
207
|
-
version: 1,
|
|
208
|
-
revision: createdIds.length > 0 ||
|
|
209
|
-
deletedIds !== undefined ||
|
|
210
|
-
[...dirty.keys()].some((id) => !derivedOnly.has(id))
|
|
211
|
-
? Math.max(disk.revision, this.#shared.index.revision) + 1
|
|
212
|
-
: disk.revision,
|
|
213
|
-
sessions: [...sessions.values()]
|
|
214
|
-
};
|
|
215
|
-
await persistSessionIndex(path, updated);
|
|
216
|
-
for (const [id, version] of dirty) {
|
|
217
|
-
// A different creation still needs its own index transaction to publish its directory.
|
|
218
|
-
if (this.#shared.pendingPublishIds.has(id) && !created.has(id))
|
|
219
|
-
continue;
|
|
220
|
-
if (this.#shared.dirtySessionVersions.get(id) === version) {
|
|
221
|
-
this.#shared.dirtySessionVersions.delete(id);
|
|
222
|
-
this.#shared.derivedOnlyDirtySessions.delete(id);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
for (const id of created)
|
|
226
|
-
this.#shared.pendingPublishIds.delete(id);
|
|
227
|
-
const pending = this.#shared.index.sessions.filter((session) => this.#shared.dirtySessionVersions.has(session.id) &&
|
|
228
|
-
(sessions.has(session.id) || this.#shared.pendingPublishIds.has(session.id)));
|
|
229
|
-
const replacements = new Map(pending.map((session) => [session.id, session]));
|
|
230
|
-
const published = new Set(updated.sessions.map((session) => session.id));
|
|
231
|
-
this.#shared.index = {
|
|
232
|
-
...updated,
|
|
233
|
-
sessions: [
|
|
234
|
-
...updated.sessions.map((session) => replacements.get(session.id) ?? session),
|
|
235
|
-
...pending.filter((session) => !published.has(session.id))
|
|
236
|
-
]
|
|
237
|
-
};
|
|
238
|
-
this.#shared.indexDirty = this.#shared.dirtySessionVersions.size > 0;
|
|
239
|
-
}
|
|
240
|
-
finally {
|
|
241
|
-
await release();
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
async #refreshIndex() {
|
|
245
|
-
return this.#serializeIndexOperation(() => this.#readCurrentIndex());
|
|
246
|
-
}
|
|
247
|
-
async #readCurrentIndex() {
|
|
248
|
-
const disk = await readSessionIndex(join(this.#shared.root, 'sessions', 'index.json'));
|
|
249
|
-
if (!disk)
|
|
250
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session index is missing.');
|
|
251
|
-
// A local unflushed revision may exceed another writer's committed revision.
|
|
252
|
-
// Always refresh membership from disk, then overlay only our pending changes.
|
|
253
|
-
const local = new Map(this.#shared.index.sessions.map((session) => [session.id, session]));
|
|
254
|
-
const published = new Set(disk.sessions.map((session) => session.id));
|
|
255
|
-
this.#shared.index = {
|
|
256
|
-
...disk,
|
|
257
|
-
revision: this.#shared.indexDirty || this.#shared.pendingPublishIds.size > 0
|
|
258
|
-
? Math.max(disk.revision, this.#shared.index.revision)
|
|
259
|
-
: disk.revision,
|
|
260
|
-
sessions: disk.sessions
|
|
261
|
-
.map((session) => {
|
|
262
|
-
const current = local.get(session.id);
|
|
263
|
-
if (!current || !this.#shared.dirtySessionVersions.has(session.id))
|
|
264
|
-
return session;
|
|
265
|
-
return this.#shared.derivedOnlyDirtySessions.has(session.id)
|
|
266
|
-
? mergeSessionDerivedCache(session, current)
|
|
267
|
-
: current;
|
|
268
|
-
})
|
|
269
|
-
.concat(this.#shared.index.sessions.filter((session) => this.#shared.pendingPublishIds.has(session.id) && !published.has(session.id)))
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
/** Hold the new Session lock and mark its directory until JSONL and index publication finish. */
|
|
273
|
-
async #beginUnpublishedSession(sessionId) {
|
|
274
|
-
const directory = join(this.#shared.root, 'sessions', sessionId);
|
|
275
|
-
await mkdir(directory);
|
|
276
|
-
const release = await acquireFileLock(join(directory, 'lock'), 'Session already has a writer.', 0);
|
|
277
|
-
try {
|
|
278
|
-
await writeFile(join(directory, 'publication-pending'), '1', {
|
|
279
|
-
mode: 0o600,
|
|
280
|
-
flag: 'wx',
|
|
281
|
-
flush: true
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
catch (error) {
|
|
285
|
-
await release();
|
|
286
|
-
await rm(directory, { recursive: true, force: true });
|
|
287
|
-
throw error;
|
|
288
|
-
}
|
|
289
|
-
return { directory, release };
|
|
290
|
-
}
|
|
291
|
-
async createSession(input) {
|
|
292
|
-
this.#assertOpen();
|
|
293
|
-
const source = normalizeSessionSource(input.source);
|
|
294
|
-
if (source.type !== 'subagent')
|
|
295
|
-
return this.#createSession(input, source);
|
|
296
|
-
// Publish under the parent's existing serial domain so tree admission cannot miss a child.
|
|
297
|
-
return this.#serializeRecordOperation(source.parentSessionId, async () => {
|
|
298
|
-
this.#assertOpen();
|
|
299
|
-
if (this.#shared.writerLeases.get(source.parentSessionId)?.maintenance)
|
|
300
|
-
throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session is under maintenance.');
|
|
301
|
-
await this.getSession(source.parentSessionId);
|
|
302
|
-
return this.#createSession(input, source);
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
/** Append the durable creation record before making this Session visible in the index. */
|
|
306
|
-
async #createSession(input, source) {
|
|
307
|
-
const id = randomUUID();
|
|
308
|
-
const now = new Date().toISOString();
|
|
309
|
-
const meta = {
|
|
310
|
-
id,
|
|
311
|
-
title: input.title ?? 'New session',
|
|
312
|
-
workspace: input.workspace,
|
|
313
|
-
createdAt: now,
|
|
314
|
-
lastActivityAt: now,
|
|
315
|
-
source,
|
|
316
|
-
nextSequence: 1,
|
|
317
|
-
automaticTitlePending: input.title === undefined,
|
|
318
|
-
resume: {
|
|
319
|
-
version: 1,
|
|
320
|
-
userTurns: 0,
|
|
321
|
-
todoRevision: 0,
|
|
322
|
-
executionModelIndexed: true,
|
|
323
|
-
firstUserMessageIndexed: true
|
|
324
|
-
},
|
|
325
|
-
segments: []
|
|
326
|
-
};
|
|
327
|
-
const publication = await this.#beginUnpublishedSession(id);
|
|
328
|
-
this.#shared.segments.set(id, 1);
|
|
329
|
-
this.#shared.pendingSessions.set(id, meta);
|
|
330
|
-
let discard = false;
|
|
331
|
-
try {
|
|
332
|
-
try {
|
|
333
|
-
await this.append(id, {
|
|
334
|
-
type: 'session.created',
|
|
335
|
-
payload: {
|
|
336
|
-
title: meta.title,
|
|
337
|
-
workspace: meta.workspace,
|
|
338
|
-
automaticTitle: input.title === undefined,
|
|
339
|
-
source: meta.source
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
catch (error) {
|
|
344
|
-
this.#shared.pendingSessions.delete(id);
|
|
345
|
-
this.#shared.segments.delete(id);
|
|
346
|
-
discard = true;
|
|
347
|
-
throw error;
|
|
348
|
-
}
|
|
349
|
-
this.#shared.pendingSessions.delete(id);
|
|
350
|
-
this.#shared.index.sessions.unshift(meta);
|
|
351
|
-
this.#shared.pendingPublishIds.add(id);
|
|
352
|
-
this.#markIndexDirty(id);
|
|
353
|
-
try {
|
|
354
|
-
await this.#saveIndex(undefined, [id]);
|
|
355
|
-
}
|
|
356
|
-
catch (error) {
|
|
357
|
-
// A failed lock release can follow a committed index rename; do not unpublish that Session.
|
|
358
|
-
// If the index cannot be read, leave the disk marker for recovery and keep the original error.
|
|
359
|
-
const disk = await readSessionIndex(join(this.#shared.root, 'sessions', 'index.json')).catch(() => undefined);
|
|
360
|
-
if (disk?.sessions.some((session) => session.id === id)) {
|
|
361
|
-
this.#shared.pendingPublishIds.delete(id);
|
|
362
|
-
this.#shared.dirtySessionVersions.delete(id);
|
|
363
|
-
this.#shared.indexDirty = this.#shared.dirtySessionVersions.size > 0;
|
|
364
|
-
}
|
|
365
|
-
else {
|
|
366
|
-
// Keep the marker until disk membership can be checked during recovery.
|
|
367
|
-
this.#shared.index.sessions = this.#shared.index.sessions.filter((item) => item.id !== id);
|
|
368
|
-
this.#shared.segments.delete(id);
|
|
369
|
-
this.#shared.pendingPublishIds.delete(id);
|
|
370
|
-
this.#shared.dirtySessionVersions.delete(id);
|
|
371
|
-
this.#shared.indexDirty = this.#shared.dirtySessionVersions.size > 0;
|
|
372
|
-
throw error;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
await rm(join(publication.directory, 'publication-pending'), { force: true });
|
|
376
|
-
return meta;
|
|
377
|
-
}
|
|
378
|
-
finally {
|
|
379
|
-
try {
|
|
380
|
-
await publication.release();
|
|
381
|
-
}
|
|
382
|
-
finally {
|
|
383
|
-
if (discard)
|
|
384
|
-
await rm(publication.directory, { recursive: true, force: true });
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
#sessionDirectory(sessionId) {
|
|
389
|
-
this.#assertOpen();
|
|
390
|
-
if (!this.#shared.index.sessions.some((session) => session.id === sessionId) &&
|
|
391
|
-
!this.#shared.pendingSessions.has(sessionId))
|
|
392
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
393
|
-
return join(this.#shared.root, 'sessions', sessionId);
|
|
394
|
-
}
|
|
395
|
-
async readContextualWorldState(sessionId) {
|
|
396
|
-
const path = join(this.#sessionDirectory(sessionId), CONTEXTUAL_WORLD_STATE_FILE);
|
|
397
|
-
try {
|
|
398
|
-
const envelope = JSON.parse(await readFile(path, 'utf8'));
|
|
399
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
400
|
-
if (envelope.version !== 1 ||
|
|
401
|
-
!Number.isSafeInteger(envelope.historySequence) ||
|
|
402
|
-
envelope.historySequence < 1 ||
|
|
403
|
-
meta === undefined ||
|
|
404
|
-
envelope.historySequence >= meta.nextSequence)
|
|
405
|
-
return undefined;
|
|
406
|
-
return envelope.state;
|
|
407
|
-
}
|
|
408
|
-
catch {
|
|
409
|
-
return undefined;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
async writeContextualWorldState(sessionId, state) {
|
|
413
|
-
const directory = this.#sessionDirectory(sessionId);
|
|
414
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
415
|
-
if (!meta)
|
|
416
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
417
|
-
const path = join(directory, CONTEXTUAL_WORLD_STATE_FILE);
|
|
418
|
-
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
419
|
-
const serialized = JSON.stringify({
|
|
420
|
-
version: 1,
|
|
421
|
-
historySequence: meta.nextSequence - 1,
|
|
422
|
-
state
|
|
423
|
-
});
|
|
424
|
-
if (Buffer.byteLength(serialized) > MAX_CONTEXTUAL_WORLD_STATE_BYTES)
|
|
425
|
-
throw new MarAgentError('MAR_AGENT_HOST_LIMIT_EXCEEDED', 'Contextual world state exceeds the storage limit.');
|
|
426
|
-
try {
|
|
427
|
-
await writeFile(temporary, serialized, { mode: 0o600, flag: 'wx' });
|
|
428
|
-
await rename(temporary, path);
|
|
429
|
-
}
|
|
430
|
-
finally {
|
|
431
|
-
await rm(temporary, { force: true });
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
/** Mark each image pending before publication; turn.user or lease recovery resolves ownership. */
|
|
435
|
-
async persistInputImages(sessionId, images) {
|
|
436
|
-
this.#assertOpen();
|
|
437
|
-
if (images.length === 0)
|
|
438
|
-
return [];
|
|
439
|
-
if (images.length > MAX_INPUT_IMAGES)
|
|
440
|
-
throw new MarAgentError('MAR_AGENT_HOST_LIMIT_EXCEEDED', 'Too many image inputs were provided.');
|
|
441
|
-
const resources = [];
|
|
442
|
-
let total = 0;
|
|
443
|
-
for (const image of images) {
|
|
444
|
-
if ((image.url !== undefined && !isModelImageUrl(image.url)) ||
|
|
445
|
-
(image.urlExpiresAt !== undefined &&
|
|
446
|
-
(!Number.isSafeInteger(image.urlExpiresAt) ||
|
|
447
|
-
image.urlExpiresAt <= 0 ||
|
|
448
|
-
image.url === undefined)))
|
|
449
|
-
throw new MarAgentError('MAR_AGENT_SESSION_INPUT_INVALID', 'Image model URL is invalid.');
|
|
450
|
-
let resource = await this.persistResource(sessionId, image);
|
|
451
|
-
if (image.url !== undefined && image.urlExpiresAt !== undefined)
|
|
452
|
-
resource = await this.setResourceUrl(sessionId, resource.resourceId, {
|
|
453
|
-
url: image.url,
|
|
454
|
-
urlExpiresAt: image.urlExpiresAt
|
|
455
|
-
});
|
|
456
|
-
total += resource.byteSize;
|
|
457
|
-
if (resource.byteSize > 5 * 1024 * 1024 || total > 10 * 1024 * 1024)
|
|
458
|
-
throw new MarAgentError('MAR_AGENT_HOST_LIMIT_EXCEEDED', 'Image input exceeds the supported size limit.');
|
|
459
|
-
resources.push({
|
|
460
|
-
...resource,
|
|
461
|
-
resourceInputVersion: 1,
|
|
462
|
-
name: image.name,
|
|
463
|
-
fileName: `${resource.resourceId}.bin`
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
return resources;
|
|
467
|
-
}
|
|
468
|
-
async persistResource(sessionId, image) {
|
|
469
|
-
this.#assertOpen();
|
|
470
|
-
return this.#serializeRecordOperation(sessionId, () => persistResource(join(this.#sessionDirectory(sessionId), 'resources'), image));
|
|
471
|
-
}
|
|
472
|
-
async setResourceUrl(sessionId, resourceId, published) {
|
|
473
|
-
this.#assertOpen();
|
|
474
|
-
return this.#serializeRecordOperation(sessionId, () => setResourceUrl(join(this.#sessionDirectory(sessionId), 'resources'), resourceId, published));
|
|
475
|
-
}
|
|
476
|
-
async readResource(sessionId, resourceId) {
|
|
477
|
-
this.#assertOpen();
|
|
478
|
-
const directory = join(this.#sessionDirectory(sessionId), 'resources');
|
|
479
|
-
if (!/^[a-f0-9]{64}$/u.test(resourceId))
|
|
480
|
-
return undefined;
|
|
481
|
-
return this.#serializeRecordOperation(sessionId, async () => {
|
|
482
|
-
let pending = false;
|
|
483
|
-
try {
|
|
484
|
-
await lstat(join(directory, `.${resourceId}.pending`));
|
|
485
|
-
pending = true;
|
|
486
|
-
}
|
|
487
|
-
catch (cause) {
|
|
488
|
-
if (cause.code !== 'ENOENT')
|
|
489
|
-
throw new MarAgentError('MAR_AGENT_IMAGE_UNAVAILABLE', 'Session image resource is unavailable.', {
|
|
490
|
-
cause
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
if (pending &&
|
|
494
|
-
(this.#shared.writerLeases.get(sessionId)?.appendFailure !== undefined ||
|
|
495
|
-
!resourceFiles(await this.#readRecords(sessionId)).has(`${resourceId}.bin`)))
|
|
496
|
-
return undefined;
|
|
497
|
-
return readResource(directory, resourceId);
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
async writeToolResultResource(sessionId, text) {
|
|
501
|
-
this.#assertOpen();
|
|
502
|
-
if (!this.#shared.index.sessions.some((item) => item.id === sessionId))
|
|
503
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
504
|
-
return writeToolResultResource(this.#sessionDirectory(sessionId), text);
|
|
505
|
-
}
|
|
506
|
-
async readToolResultResource(sessionId, input) {
|
|
507
|
-
this.#assertOpen();
|
|
508
|
-
await this.#refreshIndex();
|
|
509
|
-
if (!this.#shared.index.sessions.some((item) => item.id === sessionId))
|
|
510
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
511
|
-
return readToolResultResource(this.#sessionDirectory(sessionId), sessionId, input, async (sequence) => {
|
|
512
|
-
await this.#refreshIndex();
|
|
513
|
-
return (await this.readPage(sessionId, { beforeSequence: sequence + 1, limit: 1 })).records.find((record) => record.sequence === sequence);
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
async readInputImages(sessionId, value) {
|
|
517
|
-
this.#assertOpen();
|
|
518
|
-
const references = inputImageReferences(Array.isArray(value) ? value.filter((item) => item?.resourceInputVersion === 1) : undefined);
|
|
519
|
-
if (references.length === 0)
|
|
520
|
-
return [];
|
|
521
|
-
const images = [];
|
|
522
|
-
for (const reference of references) {
|
|
523
|
-
if (reference.resourceId === undefined)
|
|
524
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session resource identity is missing.');
|
|
525
|
-
images.push({
|
|
526
|
-
resourceInputVersion: 1,
|
|
527
|
-
mimeType: reference.mimeType,
|
|
528
|
-
byteSize: reference.byteSize,
|
|
529
|
-
resourceId: reference.resourceId,
|
|
530
|
-
...(reference.url === undefined ? {} : { url: reference.url }),
|
|
531
|
-
...(reference.urlExpiresAt === undefined ? {} : { urlExpiresAt: reference.urlExpiresAt })
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
return images;
|
|
535
|
-
}
|
|
536
|
-
async readInputImage(sessionId, input) {
|
|
537
|
-
this.#assertOpen();
|
|
538
|
-
if (!Number.isSafeInteger(input.sequence) ||
|
|
539
|
-
input.sequence < 1 ||
|
|
540
|
-
input.sequence >= Number.MAX_SAFE_INTEGER ||
|
|
541
|
-
!Number.isSafeInteger(input.imageIndex) ||
|
|
542
|
-
input.imageIndex < 0)
|
|
543
|
-
return undefined;
|
|
544
|
-
const page = await this.readPage(sessionId, {
|
|
545
|
-
limit: 1,
|
|
546
|
-
beforeSequence: input.sequence + 1
|
|
547
|
-
});
|
|
548
|
-
const record = page.records[0];
|
|
549
|
-
if (record?.sequence !== input.sequence ||
|
|
550
|
-
record.type !== 'turn.user' ||
|
|
551
|
-
!isObject(record.payload))
|
|
552
|
-
return undefined;
|
|
553
|
-
if (!Array.isArray(record.payload.images) || input.imageIndex >= record.payload.images.length)
|
|
554
|
-
return undefined;
|
|
555
|
-
if (record.payload.images[input.imageIndex]?.resourceInputVersion !== 1)
|
|
556
|
-
return undefined;
|
|
557
|
-
const reference = inputImageReference(record.payload.images[input.imageIndex]);
|
|
558
|
-
if (reference === undefined)
|
|
559
|
-
return undefined;
|
|
560
|
-
if (reference.resourceId === undefined)
|
|
561
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session resource identity is missing.');
|
|
562
|
-
const data = await readInputImageBytes(join(this.#sessionDirectory(sessionId), 'resources'), reference);
|
|
563
|
-
return {
|
|
564
|
-
name: reference.name,
|
|
565
|
-
mimeType: reference.mimeType,
|
|
566
|
-
byteSize: reference.byteSize,
|
|
567
|
-
fileName: reference.fileName,
|
|
568
|
-
sha256: reference.sha256,
|
|
569
|
-
dataBase64: data.toString('base64')
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
async forkSession(sourceSessionId, input) {
|
|
573
|
-
this.#assertOpen();
|
|
574
|
-
const release = await this.acquireSessionLease(sourceSessionId);
|
|
575
|
-
try {
|
|
576
|
-
const source = this.#shared.index.sessions.find((item) => item.id === sourceSessionId);
|
|
577
|
-
if (!source)
|
|
578
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
579
|
-
const records = await this.read(sourceSessionId);
|
|
580
|
-
const boundaryIndex = input.boundaryTurnId === null
|
|
581
|
-
? -1
|
|
582
|
-
: records.findLastIndex((record) => record.turnId === input.boundaryTurnId);
|
|
583
|
-
if (input.boundaryTurnId !== null && boundaryIndex < 0)
|
|
584
|
-
throw new MarAgentError('MAR_AGENT_REWIND_TARGET_INVALID', 'Session rewind target was not found.');
|
|
585
|
-
const createdAt = new Date().toISOString();
|
|
586
|
-
const target = {
|
|
587
|
-
id: randomUUID(),
|
|
588
|
-
title: input.title,
|
|
589
|
-
workspace: source.workspace,
|
|
590
|
-
createdAt,
|
|
591
|
-
lastActivityAt: createdAt,
|
|
592
|
-
source: { type: 'user' },
|
|
593
|
-
nextSequence: 1,
|
|
594
|
-
automaticTitlePending: false,
|
|
595
|
-
resume: {
|
|
596
|
-
version: 1,
|
|
597
|
-
userTurns: 0,
|
|
598
|
-
todoRevision: 0,
|
|
599
|
-
executionModelIndexed: true,
|
|
600
|
-
firstUserMessageIndexed: true
|
|
601
|
-
},
|
|
602
|
-
segments: []
|
|
603
|
-
};
|
|
604
|
-
const publication = await this.#beginUnpublishedSession(target.id);
|
|
605
|
-
this.#shared.segments.set(target.id, 1);
|
|
606
|
-
this.#shared.pendingSessions.set(target.id, target);
|
|
607
|
-
let discard = false;
|
|
608
|
-
try {
|
|
609
|
-
const copied = [
|
|
610
|
-
{
|
|
611
|
-
schemaVersion: 1,
|
|
612
|
-
recordId: randomUUID(),
|
|
613
|
-
sequence: 1,
|
|
614
|
-
timestamp: createdAt,
|
|
615
|
-
sessionId: target.id,
|
|
616
|
-
type: 'session.created',
|
|
617
|
-
payload: {
|
|
618
|
-
title: target.title,
|
|
619
|
-
workspace: target.workspace,
|
|
620
|
-
automaticTitle: false,
|
|
621
|
-
source: target.source
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
];
|
|
625
|
-
const retained = input.boundaryTurnId === null ? [] : records.slice(1, boundaryIndex + 1);
|
|
626
|
-
const inlineState = inlineMediaHistory(retained).state;
|
|
627
|
-
const stateAnchor = inlineState &&
|
|
628
|
-
retained.findLast((record) => record.type === 'resource.window' || record.type === 'compact.completed');
|
|
629
|
-
const forkIdentities = new Map();
|
|
630
|
-
for (const record of retained) {
|
|
631
|
-
if (record.type === 'context.gc.completed' || record.type === 'responses.continuation')
|
|
632
|
-
continue;
|
|
633
|
-
const identity = { recordId: randomUUID(), sequence: copied.length + 1 };
|
|
634
|
-
forkIdentities.set(record.recordId, identity);
|
|
635
|
-
copied.push({
|
|
636
|
-
schemaVersion: 1,
|
|
637
|
-
...identity,
|
|
638
|
-
sessionId: target.id,
|
|
639
|
-
type: record.type,
|
|
640
|
-
payload: forkedPayload(record.type, record === stateAnchor
|
|
641
|
-
? record.type === 'resource.window'
|
|
642
|
-
? inlineState
|
|
643
|
-
: {
|
|
644
|
-
...record.payload,
|
|
645
|
-
inlineMediaState: inlineState
|
|
646
|
-
}
|
|
647
|
-
: record.payload, sourceSessionId, target.id, input.title, forkIdentities),
|
|
648
|
-
...(record.executionId === undefined ? {} : { executionId: record.executionId }),
|
|
649
|
-
...(record.turnId === undefined ? {} : { turnId: record.turnId }),
|
|
650
|
-
timestamp: record.timestamp
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
// A fork is unpublished until every segment and referenced image is durable.
|
|
654
|
-
const staged = await stageSessionSegments(this.#sessionDirectory(target.id), copied, 'fork');
|
|
655
|
-
for (const file of staged.files)
|
|
656
|
-
await rename(file.temporaryPath, file.path);
|
|
657
|
-
target.segments = staged.segments;
|
|
658
|
-
target.nextSequence = copied.length + 1;
|
|
659
|
-
this.#shared.segments.set(target.id, staged.segments.length);
|
|
660
|
-
for (const record of copied)
|
|
661
|
-
this.#applyRecordToResumeIndex(target, record);
|
|
662
|
-
await copyToolResultResources(this.#sessionDirectory(sourceSessionId), this.#sessionDirectory(target.id), copied);
|
|
663
|
-
await copyGeneratedImages(this.#sessionDirectory(sourceSessionId), this.#sessionDirectory(target.id), generatedImageFileNames(retained));
|
|
664
|
-
await copyResources(this.#sessionDirectory(sourceSessionId), this.#sessionDirectory(target.id), retained);
|
|
665
|
-
target.lastActivityAt = target.createdAt;
|
|
666
|
-
this.#shared.pendingSessions.delete(target.id);
|
|
667
|
-
this.#shared.index = {
|
|
668
|
-
version: 1,
|
|
669
|
-
revision: this.#shared.index.revision + 1,
|
|
670
|
-
sessions: [target, ...this.#shared.index.sessions]
|
|
671
|
-
};
|
|
672
|
-
this.#shared.pendingPublishIds.add(target.id);
|
|
673
|
-
this.#markIndexDirty(target.id);
|
|
674
|
-
await this.#saveIndex(undefined, [target.id]);
|
|
675
|
-
await rm(join(publication.directory, 'publication-pending'), { force: true });
|
|
676
|
-
return {
|
|
677
|
-
id: target.id,
|
|
678
|
-
title: target.title,
|
|
679
|
-
workspace: target.workspace,
|
|
680
|
-
createdAt: target.createdAt,
|
|
681
|
-
lastActivityAt: target.createdAt,
|
|
682
|
-
source: target.source
|
|
683
|
-
};
|
|
684
|
-
}
|
|
685
|
-
catch (error) {
|
|
686
|
-
this.#shared.pendingSessions.delete(target.id);
|
|
687
|
-
const disk = await readSessionIndex(join(this.#shared.root, 'sessions', 'index.json'));
|
|
688
|
-
if (disk?.sessions.some((session) => session.id === target.id))
|
|
689
|
-
return {
|
|
690
|
-
id: target.id,
|
|
691
|
-
title: target.title,
|
|
692
|
-
workspace: target.workspace,
|
|
693
|
-
createdAt: target.createdAt,
|
|
694
|
-
lastActivityAt: target.createdAt,
|
|
695
|
-
source: target.source
|
|
696
|
-
};
|
|
697
|
-
const publishedIndex = this.#shared.index.sessions.findIndex((item) => item.id === target.id);
|
|
698
|
-
if (publishedIndex >= 0) {
|
|
699
|
-
this.#shared.index.sessions.splice(publishedIndex, 1);
|
|
700
|
-
this.#shared.index.revision++;
|
|
701
|
-
this.#shared.pendingPublishIds.delete(target.id);
|
|
702
|
-
this.#shared.dirtySessionVersions.delete(target.id);
|
|
703
|
-
this.#shared.indexDirty = this.#shared.dirtySessionVersions.size > 0;
|
|
704
|
-
}
|
|
705
|
-
this.#shared.segments.delete(target.id);
|
|
706
|
-
discard = true;
|
|
707
|
-
throw error;
|
|
708
|
-
}
|
|
709
|
-
finally {
|
|
710
|
-
try {
|
|
711
|
-
await publication.release();
|
|
712
|
-
}
|
|
713
|
-
finally {
|
|
714
|
-
if (discard)
|
|
715
|
-
await rm(publication.directory, { recursive: true, force: true });
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
finally {
|
|
720
|
-
await release();
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
/** Replace history under a journal; the indexed transaction ID decides the commit side. */
|
|
724
|
-
async rollbackSession(sessionId, input) {
|
|
725
|
-
this.#assertOpen();
|
|
726
|
-
const release = await this.acquireSessionLease(sessionId);
|
|
727
|
-
try {
|
|
728
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
729
|
-
if (!meta)
|
|
730
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
731
|
-
const records = await this.read(sessionId);
|
|
732
|
-
if (records[0]?.type !== 'session.created')
|
|
733
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session history is corrupt.');
|
|
734
|
-
const boundaryIndex = rollbackBoundaryIndex(records, input);
|
|
735
|
-
const retained = records
|
|
736
|
-
.slice(0, boundaryIndex + 1)
|
|
737
|
-
.filter((record) => record.type !== 'responses.continuation');
|
|
738
|
-
return await this.#replaceHistory(meta, retained, { kind: 'rollback' });
|
|
739
|
-
}
|
|
740
|
-
finally {
|
|
741
|
-
await release();
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
async updateTitle(sessionId, title, input = {}, owner) {
|
|
745
|
-
this.#assertOpen();
|
|
746
|
-
const normalized = title.trim();
|
|
747
|
-
if (!normalized)
|
|
748
|
-
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Session title is required.');
|
|
749
|
-
const release = await this.acquireSessionLease(sessionId, owner);
|
|
750
|
-
try {
|
|
751
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
752
|
-
if (input.expectedTitle !== undefined && meta.title !== input.expectedTitle)
|
|
753
|
-
return false;
|
|
754
|
-
if (input.automatic || input.executionId !== undefined) {
|
|
755
|
-
const records = await this.read(sessionId);
|
|
756
|
-
const latestTitle = records.findLast((record) => record.type === 'session.title.updated');
|
|
757
|
-
if (input.automatic &&
|
|
758
|
-
(records[0]?.payload?.automaticTitle !== true ||
|
|
759
|
-
latestTitle?.payload?.automatic === false))
|
|
760
|
-
return false;
|
|
761
|
-
if (input.executionId !== undefined &&
|
|
762
|
-
records.findLast((record) => record.type === 'turn.user')?.executionId !==
|
|
763
|
-
input.executionId)
|
|
764
|
-
return false;
|
|
765
|
-
}
|
|
766
|
-
await this.append(sessionId, {
|
|
767
|
-
type: 'session.title.updated',
|
|
768
|
-
payload: { title: normalized, automatic: input.automatic === true },
|
|
769
|
-
...(input.executionId ? { executionId: input.executionId } : {}),
|
|
770
|
-
...(input.turnId ? { turnId: input.turnId } : {})
|
|
771
|
-
});
|
|
772
|
-
return true;
|
|
773
|
-
}
|
|
774
|
-
finally {
|
|
775
|
-
await release();
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
/** Reset history with the same recovery journal while preserving the Session identity. */
|
|
779
|
-
async clearSession(sessionId, maintenance) {
|
|
780
|
-
this.#assertOpen();
|
|
781
|
-
const holding = maintenance ?? (await this.acquireSessionMaintenance(sessionId, 'clear'));
|
|
782
|
-
this.#assertMaintenance(sessionId, 'clear', holding);
|
|
783
|
-
let failed = false;
|
|
784
|
-
try {
|
|
785
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
786
|
-
if (!meta)
|
|
787
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
788
|
-
const records = await this.read(sessionId);
|
|
789
|
-
if (records[0]?.type !== 'session.created')
|
|
790
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session history is corrupt.');
|
|
791
|
-
const clearedAt = new Date().toISOString();
|
|
792
|
-
const retained = [
|
|
793
|
-
{
|
|
794
|
-
schemaVersion: 1,
|
|
795
|
-
recordId: randomUUID(),
|
|
796
|
-
sequence: 1,
|
|
797
|
-
timestamp: meta.createdAt,
|
|
798
|
-
sessionId,
|
|
799
|
-
type: 'session.created',
|
|
800
|
-
payload: {
|
|
801
|
-
title: meta.title,
|
|
802
|
-
workspace: meta.workspace,
|
|
803
|
-
automaticTitle: meta.automaticTitlePending === true,
|
|
804
|
-
source: normalizeSessionSource(meta.source)
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
];
|
|
808
|
-
return await this.#replaceHistory(meta, retained, { kind: 'clear', clearedAt });
|
|
809
|
-
}
|
|
810
|
-
catch (error) {
|
|
811
|
-
failed = true;
|
|
812
|
-
throw error;
|
|
813
|
-
}
|
|
814
|
-
finally {
|
|
815
|
-
if (!maintenance) {
|
|
816
|
-
const releasing = holding.release();
|
|
817
|
-
if (failed)
|
|
818
|
-
await releasing.catch(() => undefined);
|
|
819
|
-
else
|
|
820
|
-
await releasing;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
/** Called only while rollback or clear holds the maintenance lease. */
|
|
825
|
-
async #replaceHistory(meta, retained, operation) {
|
|
826
|
-
const sessionId = meta.id;
|
|
827
|
-
const staged = await stageSessionSegments(this.#sessionDirectory(sessionId), retained);
|
|
828
|
-
const transactionId = randomUUID();
|
|
829
|
-
const targetNextSequence = operation.kind === 'clear' ? 2 : (retained.at(-1)?.sequence ?? 0) + 1;
|
|
830
|
-
const contextualWorldStatePath = join(this.#sessionDirectory(sessionId), CONTEXTUAL_WORLD_STATE_FILE);
|
|
831
|
-
const contextualWorldStateBackup = `${contextualWorldStatePath}.${targetNextSequence}.${transactionId}.${operation.kind}-backup`;
|
|
832
|
-
let contextualWorldStateStaged = false;
|
|
833
|
-
let generatedTrash = [];
|
|
834
|
-
let resourceTrash = [];
|
|
835
|
-
const originalMeta = structuredClone(meta);
|
|
836
|
-
const originalIndexRevision = this.#shared.index.revision;
|
|
837
|
-
const originalSegment = this.#shared.segments.get(sessionId);
|
|
838
|
-
const originalPaths = await this.#segmentPaths(sessionId);
|
|
839
|
-
const backups = originalPaths.map((path) => ({
|
|
840
|
-
path,
|
|
841
|
-
backup: `${path}.${transactionId}.${operation.kind}-backup`
|
|
842
|
-
}));
|
|
843
|
-
const transaction = {
|
|
844
|
-
version: 1,
|
|
845
|
-
id: transactionId,
|
|
846
|
-
kind: operation.kind,
|
|
847
|
-
originalSegments: originalPaths.map((path) => basename(path)),
|
|
848
|
-
stagedSegments: staged.files.map((item) => ({
|
|
849
|
-
path: basename(item.path),
|
|
850
|
-
temporary: basename(item.temporaryPath)
|
|
851
|
-
})),
|
|
852
|
-
sidecarBackup: basename(contextualWorldStateBackup)
|
|
853
|
-
};
|
|
854
|
-
const installedPaths = [];
|
|
855
|
-
let committed = false;
|
|
856
|
-
try {
|
|
857
|
-
await beginSessionStorageTransaction(this.#sessionDirectory(sessionId), transaction);
|
|
858
|
-
contextualWorldStateStaged = await renameIfExists(contextualWorldStatePath, contextualWorldStateBackup);
|
|
859
|
-
generatedTrash = await stageUnreferencedImages(this.#sessionDirectory(sessionId), 'generated_images', operation.kind === 'rollback' ? new Set(generatedImageFileNames(retained)) : new Set(), transactionId);
|
|
860
|
-
resourceTrash.push(...(await stageUnreferencedImages(this.#sessionDirectory(sessionId), 'resources', operation.kind === 'rollback' ? resourceFiles(retained) : new Set(), transactionId)), ...(await stageUnreferencedImages(this.#sessionDirectory(sessionId), 'tool_results', operation.kind === 'rollback' ? toolResultResourceFiles(retained) : new Set(), transactionId)));
|
|
861
|
-
for (const item of backups)
|
|
862
|
-
await rename(item.path, item.backup);
|
|
863
|
-
for (const item of staged.files) {
|
|
864
|
-
await rename(item.temporaryPath, item.path);
|
|
865
|
-
installedPaths.push(item.path);
|
|
866
|
-
}
|
|
867
|
-
if (operation.kind === 'rollback') {
|
|
868
|
-
const state = deriveExecutionState(retained);
|
|
869
|
-
const completedAt = new Date().toISOString();
|
|
870
|
-
meta.title = retainedSessionTitle(retained, meta.title);
|
|
871
|
-
meta.lastActivityAt = completedAt;
|
|
872
|
-
meta.nextSequence = targetNextSequence;
|
|
873
|
-
meta.storageTransactionId = transactionId;
|
|
874
|
-
meta.automaticTitlePending = state.automaticTitlePending;
|
|
875
|
-
meta.resume = state.resume;
|
|
876
|
-
}
|
|
877
|
-
else {
|
|
878
|
-
meta.lastActivityAt = operation.clearedAt;
|
|
879
|
-
meta.nextSequence = targetNextSequence;
|
|
880
|
-
meta.storageTransactionId = transactionId;
|
|
881
|
-
meta.resume = { version: 1, userTurns: 0, todoRevision: 0, firstUserMessageIndexed: true };
|
|
882
|
-
}
|
|
883
|
-
meta.segments = staged.segments;
|
|
884
|
-
this.#shared.segments.set(sessionId, operation.kind === 'clear' ? 1 : (staged.segments.at(-1)?.number ?? 1));
|
|
885
|
-
this.#shared.index.revision++;
|
|
886
|
-
this.#markIndexDirty(sessionId);
|
|
887
|
-
await this.#saveIndex();
|
|
888
|
-
committed = true;
|
|
889
|
-
await recoverSessionStorageTransaction(this.#sessionDirectory(sessionId), transactionId);
|
|
890
|
-
return {
|
|
891
|
-
id: meta.id,
|
|
892
|
-
title: meta.title,
|
|
893
|
-
workspace: meta.workspace,
|
|
894
|
-
createdAt: meta.createdAt,
|
|
895
|
-
lastActivityAt: meta.lastActivityAt,
|
|
896
|
-
source: normalizeSessionSource(meta.source)
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
catch (error) {
|
|
900
|
-
if (!committed) {
|
|
901
|
-
committed = await this.#storageTransactionCommitted(sessionId, transactionId);
|
|
902
|
-
if (committed) {
|
|
903
|
-
this.#shared.dirtySessionVersions.delete(sessionId);
|
|
904
|
-
this.#shared.indexDirty = this.#shared.dirtySessionVersions.size > 0;
|
|
905
|
-
await recoverSessionStorageTransaction(this.#sessionDirectory(sessionId), transactionId);
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
if (!committed) {
|
|
909
|
-
await Promise.all(installedPaths.map((path) => rm(path, { force: true }))).catch(() => undefined);
|
|
910
|
-
await restoreRenamedFiles(backups.map((item) => ({ from: item.backup, to: item.path })));
|
|
911
|
-
if (contextualWorldStateStaged)
|
|
912
|
-
await restoreRenamedFiles([
|
|
913
|
-
{ from: contextualWorldStateBackup, to: contextualWorldStatePath }
|
|
914
|
-
]);
|
|
915
|
-
await restoreRenamedFiles([...generatedTrash, ...resourceTrash]);
|
|
916
|
-
restoreSessionMeta(meta, originalMeta);
|
|
917
|
-
this.#shared.index.revision = originalIndexRevision;
|
|
918
|
-
if (originalSegment === undefined)
|
|
919
|
-
this.#shared.segments.delete(sessionId);
|
|
920
|
-
else
|
|
921
|
-
this.#shared.segments.set(sessionId, originalSegment);
|
|
922
|
-
this.#markIndexDirty(sessionId);
|
|
923
|
-
await recoverSessionStorageTransaction(this.#sessionDirectory(sessionId), originalMeta.storageTransactionId);
|
|
924
|
-
}
|
|
925
|
-
throw error;
|
|
926
|
-
}
|
|
927
|
-
finally {
|
|
928
|
-
await Promise.all(staged.files.map((item) => rm(item.temporaryPath, { force: true }))).catch(() => undefined);
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
async getSession(sessionId) {
|
|
932
|
-
this.#assertOpen();
|
|
933
|
-
await this.#refreshIndex();
|
|
934
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
935
|
-
if (!meta)
|
|
936
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
937
|
-
return {
|
|
938
|
-
id: meta.id,
|
|
939
|
-
title: meta.title,
|
|
940
|
-
workspace: meta.workspace,
|
|
941
|
-
createdAt: meta.createdAt,
|
|
942
|
-
lastActivityAt: meta.lastActivityAt,
|
|
943
|
-
source: normalizeSessionSource(meta.source),
|
|
944
|
-
automaticTitlePending: meta.automaticTitlePending === true
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
/** Serialize physical writes and honor cancellation before entering the writer. */
|
|
948
|
-
async append(sessionId, input, options = {}) {
|
|
949
|
-
this.#assertOpen();
|
|
950
|
-
options.signal?.throwIfAborted();
|
|
951
|
-
return this.#serializeRecordOperation(sessionId, () => {
|
|
952
|
-
options.signal?.throwIfAborted();
|
|
953
|
-
return this.#appendRecord(sessionId, input);
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
async #serializeRecordOperation(sessionId, action) {
|
|
957
|
-
const operation = (this.#shared.recordWrites.get(sessionId) ?? Promise.resolve())
|
|
958
|
-
.catch(() => undefined)
|
|
959
|
-
.then(action);
|
|
960
|
-
this.#shared.recordWrites.set(sessionId, operation);
|
|
961
|
-
try {
|
|
962
|
-
return await operation;
|
|
963
|
-
}
|
|
964
|
-
finally {
|
|
965
|
-
if (this.#shared.recordWrites.get(sessionId) === operation)
|
|
966
|
-
this.#shared.recordWrites.delete(sessionId);
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
async #appendRecord(sessionId, input) {
|
|
970
|
-
this.#assertOpen();
|
|
971
|
-
const writer = this.#shared.writerLeases.get(sessionId);
|
|
972
|
-
if (writer?.appendFailure)
|
|
973
|
-
throw writer.appendFailure.error;
|
|
974
|
-
const before = this.#shared.index.sessions.find((item) => item.id === sessionId) ??
|
|
975
|
-
this.#shared.pendingSessions.get(sessionId);
|
|
976
|
-
if (!before)
|
|
977
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
978
|
-
const published = !this.#shared.pendingSessions.has(sessionId);
|
|
979
|
-
const record = {
|
|
980
|
-
schemaVersion: 1,
|
|
981
|
-
recordId: randomUUID(),
|
|
982
|
-
sequence: before.nextSequence,
|
|
983
|
-
timestamp: input.timestamp ?? new Date().toISOString(),
|
|
984
|
-
sessionId,
|
|
985
|
-
...(input.executionId ? { executionId: input.executionId } : {}),
|
|
986
|
-
...(input.turnId ? { turnId: input.turnId } : {}),
|
|
987
|
-
type: input.type,
|
|
988
|
-
payload: input.payload
|
|
989
|
-
};
|
|
990
|
-
JSON.stringify(record); // Reject malformed payloads before binding resources.
|
|
991
|
-
await bindToolResultResources(this.#sessionDirectory(sessionId), record, (sequence) => this.#readRecordAtSequence(before, sequence), true);
|
|
992
|
-
const serialized = `${JSON.stringify(record)}\n`;
|
|
993
|
-
let segment = await this.#currentSegment(sessionId);
|
|
994
|
-
let path = this.#segmentPath(sessionId, segment);
|
|
995
|
-
try {
|
|
996
|
-
const current = await stat(path);
|
|
997
|
-
if (current.size > 0 && current.size + Buffer.byteLength(serialized) > MAX_SEGMENT_BYTES) {
|
|
998
|
-
segment += 1;
|
|
999
|
-
this.#shared.segments.set(sessionId, segment);
|
|
1000
|
-
path = this.#segmentPath(sessionId, segment);
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
catch {
|
|
1004
|
-
// The first append creates the first segment.
|
|
1005
|
-
}
|
|
1006
|
-
try {
|
|
1007
|
-
await appendRecordBytes(path, serialized, (error) => {
|
|
1008
|
-
if (writer)
|
|
1009
|
-
writer.appendFailure = { error };
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
catch (error) {
|
|
1013
|
-
// A failed write/sync/close can leave a complete record on disk.
|
|
1014
|
-
const current = this.#shared.index.sessions.find((item) => item.id === sessionId) ?? before;
|
|
1015
|
-
if (current.resume?.userTurns !== undefined) {
|
|
1016
|
-
delete current.resume.userTurns;
|
|
1017
|
-
if (published)
|
|
1018
|
-
this.#markIndexDirty(sessionId);
|
|
1019
|
-
}
|
|
1020
|
-
const lease = this.#shared.writerLeases.get(sessionId);
|
|
1021
|
-
if (lease) {
|
|
1022
|
-
delete lease.codeState;
|
|
1023
|
-
delete lease.extensionCheckpoints;
|
|
1024
|
-
}
|
|
1025
|
-
throw error;
|
|
1026
|
-
}
|
|
1027
|
-
// Index refreshes and commits can replace metadata during file I/O. Publish all
|
|
1028
|
-
// durable-record metadata onto the current entry without another await boundary.
|
|
1029
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId) ??
|
|
1030
|
-
this.#shared.pendingSessions.get(sessionId);
|
|
1031
|
-
if (!meta)
|
|
1032
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1033
|
-
meta.nextSequence = record.sequence + 1;
|
|
1034
|
-
const segmentMetadata = meta.segments ?? (meta.segments = []);
|
|
1035
|
-
const known = segmentMetadata.find((item) => item.number === segment);
|
|
1036
|
-
if (known) {
|
|
1037
|
-
known.lastSequence = record.sequence;
|
|
1038
|
-
known.bytes += Buffer.byteLength(serialized);
|
|
1039
|
-
}
|
|
1040
|
-
else
|
|
1041
|
-
segmentMetadata.push({
|
|
1042
|
-
number: segment,
|
|
1043
|
-
firstSequence: record.sequence,
|
|
1044
|
-
lastSequence: record.sequence,
|
|
1045
|
-
bytes: Buffer.byteLength(serialized)
|
|
1046
|
-
});
|
|
1047
|
-
meta.lastActivityAt = record.timestamp;
|
|
1048
|
-
this.#applyRecordToResumeIndex(meta, record);
|
|
1049
|
-
if (published) {
|
|
1050
|
-
this.#shared.index.revision++;
|
|
1051
|
-
this.#markIndexDirty(sessionId);
|
|
1052
|
-
}
|
|
1053
|
-
updateSessionCurrentState(this.#shared.writerLeases.get(sessionId), record.type, serialized);
|
|
1054
|
-
await completeToolResultResources(this.#sessionDirectory(sessionId), record);
|
|
1055
|
-
await completeResources(join(this.#sessionDirectory(sessionId), 'resources'), record);
|
|
1056
|
-
return record;
|
|
1057
|
-
}
|
|
1058
|
-
async read(sessionId) {
|
|
1059
|
-
return this.#serializeRecordOperation(sessionId, () => this.#readRecords(sessionId));
|
|
1060
|
-
}
|
|
1061
|
-
async #readRecords(sessionId) {
|
|
1062
|
-
this.#assertOpen();
|
|
1063
|
-
try {
|
|
1064
|
-
const records = [];
|
|
1065
|
-
for (const path of await this.#segmentPaths(sessionId))
|
|
1066
|
-
records.push(...(await readRecords(path)));
|
|
1067
|
-
return records;
|
|
1068
|
-
}
|
|
1069
|
-
catch (cause) {
|
|
1070
|
-
if (cause instanceof MarAgentError)
|
|
1071
|
-
throw cause;
|
|
1072
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.', { cause });
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
async readCodeModeState(sessionId) {
|
|
1076
|
-
return this.#serializeRecordOperation(sessionId, () => this.#readCodeModeState(sessionId));
|
|
1077
|
-
}
|
|
1078
|
-
async #readCodeModeState(sessionId) {
|
|
1079
|
-
this.#assertOpen();
|
|
1080
|
-
const lease = this.#shared.writerLeases.get(sessionId);
|
|
1081
|
-
if (lease?.owner === undefined)
|
|
1082
|
-
return restoreCodeModeState(await this.#readRecords(sessionId));
|
|
1083
|
-
lease.codeState ??= restoreCodeModeState(await this.#readRecords(sessionId));
|
|
1084
|
-
return applyCodeModeStateMutations(lease.codeState, []);
|
|
1085
|
-
}
|
|
1086
|
-
async readExtensionCheckpoint(sessionId, extensionId, key) {
|
|
1087
|
-
return this.#serializeRecordOperation(sessionId, () => this.#readExtensionCheckpoint(sessionId, extensionId, key));
|
|
1088
|
-
}
|
|
1089
|
-
async #readExtensionCheckpoint(sessionId, extensionId, key) {
|
|
1090
|
-
this.#assertOpen();
|
|
1091
|
-
const lease = this.#shared.writerLeases.get(sessionId);
|
|
1092
|
-
if (lease?.owner === undefined)
|
|
1093
|
-
return restoreExtensionCheckpoint(await this.#readRecords(sessionId), extensionId, key);
|
|
1094
|
-
if (!lease.extensionCheckpoints) {
|
|
1095
|
-
const index = new Map();
|
|
1096
|
-
for (const record of await this.#readRecords(sessionId))
|
|
1097
|
-
if (record.type === 'extension.checkpoint.updated')
|
|
1098
|
-
indexExtensionCheckpoint(index, record.payload);
|
|
1099
|
-
lease.extensionCheckpoints = index;
|
|
1100
|
-
}
|
|
1101
|
-
return extensionCheckpointFromPayload(lease.extensionCheckpoints.get(extensionId)?.get(key));
|
|
1102
|
-
}
|
|
1103
|
-
async writeExtensionCheckpoint(sessionId, extensionId, key, value, expectedRevision, input) {
|
|
1104
|
-
if (typeof key !== 'string' ||
|
|
1105
|
-
(expectedRevision !== undefined &&
|
|
1106
|
-
(!Number.isSafeInteger(expectedRevision) || expectedRevision < 0)))
|
|
1107
|
-
throw new MarAgentError('MAR_AGENT_EXTENSION_DATA_INVALID', 'Checkpoint key or expected revision is invalid.');
|
|
1108
|
-
const copied = value === undefined ? undefined : cloneCoreJson(value);
|
|
1109
|
-
input.signal.throwIfAborted();
|
|
1110
|
-
const release = await this.acquireSessionWriteLease(sessionId, input.owner);
|
|
1111
|
-
try {
|
|
1112
|
-
return await this.#serializeRecordOperation(sessionId, async () => {
|
|
1113
|
-
const current = await this.#readExtensionCheckpoint(sessionId, extensionId, key);
|
|
1114
|
-
if (expectedRevision !== undefined && expectedRevision !== current.revision)
|
|
1115
|
-
throw new MarAgentError('MAR_AGENT_EXTENSION_DATA_CONFLICT', 'Checkpoint revision changed before commit.');
|
|
1116
|
-
input.signal.throwIfAborted();
|
|
1117
|
-
const revision = current.revision + 1;
|
|
1118
|
-
await this.#appendRecord(sessionId, {
|
|
1119
|
-
type: 'extension.checkpoint.updated',
|
|
1120
|
-
payload: {
|
|
1121
|
-
extensionId,
|
|
1122
|
-
key,
|
|
1123
|
-
revision,
|
|
1124
|
-
...(copied === undefined ? { deleted: true } : { value: copied })
|
|
1125
|
-
},
|
|
1126
|
-
...(input.executionId ? { executionId: input.executionId } : {}),
|
|
1127
|
-
...(input.turnId ? { turnId: input.turnId } : {})
|
|
1128
|
-
});
|
|
1129
|
-
return { revision, ...(copied === undefined ? {} : { value: cloneCoreJson(copied) }) };
|
|
1130
|
-
});
|
|
1131
|
-
}
|
|
1132
|
-
finally {
|
|
1133
|
-
await release();
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
async commitCodeModeState(sessionId, mutations, input) {
|
|
1137
|
-
const operation = (this.#shared.codeStateWrites.get(sessionId) ?? Promise.resolve())
|
|
1138
|
-
.catch(() => undefined)
|
|
1139
|
-
.then(async () => {
|
|
1140
|
-
const parsed = parseCodeModeStateMutations(mutations);
|
|
1141
|
-
if (parsed.length === 0)
|
|
1142
|
-
return this.readCodeModeState(sessionId);
|
|
1143
|
-
const current = await this.readCodeModeState(sessionId);
|
|
1144
|
-
if (input.expectedState && !isDeepStrictEqual(current, input.expectedState))
|
|
1145
|
-
throw new MarAgentError('MAR_AGENT_CODE_STATE_CONFLICT', 'Session state changed while this cell was running.');
|
|
1146
|
-
const state = applyCodeModeStateMutations(current, parsed);
|
|
1147
|
-
await this.append(sessionId, {
|
|
1148
|
-
type: 'code.state.updated',
|
|
1149
|
-
payload: { mutations: parsed },
|
|
1150
|
-
executionId: input.executionId,
|
|
1151
|
-
turnId: input.turnId
|
|
1152
|
-
});
|
|
1153
|
-
return state;
|
|
1154
|
-
});
|
|
1155
|
-
this.#shared.codeStateWrites.set(sessionId, operation);
|
|
1156
|
-
try {
|
|
1157
|
-
return await operation;
|
|
1158
|
-
}
|
|
1159
|
-
finally {
|
|
1160
|
-
if (this.#shared.codeStateWrites.get(sessionId) === operation)
|
|
1161
|
-
this.#shared.codeStateWrites.delete(sessionId);
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
async readExecutionState(sessionId) {
|
|
1165
|
-
this.#assertOpen();
|
|
1166
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
1167
|
-
if (!meta)
|
|
1168
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1169
|
-
if (meta.resume?.version !== 1 ||
|
|
1170
|
-
typeof meta.automaticTitlePending !== 'boolean' ||
|
|
1171
|
-
meta.resume.compactSequence === undefined)
|
|
1172
|
-
return this.#rebuildExecutionState(meta);
|
|
1173
|
-
try {
|
|
1174
|
-
const contextRecords = await this.#readFromSequence(meta, meta.resume.compactSequence);
|
|
1175
|
-
if (contextRecords[0]?.sequence !== meta.resume.compactSequence ||
|
|
1176
|
-
!isCompactCheckpoint(contextRecords[0]) ||
|
|
1177
|
-
contextRecords.some((record, index) => index > 0 && isCompactCheckpoint(record)))
|
|
1178
|
-
return this.#rebuildExecutionState(meta);
|
|
1179
|
-
const todo = await this.#readIndexedTodo(meta, contextRecords);
|
|
1180
|
-
if (!todo)
|
|
1181
|
-
return this.#rebuildExecutionState(meta);
|
|
1182
|
-
return {
|
|
1183
|
-
automaticTitlePending: meta.automaticTitlePending,
|
|
1184
|
-
todo,
|
|
1185
|
-
contextRecords
|
|
1186
|
-
};
|
|
1187
|
-
}
|
|
1188
|
-
catch {
|
|
1189
|
-
return this.#rebuildExecutionState(meta);
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
/** Compare the planned sequence inside the existing record writer queue. */
|
|
1193
|
-
async commitContextGc(sessionId, draft, planningRecords) {
|
|
1194
|
-
this.#assertOpen();
|
|
1195
|
-
const records = planningRecords ?? (await this.readExecutionState(sessionId)).contextRecords;
|
|
1196
|
-
return this.#serializeRecordOperation(sessionId, async () => {
|
|
1197
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
1198
|
-
if (!meta)
|
|
1199
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1200
|
-
const boundarySequence = meta.nextSequence - 1;
|
|
1201
|
-
if (boundarySequence !== draft.boundarySequence)
|
|
1202
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Context GC projection changed before checkpoint commit.');
|
|
1203
|
-
if (records.at(-1)?.sequence !== boundarySequence ||
|
|
1204
|
-
records.at(-1)?.sessionId !== sessionId ||
|
|
1205
|
-
!sameContextGcBase(contextGcBaseFromRecords(records, boundarySequence), draft.base))
|
|
1206
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Context GC planning boundary is invalid.');
|
|
1207
|
-
const previous = latestValidContextGcCheckpoint(records, boundarySequence);
|
|
1208
|
-
if ((!draft.inlineMedia || draft.inlineMedia.reason === 'TEXT_GC') &&
|
|
1209
|
-
!contextGcCheckpointIntervalEligible(draft.trigger, previous?.payload.trigger))
|
|
1210
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Context GC checkpoint interval validation failed.');
|
|
1211
|
-
const plannedReplacements = normalizeContextGcReplacements(draft.replacements);
|
|
1212
|
-
if (!plannedReplacements || (plannedReplacements.length === 0 && !draft.inlineMedia))
|
|
1213
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Context GC checkpoint must contain a replacement.');
|
|
1214
|
-
const replacements = normalizeContextGcReplacements([
|
|
1215
|
-
...(previous?.payload.replacements ?? []),
|
|
1216
|
-
...plannedReplacements
|
|
1217
|
-
]);
|
|
1218
|
-
if (!replacements ||
|
|
1219
|
-
(previous &&
|
|
1220
|
-
isDeepStrictEqual(replacements, previous.payload.replacements) &&
|
|
1221
|
-
(!draft.inlineMedia ||
|
|
1222
|
-
isDeepStrictEqual(draft.inlineMedia, previous.payload.inlineMedia))))
|
|
1223
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Context GC replacement map is invalid.');
|
|
1224
|
-
const payload = {
|
|
1225
|
-
...structuredClone(draft),
|
|
1226
|
-
checkpointId: randomUUID(),
|
|
1227
|
-
generation: (previous?.payload.generation ?? 0) + 1,
|
|
1228
|
-
replacements
|
|
1229
|
-
};
|
|
1230
|
-
const candidate = {
|
|
1231
|
-
schemaVersion: 1,
|
|
1232
|
-
recordId: randomUUID(),
|
|
1233
|
-
sequence: boundarySequence + 1,
|
|
1234
|
-
timestamp: new Date().toISOString(),
|
|
1235
|
-
sessionId,
|
|
1236
|
-
type: 'context.gc.completed',
|
|
1237
|
-
payload
|
|
1238
|
-
};
|
|
1239
|
-
if (Buffer.byteLength(JSON.stringify(payload)) >= MAX_SEGMENT_BYTES ||
|
|
1240
|
-
validateContextGcCheckpoint(records, candidate, meta.workspace, previous?.payload.replacements) === undefined)
|
|
1241
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Context GC checkpoint validation failed.');
|
|
1242
|
-
const turnId = records.findLast((record) => record.type === 'execution.header')?.turnId;
|
|
1243
|
-
return this.#appendRecord(sessionId, {
|
|
1244
|
-
type: 'context.gc.completed',
|
|
1245
|
-
...(turnId === undefined ? {} : { turnId }),
|
|
1246
|
-
payload
|
|
1247
|
-
});
|
|
1248
|
-
});
|
|
1249
|
-
}
|
|
1250
|
-
async readPage(sessionId, input = {}) {
|
|
1251
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
1252
|
-
if (!meta)
|
|
1253
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1254
|
-
const before = input.beforeSequence ?? Number.POSITIVE_INFINITY;
|
|
1255
|
-
const limit = Math.min(1000, Math.max(1, input.limit ?? 100));
|
|
1256
|
-
const page = [];
|
|
1257
|
-
let hasEarlier = false;
|
|
1258
|
-
const paths = (await this.#segmentPaths(sessionId)).reverse();
|
|
1259
|
-
for (const [pathIndex, path] of paths.entries()) {
|
|
1260
|
-
const segmentNumber = segmentNumberFromPath(path);
|
|
1261
|
-
const segment = meta.segments?.find((item) => item.number === segmentNumber);
|
|
1262
|
-
if (segment !== undefined && segment.firstSequence >= before)
|
|
1263
|
-
continue;
|
|
1264
|
-
const needed = limit - page.length;
|
|
1265
|
-
const window = await readRecordPageFromTail(path, before, needed, this.#shared.historyPagePositions);
|
|
1266
|
-
page.unshift(...window.records);
|
|
1267
|
-
if (window.hasEarlier) {
|
|
1268
|
-
hasEarlier = true;
|
|
1269
|
-
break;
|
|
1270
|
-
}
|
|
1271
|
-
if (page.length >= limit) {
|
|
1272
|
-
hasEarlier = pathIndex < paths.length - 1;
|
|
1273
|
-
break;
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
const previousBeforeSequence = hasEarlier ? page[0]?.sequence : undefined;
|
|
1277
|
-
return { records: page, ...(previousBeforeSequence ? { previousBeforeSequence } : {}) };
|
|
1278
|
-
}
|
|
1279
|
-
async compact(sessionId, input, options = {}) {
|
|
1280
|
-
if (!input.summary.trim())
|
|
1281
|
-
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Compact summary is required.');
|
|
1282
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
1283
|
-
if (!meta)
|
|
1284
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1285
|
-
return this.append(sessionId, {
|
|
1286
|
-
type: 'compact.completed',
|
|
1287
|
-
payload: {
|
|
1288
|
-
summary: input.summary,
|
|
1289
|
-
...(input.pendingInputs?.length ? { pendingInputs: input.pendingInputs } : {}),
|
|
1290
|
-
...(input.snapshotRecordId === undefined
|
|
1291
|
-
? {}
|
|
1292
|
-
: { snapshotRecordId: input.snapshotRecordId }),
|
|
1293
|
-
...(input.snapshotMessageCount === undefined
|
|
1294
|
-
? {}
|
|
1295
|
-
: { snapshotMessageCount: input.snapshotMessageCount }),
|
|
1296
|
-
...(input.replacementHistory === undefined
|
|
1297
|
-
? {}
|
|
1298
|
-
: { replacementHistory: input.replacementHistory }),
|
|
1299
|
-
...(input.retainedModelContext?.length
|
|
1300
|
-
? { retainedModelContext: input.retainedModelContext }
|
|
1301
|
-
: {}),
|
|
1302
|
-
...(input.inlineMediaState ? { inlineMediaState: input.inlineMediaState } : {}),
|
|
1303
|
-
...(input.environment === undefined ? {} : { environment: input.environment }),
|
|
1304
|
-
boundarySequence: meta.nextSequence - 1,
|
|
1305
|
-
automatic: input.automatic === true,
|
|
1306
|
-
...(input.modelId === undefined ? {} : { modelId: input.modelId }),
|
|
1307
|
-
...(input.contextWindowTokens === undefined
|
|
1308
|
-
? {}
|
|
1309
|
-
: { contextWindowTokens: input.contextWindowTokens }),
|
|
1310
|
-
...(input.usage === undefined ? {} : { usage: input.usage })
|
|
1311
|
-
},
|
|
1312
|
-
...(input.executionId ? { executionId: input.executionId } : {}),
|
|
1313
|
-
...(input.turnId ? { turnId: input.turnId } : {})
|
|
1314
|
-
}, options);
|
|
1315
|
-
}
|
|
1316
|
-
/** A warm cell retains physical writer ownership between Executions of the same Agent. */
|
|
1317
|
-
async acquireSessionLease(sessionId, owner) {
|
|
1318
|
-
return this.#serializeRecordOperation(sessionId, async () => {
|
|
1319
|
-
this.#assertOpen();
|
|
1320
|
-
let lease = this.#shared.writerLeases.get(sessionId);
|
|
1321
|
-
if (lease) {
|
|
1322
|
-
if (lease.maintenance || lease.executing || owner === undefined || lease.owner !== owner)
|
|
1323
|
-
throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session already has a writer.');
|
|
1324
|
-
if (lease.appendFailure)
|
|
1325
|
-
throw lease.appendFailure.error;
|
|
1326
|
-
lease.executing = true;
|
|
1327
|
-
}
|
|
1328
|
-
else {
|
|
1329
|
-
const release = await this.#acquireSessionLease(sessionId);
|
|
1330
|
-
lease = { owner, executing: true, retained: 0, release };
|
|
1331
|
-
this.#shared.writerLeases.set(sessionId, lease);
|
|
1332
|
-
}
|
|
1333
|
-
const current = lease;
|
|
1334
|
-
let released = false;
|
|
1335
|
-
return () => this.#serializeRecordOperation(sessionId, async () => {
|
|
1336
|
-
if (released)
|
|
1337
|
-
return;
|
|
1338
|
-
released = true;
|
|
1339
|
-
current.executing = false;
|
|
1340
|
-
await this.#releaseUnusedWriter(sessionId, current);
|
|
1341
|
-
});
|
|
1342
|
-
});
|
|
1343
|
-
}
|
|
1344
|
-
hasRetainedSessionLease(sessionId, owner) {
|
|
1345
|
-
const lease = this.#shared.writerLeases.get(sessionId);
|
|
1346
|
-
return lease?.owner === owner && lease.retained > 0;
|
|
1347
|
-
}
|
|
1348
|
-
async acquireSessionMaintenance(sessionId, kind, owner) {
|
|
1349
|
-
this.#assertOpen();
|
|
1350
|
-
const acquired = new Map();
|
|
1351
|
-
let sessionIds = [];
|
|
1352
|
-
let released = false;
|
|
1353
|
-
const holding = Object.freeze({
|
|
1354
|
-
get sessionIds() {
|
|
1355
|
-
return sessionIds;
|
|
1356
|
-
},
|
|
1357
|
-
release: async () => {
|
|
1358
|
-
if (released)
|
|
1359
|
-
return;
|
|
1360
|
-
released = true;
|
|
1361
|
-
this.#maintenance.delete(holding);
|
|
1362
|
-
let failure;
|
|
1363
|
-
for (const [id, writer] of [...acquired].reverse()) {
|
|
1364
|
-
try {
|
|
1365
|
-
await this.#serializeRecordOperation(id, async () => {
|
|
1366
|
-
delete writer.maintenance;
|
|
1367
|
-
await this.#releaseUnusedWriter(id, writer);
|
|
1368
|
-
});
|
|
1369
|
-
}
|
|
1370
|
-
catch (error) {
|
|
1371
|
-
failure ??= { error };
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
if (failure)
|
|
1375
|
-
throw failure.error;
|
|
1376
|
-
}
|
|
1377
|
-
});
|
|
1378
|
-
try {
|
|
1379
|
-
// Re-read membership after acquiring writers; never hand the SDK a pre-lock tree snapshot.
|
|
1380
|
-
for (;;) {
|
|
1381
|
-
const tree = kind === 'delete' ? await this.sessionTree(sessionId) : [sessionId];
|
|
1382
|
-
for (const id of tree.toReversed()) {
|
|
1383
|
-
if (acquired.has(id))
|
|
1384
|
-
continue;
|
|
1385
|
-
await this.#serializeRecordOperation(id, async () => {
|
|
1386
|
-
this.#assertOpen();
|
|
1387
|
-
let writer = this.#shared.writerLeases.get(id);
|
|
1388
|
-
if (writer &&
|
|
1389
|
-
(writer.maintenance ||
|
|
1390
|
-
owner === undefined ||
|
|
1391
|
-
writer.owner !== owner ||
|
|
1392
|
-
(kind === 'clear' && writer.executing)))
|
|
1393
|
-
throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session already has a writer.');
|
|
1394
|
-
if (!writer) {
|
|
1395
|
-
// Deleting a stale index entry must not recreate its missing directory.
|
|
1396
|
-
const release = kind === 'delete' && !(await sessionDirectoryExists(this.#sessionDirectory(id)))
|
|
1397
|
-
? async () => { }
|
|
1398
|
-
: await this.#acquireSessionLease(id);
|
|
1399
|
-
writer = { owner, executing: false, retained: 0, release };
|
|
1400
|
-
this.#shared.writerLeases.set(id, writer);
|
|
1401
|
-
}
|
|
1402
|
-
writer.maintenance = holding;
|
|
1403
|
-
acquired.set(id, writer);
|
|
1404
|
-
});
|
|
1405
|
-
}
|
|
1406
|
-
const current = kind === 'clear' ? tree : await this.sessionTree(sessionId);
|
|
1407
|
-
if (current.every((id) => acquired.has(id))) {
|
|
1408
|
-
sessionIds = Object.freeze(current);
|
|
1409
|
-
break;
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
this.#maintenance.set(holding, { sessionId, kind });
|
|
1413
|
-
return holding;
|
|
1414
|
-
}
|
|
1415
|
-
catch (error) {
|
|
1416
|
-
await holding.release().catch(() => undefined);
|
|
1417
|
-
throw error;
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
#assertMaintenance(sessionId, kind, holding) {
|
|
1421
|
-
const current = this.#maintenance.get(holding);
|
|
1422
|
-
if (current?.sessionId !== sessionId || current.kind !== kind)
|
|
1423
|
-
throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session maintenance ownership is invalid.');
|
|
1424
|
-
}
|
|
1425
|
-
/** Native writes join their Agent's sole writer, without admitting another Execution. */
|
|
1426
|
-
async acquireSessionWriteLease(sessionId, owner) {
|
|
1427
|
-
return this.#serializeRecordOperation(sessionId, async () => {
|
|
1428
|
-
this.#assertOpen();
|
|
1429
|
-
let lease = this.#shared.writerLeases.get(sessionId);
|
|
1430
|
-
if (lease && (lease.maintenance || lease.owner !== owner))
|
|
1431
|
-
throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session already has a writer.');
|
|
1432
|
-
if (lease?.appendFailure)
|
|
1433
|
-
throw lease.appendFailure.error;
|
|
1434
|
-
if (!lease) {
|
|
1435
|
-
const release = await this.#acquireSessionLease(sessionId);
|
|
1436
|
-
lease = { owner, executing: false, retained: 0, release };
|
|
1437
|
-
this.#shared.writerLeases.set(sessionId, lease);
|
|
1438
|
-
}
|
|
1439
|
-
lease.retained++;
|
|
1440
|
-
const current = lease;
|
|
1441
|
-
let released = false;
|
|
1442
|
-
return () => this.#serializeRecordOperation(sessionId, async () => {
|
|
1443
|
-
if (released)
|
|
1444
|
-
return;
|
|
1445
|
-
released = true;
|
|
1446
|
-
current.retained--;
|
|
1447
|
-
await this.#releaseUnusedWriter(sessionId, current);
|
|
1448
|
-
});
|
|
1449
|
-
});
|
|
1450
|
-
}
|
|
1451
|
-
async retainSessionLease(sessionId, owner) {
|
|
1452
|
-
return this.#serializeRecordOperation(sessionId, async () => {
|
|
1453
|
-
const lease = this.#shared.writerLeases.get(sessionId);
|
|
1454
|
-
if (!lease || lease.maintenance || !lease.executing || lease.owner !== owner)
|
|
1455
|
-
throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Retained work requires its originating Session writer.');
|
|
1456
|
-
lease.retained++;
|
|
1457
|
-
let released = false;
|
|
1458
|
-
return () => this.#serializeRecordOperation(sessionId, async () => {
|
|
1459
|
-
if (released)
|
|
1460
|
-
return;
|
|
1461
|
-
released = true;
|
|
1462
|
-
lease.retained--;
|
|
1463
|
-
await this.#releaseUnusedWriter(sessionId, lease);
|
|
1464
|
-
});
|
|
1465
|
-
});
|
|
1466
|
-
}
|
|
1467
|
-
async #releaseUnusedWriter(sessionId, lease) {
|
|
1468
|
-
if (lease.maintenance || lease.executing || lease.retained)
|
|
1469
|
-
return;
|
|
1470
|
-
try {
|
|
1471
|
-
await lease.release();
|
|
1472
|
-
}
|
|
1473
|
-
finally {
|
|
1474
|
-
delete lease.codeState;
|
|
1475
|
-
delete lease.extensionCheckpoints;
|
|
1476
|
-
this.#shared.writerLeases.delete(sessionId);
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
|
-
/** Recover files under the sole writer lock; flush dirty index state before releasing it. */
|
|
1480
|
-
async #acquireSessionLease(sessionId) {
|
|
1481
|
-
this.#assertOpen();
|
|
1482
|
-
await this.#refreshIndex();
|
|
1483
|
-
if (!this.#shared.index.sessions.some((item) => item.id === sessionId))
|
|
1484
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1485
|
-
const path = join(this.#shared.root, 'sessions', sessionId, 'lock');
|
|
1486
|
-
const release = await acquireFileLock(path, 'Session already has a writer.', 0);
|
|
1487
|
-
try {
|
|
1488
|
-
const disk = await readSessionIndex(join(this.#shared.root, 'sessions', 'index.json'));
|
|
1489
|
-
if (!disk)
|
|
1490
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session index is missing.');
|
|
1491
|
-
const current = disk.sessions.find((item) => item.id === sessionId);
|
|
1492
|
-
if (!current)
|
|
1493
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1494
|
-
await recoverSessionStorageTransaction(this.#sessionDirectory(sessionId), current.storageTransactionId);
|
|
1495
|
-
await recoverContextualWorldStateTransactions(this.#shared.root, current);
|
|
1496
|
-
await this.#refreshIndex();
|
|
1497
|
-
await this.#reconcileTail(sessionId);
|
|
1498
|
-
}
|
|
1499
|
-
catch (error) {
|
|
1500
|
-
await release();
|
|
1501
|
-
throw error;
|
|
1502
|
-
}
|
|
1503
|
-
return async () => {
|
|
1504
|
-
try {
|
|
1505
|
-
if (this.#shared.indexDirty)
|
|
1506
|
-
await this.#saveIndex();
|
|
1507
|
-
}
|
|
1508
|
-
finally {
|
|
1509
|
-
await release();
|
|
1510
|
-
}
|
|
1511
|
-
};
|
|
1512
|
-
}
|
|
1513
|
-
async list(input = {}) {
|
|
1514
|
-
this.#assertOpen();
|
|
1515
|
-
await this.#refreshIndex();
|
|
1516
|
-
const filter = sessionListFilter(input);
|
|
1517
|
-
const start = decodeSessionListCursor(input.cursor, this.#shared.index.revision, filter);
|
|
1518
|
-
const limit = Math.min(200, Math.max(1, input.limit ?? 50));
|
|
1519
|
-
const ordered = this.#shared.index.sessions
|
|
1520
|
-
.filter((session) => sessionMatchesFilter(session, filter))
|
|
1521
|
-
.sort((a, b) => b.lastActivityAt.localeCompare(a.lastActivityAt));
|
|
1522
|
-
const sessions = ordered.slice(start, start + limit).map((session) => ({
|
|
1523
|
-
id: session.id,
|
|
1524
|
-
title: session.title,
|
|
1525
|
-
...(session.resume?.firstUserMessagePreview === undefined
|
|
1526
|
-
? {}
|
|
1527
|
-
: { firstUserMessagePreview: session.resume.firstUserMessagePreview }),
|
|
1528
|
-
workspace: session.workspace,
|
|
1529
|
-
createdAt: session.createdAt,
|
|
1530
|
-
lastActivityAt: session.lastActivityAt,
|
|
1531
|
-
source: normalizeSessionSource(session.source),
|
|
1532
|
-
...(session.resume?.modelId === undefined
|
|
1533
|
-
? {}
|
|
1534
|
-
: {
|
|
1535
|
-
modelId: session.resume.modelId,
|
|
1536
|
-
...(session.resume.modelName === undefined
|
|
1537
|
-
? {}
|
|
1538
|
-
: { modelName: session.resume.modelName }),
|
|
1539
|
-
...(session.resume.reasoningEffort === undefined
|
|
1540
|
-
? {}
|
|
1541
|
-
: { reasoningEffort: session.resume.reasoningEffort })
|
|
1542
|
-
})
|
|
1543
|
-
}));
|
|
1544
|
-
const next = start + limit < ordered.length
|
|
1545
|
-
? encodeSessionListCursor(this.#shared.index.revision, start + limit, filter)
|
|
1546
|
-
: undefined;
|
|
1547
|
-
return { sessions, ...(next ? { nextCursor: next } : {}) };
|
|
1548
|
-
}
|
|
1549
|
-
async listSubagentChildren(parentSessionId) {
|
|
1550
|
-
this.#assertOpen();
|
|
1551
|
-
await this.#refreshIndex();
|
|
1552
|
-
return this.#shared.index.sessions
|
|
1553
|
-
.map((session) => ({ session, source: normalizeSessionSource(session.source) }))
|
|
1554
|
-
.filter((item) => item.source.type === 'subagent' && item.source.parentSessionId === parentSessionId)
|
|
1555
|
-
.map(({ session, source }) => ({
|
|
1556
|
-
id: session.id,
|
|
1557
|
-
title: session.title,
|
|
1558
|
-
workspace: session.workspace,
|
|
1559
|
-
createdAt: session.createdAt,
|
|
1560
|
-
lastActivityAt: session.lastActivityAt,
|
|
1561
|
-
source,
|
|
1562
|
-
...(session.resume?.modelId === undefined
|
|
1563
|
-
? {}
|
|
1564
|
-
: {
|
|
1565
|
-
modelId: session.resume.modelId,
|
|
1566
|
-
...(session.resume.modelName === undefined
|
|
1567
|
-
? {}
|
|
1568
|
-
: { modelName: session.resume.modelName }),
|
|
1569
|
-
...(session.resume.reasoningEffort === undefined
|
|
1570
|
-
? {}
|
|
1571
|
-
: { reasoningEffort: session.resume.reasoningEffort })
|
|
1572
|
-
})
|
|
1573
|
-
}));
|
|
1574
|
-
}
|
|
1575
|
-
async executionConfiguration(sessionId) {
|
|
1576
|
-
this.#assertOpen();
|
|
1577
|
-
await this.#refreshIndex();
|
|
1578
|
-
let meta = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1579
|
-
if (meta === undefined)
|
|
1580
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1581
|
-
if (meta.resume?.executionModelIndexed !== true) {
|
|
1582
|
-
await this.#ensureExecutionModelIndexed(sessionId);
|
|
1583
|
-
meta = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1584
|
-
if (meta === undefined)
|
|
1585
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1586
|
-
}
|
|
1587
|
-
return meta.resume?.modelId === undefined
|
|
1588
|
-
? undefined
|
|
1589
|
-
: {
|
|
1590
|
-
modelId: meta.resume.modelId,
|
|
1591
|
-
...(meta.resume.modelName === undefined ? {} : { modelName: meta.resume.modelName }),
|
|
1592
|
-
...(meta.resume.reasoningEffort === undefined
|
|
1593
|
-
? {}
|
|
1594
|
-
: { reasoningEffort: meta.resume.reasoningEffort })
|
|
1595
|
-
};
|
|
1596
|
-
}
|
|
1597
|
-
async sessionTree(sessionId) {
|
|
1598
|
-
this.#assertOpen();
|
|
1599
|
-
await this.#refreshIndex();
|
|
1600
|
-
if (!this.#shared.index.sessions.some((session) => session.id === sessionId))
|
|
1601
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1602
|
-
return sessionTreeIds(this.#shared.index.sessions, sessionId);
|
|
1603
|
-
}
|
|
1604
|
-
#applyRecordToResumeIndex(meta, record) {
|
|
1605
|
-
if (record.type === 'turn.user')
|
|
1606
|
-
meta.automaticTitlePending = false;
|
|
1607
|
-
if (record.type === 'session.title.updated' && isObject(record.payload)) {
|
|
1608
|
-
if (typeof record.payload.title === 'string')
|
|
1609
|
-
meta.title = record.payload.title;
|
|
1610
|
-
meta.automaticTitlePending = false;
|
|
1611
|
-
}
|
|
1612
|
-
const resume = meta.resume;
|
|
1613
|
-
if (resume?.version !== 1 || !Number.isSafeInteger(resume.todoRevision))
|
|
1614
|
-
return;
|
|
1615
|
-
if (resume.userTurns !== undefined && isNativeUserTurn(record))
|
|
1616
|
-
resume.userTurns++;
|
|
1617
|
-
if (resume.firstUserMessageIndexed === true && resume.firstUserMessagePreview === undefined) {
|
|
1618
|
-
const preview = userMessagePreviewFromRecord(record);
|
|
1619
|
-
if (preview !== undefined)
|
|
1620
|
-
resume.firstUserMessagePreview = preview;
|
|
1621
|
-
}
|
|
1622
|
-
const executionModel = executionModelFromRecord(record);
|
|
1623
|
-
if (executionModel !== undefined) {
|
|
1624
|
-
resume.executionModelIndexed = true;
|
|
1625
|
-
resume.modelId = executionModel.modelId;
|
|
1626
|
-
if (executionModel.modelName === undefined)
|
|
1627
|
-
delete resume.modelName;
|
|
1628
|
-
else
|
|
1629
|
-
resume.modelName = executionModel.modelName;
|
|
1630
|
-
if (executionModel.reasoningEffort === undefined)
|
|
1631
|
-
delete resume.reasoningEffort;
|
|
1632
|
-
else
|
|
1633
|
-
resume.reasoningEffort = executionModel.reasoningEffort;
|
|
1634
|
-
}
|
|
1635
|
-
if (isCompactCheckpoint(record))
|
|
1636
|
-
resume.compactSequence = record.sequence;
|
|
1637
|
-
if (record.type !== 'todo.updated' || !todoItemsFromSnapshot(record.payload))
|
|
1638
|
-
return;
|
|
1639
|
-
resume.todoSequence = record.sequence;
|
|
1640
|
-
resume.todoRevision++;
|
|
1641
|
-
}
|
|
1642
|
-
async #readIndexedTodo(meta, contextRecords) {
|
|
1643
|
-
const resume = meta.resume;
|
|
1644
|
-
if (resume?.version !== 1 ||
|
|
1645
|
-
!Number.isSafeInteger(resume.todoRevision) ||
|
|
1646
|
-
resume.todoRevision < 0)
|
|
1647
|
-
return undefined;
|
|
1648
|
-
const latestContextTodo = contextRecords.findLast((record) => record.type === 'todo.updated' && todoItemsFromSnapshot(record.payload) !== undefined);
|
|
1649
|
-
if (latestContextTodo &&
|
|
1650
|
-
(resume.todoSequence === undefined || latestContextTodo.sequence !== resume.todoSequence))
|
|
1651
|
-
return undefined;
|
|
1652
|
-
if (resume.todoSequence === undefined)
|
|
1653
|
-
return resume.todoRevision === 0 ? { revision: 0, items: [] } : undefined;
|
|
1654
|
-
if (resume.todoRevision === 0)
|
|
1655
|
-
return undefined;
|
|
1656
|
-
const record = contextRecords.find((item) => item.sequence === resume.todoSequence) ??
|
|
1657
|
-
(await this.#readRecordAtSequence(meta, resume.todoSequence));
|
|
1658
|
-
if (record?.type !== 'todo.updated')
|
|
1659
|
-
return undefined;
|
|
1660
|
-
const items = todoItemsFromSnapshot(record.payload);
|
|
1661
|
-
return items ? { revision: resume.todoRevision, items } : undefined;
|
|
1662
|
-
}
|
|
1663
|
-
async #readRecordAtSequence(meta, sequence) {
|
|
1664
|
-
const segment = meta.segments?.find((item) => item.firstSequence <= sequence && item.lastSequence >= sequence);
|
|
1665
|
-
if (!segment)
|
|
1666
|
-
return undefined;
|
|
1667
|
-
return (await readRecords(this.#segmentPath(meta.id, segment.number))).find((record) => record.sequence === sequence);
|
|
1668
|
-
}
|
|
1669
|
-
async #readFromSequence(meta, sequence) {
|
|
1670
|
-
const segments = (meta.segments ?? [])
|
|
1671
|
-
.filter((item) => item.lastSequence >= sequence)
|
|
1672
|
-
.sort((left, right) => left.number - right.number);
|
|
1673
|
-
if (segments.length === 0)
|
|
1674
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session resume index is invalid.');
|
|
1675
|
-
const records = [];
|
|
1676
|
-
for (const segment of segments)
|
|
1677
|
-
records.push(...(await readRecords(this.#segmentPath(meta.id, segment.number))).filter((record) => record.sequence >= sequence));
|
|
1678
|
-
return records;
|
|
1679
|
-
}
|
|
1680
|
-
async #rebuildExecutionState(meta) {
|
|
1681
|
-
const state = deriveExecutionState(await this.read(meta.id));
|
|
1682
|
-
if (meta.automaticTitlePending !== state.automaticTitlePending ||
|
|
1683
|
-
!sameResumeIndex(meta.resume, state.resume)) {
|
|
1684
|
-
meta.automaticTitlePending = state.automaticTitlePending;
|
|
1685
|
-
meta.resume = state.resume;
|
|
1686
|
-
this.#shared.index.revision++;
|
|
1687
|
-
this.#markIndexDirty(meta.id);
|
|
1688
|
-
}
|
|
1689
|
-
return {
|
|
1690
|
-
automaticTitlePending: state.automaticTitlePending,
|
|
1691
|
-
todo: state.todo,
|
|
1692
|
-
contextRecords: state.contextRecords
|
|
1693
|
-
};
|
|
1694
|
-
}
|
|
1695
|
-
/** Counts native input records, not the post-Compact model context. */
|
|
1696
|
-
async userTurnCount(sessionId) {
|
|
1697
|
-
return this.#serializeRecordOperation(sessionId, async () => {
|
|
1698
|
-
this.#assertOpen();
|
|
1699
|
-
await this.#refreshIndex();
|
|
1700
|
-
const before = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1701
|
-
if (!before)
|
|
1702
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1703
|
-
const indexed = before.resume?.userTurns;
|
|
1704
|
-
if (indexed !== undefined && this.#shared.writerLeases.has(sessionId))
|
|
1705
|
-
return indexed;
|
|
1706
|
-
const nextSequence = before.nextSequence;
|
|
1707
|
-
const transactionId = before.storageTransactionId;
|
|
1708
|
-
const paths = await this.#segmentPaths(sessionId);
|
|
1709
|
-
let count = indexed ?? 0;
|
|
1710
|
-
let lastSequence = 0;
|
|
1711
|
-
let reachedPrefix = false;
|
|
1712
|
-
if (indexed !== undefined) {
|
|
1713
|
-
// No local writer: a previous writer may have committed bytes but not its index.
|
|
1714
|
-
for (const path of [...paths].reverse()) {
|
|
1715
|
-
for await (const record of readRecordsFromTail(path)) {
|
|
1716
|
-
lastSequence = Math.max(lastSequence, record.sequence);
|
|
1717
|
-
if (record.sequence < nextSequence) {
|
|
1718
|
-
reachedPrefix = true;
|
|
1719
|
-
break;
|
|
1720
|
-
}
|
|
1721
|
-
if (isNativeUserTurn(record))
|
|
1722
|
-
count++;
|
|
1723
|
-
}
|
|
1724
|
-
if (reachedPrefix)
|
|
1725
|
-
break;
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
if (indexed === undefined || lastSequence < nextSequence - 1) {
|
|
1729
|
-
count = 0;
|
|
1730
|
-
lastSequence = 0;
|
|
1731
|
-
for (const path of paths) {
|
|
1732
|
-
for await (const record of readRecordsFromHead(path)) {
|
|
1733
|
-
lastSequence = record.sequence;
|
|
1734
|
-
if (isNativeUserTurn(record))
|
|
1735
|
-
count++;
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
await this.#refreshIndex();
|
|
1740
|
-
const current = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1741
|
-
if (!current)
|
|
1742
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1743
|
-
if (current.storageTransactionId !== transactionId) {
|
|
1744
|
-
// The indexed prefix no longer belongs to this history. Fall back to the
|
|
1745
|
-
// ordinary physical read boundary without caching or a conflict error.
|
|
1746
|
-
count = 0;
|
|
1747
|
-
for (const path of await this.#segmentPaths(sessionId))
|
|
1748
|
-
for await (const record of readRecordsFromHead(path))
|
|
1749
|
-
if (isNativeUserTurn(record))
|
|
1750
|
-
count++;
|
|
1751
|
-
return count;
|
|
1752
|
-
}
|
|
1753
|
-
if (current.nextSequence === nextSequence &&
|
|
1754
|
-
lastSequence === nextSequence - 1 &&
|
|
1755
|
-
current.resume?.userTurns === undefined) {
|
|
1756
|
-
(current.resume ??= { version: 1, todoRevision: 0 }).userTurns = count;
|
|
1757
|
-
this.#markIndexDirty(sessionId, true);
|
|
1758
|
-
}
|
|
1759
|
-
return count;
|
|
1760
|
-
});
|
|
1761
|
-
}
|
|
1762
|
-
async firstUserMessagePreview(sessionId) {
|
|
1763
|
-
this.#assertOpen();
|
|
1764
|
-
await this.#refreshIndex();
|
|
1765
|
-
await this.#ensureFirstUserMessageIndexed(sessionId);
|
|
1766
|
-
return this.#shared.index.sessions.find((session) => session.id === sessionId)?.resume
|
|
1767
|
-
?.firstUserMessagePreview;
|
|
1768
|
-
}
|
|
1769
|
-
async #ensureFirstUserMessageIndexed(sessionId) {
|
|
1770
|
-
for (;;) {
|
|
1771
|
-
const before = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1772
|
-
if (before === undefined)
|
|
1773
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1774
|
-
if (before.resume?.firstUserMessageIndexed === true)
|
|
1775
|
-
return;
|
|
1776
|
-
const nextSequence = before.nextSequence;
|
|
1777
|
-
const transactionId = before.storageTransactionId;
|
|
1778
|
-
let preview;
|
|
1779
|
-
try {
|
|
1780
|
-
for (const path of await this.#segmentPaths(sessionId)) {
|
|
1781
|
-
for await (const record of readRecordsFromHead(path)) {
|
|
1782
|
-
preview = userMessagePreviewFromRecord(record);
|
|
1783
|
-
if (preview !== undefined)
|
|
1784
|
-
break;
|
|
1785
|
-
}
|
|
1786
|
-
if (preview !== undefined)
|
|
1787
|
-
break;
|
|
1788
|
-
}
|
|
1789
|
-
}
|
|
1790
|
-
catch (error) {
|
|
1791
|
-
if (error.code !== 'ENOENT' &&
|
|
1792
|
-
!(error instanceof MarAgentError && error.code === 'MAR_AGENT_SESSION_NOT_FOUND'))
|
|
1793
|
-
throw error;
|
|
1794
|
-
}
|
|
1795
|
-
await this.#refreshIndex();
|
|
1796
|
-
const current = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1797
|
-
if (current === undefined)
|
|
1798
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1799
|
-
if (current.resume?.firstUserMessageIndexed === true)
|
|
1800
|
-
return;
|
|
1801
|
-
if (current.nextSequence !== nextSequence || current.storageTransactionId !== transactionId)
|
|
1802
|
-
continue;
|
|
1803
|
-
const resume = (current.resume ??= { version: 1, todoRevision: 0 });
|
|
1804
|
-
resume.firstUserMessageIndexed = true;
|
|
1805
|
-
if (preview !== undefined)
|
|
1806
|
-
resume.firstUserMessagePreview = preview;
|
|
1807
|
-
// Derived preview caches do not change list membership, ordering or filters.
|
|
1808
|
-
this.#markIndexDirty(sessionId, true);
|
|
1809
|
-
return;
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
async #ensureExecutionModelIndexed(sessionId) {
|
|
1813
|
-
const pending = this.#shared.executionModelReads.get(sessionId);
|
|
1814
|
-
if (pending !== undefined)
|
|
1815
|
-
return pending;
|
|
1816
|
-
const read = this.#backfillExecutionModel(sessionId).finally(() => {
|
|
1817
|
-
if (this.#shared.executionModelReads.get(sessionId) === read)
|
|
1818
|
-
this.#shared.executionModelReads.delete(sessionId);
|
|
1819
|
-
});
|
|
1820
|
-
this.#shared.executionModelReads.set(sessionId, read);
|
|
1821
|
-
return read;
|
|
1822
|
-
}
|
|
1823
|
-
async #backfillExecutionModel(sessionId) {
|
|
1824
|
-
for (;;) {
|
|
1825
|
-
const before = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1826
|
-
if (before === undefined)
|
|
1827
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1828
|
-
if (before.resume?.executionModelIndexed === true)
|
|
1829
|
-
return;
|
|
1830
|
-
const nextSequence = before.nextSequence;
|
|
1831
|
-
const transactionId = before.storageTransactionId;
|
|
1832
|
-
const model = await this.#readLatestExecutionModel(sessionId);
|
|
1833
|
-
// Refresh can replace metadata objects while unrelated sessions are being read.
|
|
1834
|
-
// Merge only into the current entry, and retry if the authoritative history changed.
|
|
1835
|
-
await this.#refreshIndex();
|
|
1836
|
-
const current = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1837
|
-
if (current === undefined)
|
|
1838
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1839
|
-
if (current.resume?.executionModelIndexed === true)
|
|
1840
|
-
return;
|
|
1841
|
-
if (current.nextSequence !== nextSequence || current.storageTransactionId !== transactionId)
|
|
1842
|
-
continue;
|
|
1843
|
-
// A missing resume index stays incomplete (no compact boundary); execution
|
|
1844
|
-
// recovery will rebuild it separately instead of doing so during discovery.
|
|
1845
|
-
const resume = (current.resume ??= { version: 1, todoRevision: 0 });
|
|
1846
|
-
resume.executionModelIndexed = true;
|
|
1847
|
-
delete resume.modelId;
|
|
1848
|
-
delete resume.modelName;
|
|
1849
|
-
delete resume.reasoningEffort;
|
|
1850
|
-
if (model !== undefined)
|
|
1851
|
-
Object.assign(resume, model);
|
|
1852
|
-
this.#shared.index.revision++;
|
|
1853
|
-
this.#markIndexDirty(sessionId);
|
|
1854
|
-
return;
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
|
-
async #readLatestExecutionModel(sessionId) {
|
|
1858
|
-
try {
|
|
1859
|
-
for (const path of (await this.#segmentPaths(sessionId)).reverse())
|
|
1860
|
-
for await (const record of readRecordsFromTail(path)) {
|
|
1861
|
-
const model = executionModelFromRecord(record);
|
|
1862
|
-
if (model !== undefined)
|
|
1863
|
-
return model;
|
|
1864
|
-
}
|
|
1865
|
-
return undefined;
|
|
1866
|
-
}
|
|
1867
|
-
catch (error) {
|
|
1868
|
-
if (error.code === 'ENOENT' ||
|
|
1869
|
-
(error instanceof MarAgentError && error.code === 'MAR_AGENT_SESSION_NOT_FOUND'))
|
|
1870
|
-
return undefined;
|
|
1871
|
-
throw error;
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
async delete(sessionId, maintenance) {
|
|
1875
|
-
this.#assertOpen();
|
|
1876
|
-
const holding = maintenance ?? (await this.acquireSessionMaintenance(sessionId, 'delete'));
|
|
1877
|
-
this.#assertMaintenance(sessionId, 'delete', holding);
|
|
1878
|
-
const tree = holding.sessionIds;
|
|
1879
|
-
let failed = false;
|
|
1880
|
-
try {
|
|
1881
|
-
const deleted = await sessionDirectoryExists(join(this.#shared.root, 'sessions', sessionId));
|
|
1882
|
-
for (const id of tree.toReversed()) {
|
|
1883
|
-
await rm(join(this.#shared.root, 'sessions', id), { recursive: true, force: true });
|
|
1884
|
-
this.#shared.segments.delete(id);
|
|
1885
|
-
}
|
|
1886
|
-
const deletedSet = new Set(tree);
|
|
1887
|
-
this.#shared.index.sessions = this.#shared.index.sessions.filter((session) => !deletedSet.has(session.id));
|
|
1888
|
-
this.#shared.index.revision++;
|
|
1889
|
-
await this.#saveIndex(tree);
|
|
1890
|
-
return { deleted };
|
|
1891
|
-
}
|
|
1892
|
-
catch (error) {
|
|
1893
|
-
failed = true;
|
|
1894
|
-
throw error;
|
|
1895
|
-
}
|
|
1896
|
-
finally {
|
|
1897
|
-
if (!maintenance) {
|
|
1898
|
-
const releasing = holding.release();
|
|
1899
|
-
if (failed)
|
|
1900
|
-
await releasing.catch(() => undefined);
|
|
1901
|
-
else
|
|
1902
|
-
await releasing;
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
#assertOpen() {
|
|
1907
|
-
if (this.#leaseClosed || this.#shared.closed)
|
|
1908
|
-
throw new MarAgentError('MAR_AGENT_DISPOSED', 'Session store is closed.');
|
|
1909
|
-
}
|
|
1910
|
-
#markIndexDirty(sessionId, derivedOnly = false) {
|
|
1911
|
-
if (!derivedOnly)
|
|
1912
|
-
this.#shared.derivedOnlyDirtySessions.delete(sessionId);
|
|
1913
|
-
else if (!this.#shared.dirtySessionVersions.has(sessionId))
|
|
1914
|
-
this.#shared.derivedOnlyDirtySessions.add(sessionId);
|
|
1915
|
-
this.#shared.indexDirty = true;
|
|
1916
|
-
this.#shared.dirtySessionVersions.set(sessionId, (this.#shared.dirtySessionVersions.get(sessionId) ?? 0) + 1);
|
|
1917
|
-
}
|
|
1918
|
-
async #storageTransactionCommitted(sessionId, transactionId) {
|
|
1919
|
-
const disk = await readSessionIndex(join(this.#shared.root, 'sessions', 'index.json'));
|
|
1920
|
-
return (disk?.sessions.some((session) => session.id === sessionId && session.storageTransactionId === transactionId) ?? false);
|
|
1921
|
-
}
|
|
1922
|
-
#segmentPath(sessionId, segment) {
|
|
1923
|
-
return join(this.#shared.root, 'sessions', sessionId, `events-${String(segment).padStart(6, '0')}.jsonl`);
|
|
1924
|
-
}
|
|
1925
|
-
async #segmentPaths(sessionId) {
|
|
1926
|
-
const directory = join(this.#shared.root, 'sessions', sessionId);
|
|
1927
|
-
const names = (await readdir(directory))
|
|
1928
|
-
.filter((name) => /^events-\d{6}\.jsonl$/.test(name))
|
|
1929
|
-
.sort();
|
|
1930
|
-
if (names.length === 0)
|
|
1931
|
-
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1932
|
-
return names.map((name) => join(directory, name));
|
|
1933
|
-
}
|
|
1934
|
-
async #currentSegment(sessionId) {
|
|
1935
|
-
const cached = this.#shared.segments.get(sessionId);
|
|
1936
|
-
if (cached !== undefined)
|
|
1937
|
-
return cached;
|
|
1938
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
1939
|
-
const indexed = meta?.segments?.at(-1)?.number;
|
|
1940
|
-
if (indexed !== undefined) {
|
|
1941
|
-
this.#shared.segments.set(sessionId, indexed);
|
|
1942
|
-
return indexed;
|
|
1943
|
-
}
|
|
1944
|
-
const paths = await this.#segmentPaths(sessionId);
|
|
1945
|
-
const match = /events-(\d{6})\.jsonl$/.exec(paths.at(-1));
|
|
1946
|
-
const segment = Number(match?.[1] ?? 1);
|
|
1947
|
-
this.#shared.segments.set(sessionId, segment);
|
|
1948
|
-
return segment;
|
|
1949
|
-
}
|
|
1950
|
-
/** Repair the last segment and pending images under the lease, then sync sequence metadata. */
|
|
1951
|
-
async #reconcileTail(sessionId) {
|
|
1952
|
-
const paths = await this.#segmentPaths(sessionId);
|
|
1953
|
-
const latestPath = paths.at(-1);
|
|
1954
|
-
await repairSegmentTail(latestPath);
|
|
1955
|
-
const records = await readRecords(latestPath);
|
|
1956
|
-
await recoverResources(join(this.#sessionDirectory(sessionId), 'resources'), () => this.#readRecords(sessionId));
|
|
1957
|
-
await cleanupUnreferencedToolResources(this.#sessionDirectory(sessionId), () => this.#readRecords(sessionId));
|
|
1958
|
-
const last = records.at(-1);
|
|
1959
|
-
const match = /events-(\d{6})\.jsonl$/.exec(latestPath);
|
|
1960
|
-
const segment = Number(match?.[1] ?? 1);
|
|
1961
|
-
this.#shared.segments.set(sessionId, segment);
|
|
1962
|
-
if (last === undefined)
|
|
1963
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session history is corrupt.');
|
|
1964
|
-
const bytes = (await stat(latestPath)).size;
|
|
1965
|
-
const firstSequence = records[0].sequence;
|
|
1966
|
-
// Tail recovery must publish onto metadata that is current after all file I/O.
|
|
1967
|
-
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
1968
|
-
const known = meta.segments?.find((item) => item.number === segment);
|
|
1969
|
-
const titleRecord = records.findLast((record) => record.type === 'session.title.updated' &&
|
|
1970
|
-
typeof record.payload.title === 'string');
|
|
1971
|
-
const recoveredTitle = titleRecord?.payload?.title;
|
|
1972
|
-
const changed = meta.nextSequence !== last.sequence + 1 ||
|
|
1973
|
-
known?.bytes !== bytes ||
|
|
1974
|
-
(recoveredTitle !== undefined && meta.title !== recoveredTitle);
|
|
1975
|
-
meta.nextSequence = last.sequence + 1;
|
|
1976
|
-
if (recoveredTitle !== undefined)
|
|
1977
|
-
meta.title = recoveredTitle;
|
|
1978
|
-
meta.lastActivityAt =
|
|
1979
|
-
meta.lastActivityAt < last.timestamp ? last.timestamp : meta.lastActivityAt;
|
|
1980
|
-
if (!meta.segments)
|
|
1981
|
-
meta.segments = [];
|
|
1982
|
-
if (known) {
|
|
1983
|
-
known.firstSequence = firstSequence;
|
|
1984
|
-
known.lastSequence = last.sequence;
|
|
1985
|
-
known.bytes = bytes;
|
|
1986
|
-
}
|
|
1987
|
-
else
|
|
1988
|
-
meta.segments.push({ number: segment, firstSequence, lastSequence: last.sequence, bytes });
|
|
1989
|
-
if (changed) {
|
|
1990
|
-
delete meta.automaticTitlePending;
|
|
1991
|
-
delete meta.resume;
|
|
1992
|
-
this.#shared.index.revision++;
|
|
1993
|
-
this.#markIndexDirty(sessionId);
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
async close() {
|
|
1997
|
-
if (this.#leaseClosed)
|
|
1998
|
-
return;
|
|
1999
|
-
this.#leaseClosed = true;
|
|
2000
|
-
this.#shared.references--;
|
|
2001
|
-
if (this.#shared.references > 0)
|
|
2002
|
-
return;
|
|
2003
|
-
if (this.#shared.indexDirty)
|
|
2004
|
-
await this.#saveIndex();
|
|
2005
|
-
this.#shared.closed = true;
|
|
2006
|
-
JsonlSessionStore.#instances.delete(this.#shared.root);
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
|
-
async function sessionDirectoryExists(path) {
|
|
2010
|
-
try {
|
|
2011
|
-
await lstat(path);
|
|
2012
|
-
return true;
|
|
2013
|
-
}
|
|
2014
|
-
catch (error) {
|
|
2015
|
-
if (error.code === 'ENOENT')
|
|
2016
|
-
return false;
|
|
2017
|
-
throw error;
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2020
|
-
async function recoverContextualWorldStateTransactions(root, meta) {
|
|
2021
|
-
const directory = join(root, 'sessions', meta.id);
|
|
2022
|
-
let names;
|
|
2023
|
-
try {
|
|
2024
|
-
names = await readdir(directory);
|
|
2025
|
-
}
|
|
2026
|
-
catch (error) {
|
|
2027
|
-
if (error.code === 'ENOENT')
|
|
2028
|
-
return;
|
|
2029
|
-
throw error;
|
|
2030
|
-
}
|
|
2031
|
-
const pattern = /^context-world-state\.json\.(\d+)\.[0-9a-f-]+\.(?:rollback|clear)-backup$/u;
|
|
2032
|
-
const backups = names.filter((name) => pattern.test(name));
|
|
2033
|
-
if (backups.length === 0)
|
|
2034
|
-
return;
|
|
2035
|
-
const eventNames = names.filter((name) => /^events-\d{6}\.jsonl$/u.test(name)).sort();
|
|
2036
|
-
const latestEventName = eventNames.at(-1);
|
|
2037
|
-
if (latestEventName === undefined)
|
|
2038
|
-
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session history is corrupt.');
|
|
2039
|
-
const latestRecords = await readRecords(join(directory, latestEventName));
|
|
2040
|
-
const actualNextSequence = (latestRecords.at(-1)?.sequence ?? 0) + 1;
|
|
2041
|
-
const canonical = join(directory, CONTEXTUAL_WORLD_STATE_FILE);
|
|
2042
|
-
for (const name of backups) {
|
|
2043
|
-
const match = pattern.exec(name);
|
|
2044
|
-
const backup = join(directory, name);
|
|
2045
|
-
const targetNextSequence = Number(match[1]);
|
|
2046
|
-
if (actualNextSequence === targetNextSequence || (await sessionDirectoryExists(canonical)))
|
|
2047
|
-
await rm(backup, { force: true });
|
|
2048
|
-
else
|
|
2049
|
-
await rename(backup, canonical);
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
function restoreSessionMeta(meta, original) {
|
|
2053
|
-
meta.id = original.id;
|
|
2054
|
-
meta.title = original.title;
|
|
2055
|
-
meta.workspace = original.workspace;
|
|
2056
|
-
meta.createdAt = original.createdAt;
|
|
2057
|
-
meta.lastActivityAt = original.lastActivityAt;
|
|
2058
|
-
if (original.source === undefined)
|
|
2059
|
-
delete meta.source;
|
|
2060
|
-
else
|
|
2061
|
-
meta.source = structuredClone(original.source);
|
|
2062
|
-
meta.nextSequence = original.nextSequence;
|
|
2063
|
-
if (original.automaticTitlePending === undefined)
|
|
2064
|
-
delete meta.automaticTitlePending;
|
|
2065
|
-
else
|
|
2066
|
-
meta.automaticTitlePending = original.automaticTitlePending;
|
|
2067
|
-
if (original.resume === undefined)
|
|
2068
|
-
delete meta.resume;
|
|
2069
|
-
else
|
|
2070
|
-
meta.resume = structuredClone(original.resume);
|
|
2071
|
-
if (original.segments === undefined)
|
|
2072
|
-
delete meta.segments;
|
|
2073
|
-
else
|
|
2074
|
-
meta.segments = structuredClone(original.segments);
|
|
2075
|
-
}
|
|
2076
|
-
function isObject(value) {
|
|
2077
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
2078
|
-
}
|