@gajae-code/coding-agent 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -2
- package/dist/types/append-only-mode.d.ts +1 -1
- package/dist/types/cli/args.d.ts +4 -5
- package/dist/types/cli/daemon-cli.d.ts +12 -1
- package/dist/types/cli/notify-cli.d.ts +21 -1
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/daemon.d.ts +39 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/commands/notify.d.ts +28 -1
- package/dist/types/commands/sdk.d.ts +33 -0
- package/dist/types/config/atomic-yaml-patch.d.ts +85 -0
- package/dist/types/config/model-profile-activation.d.ts +35 -15
- package/dist/types/config/model-profiles.d.ts +11 -5
- package/dist/types/config/model-registry.d.ts +3 -3
- package/dist/types/config/model-resolver.d.ts +39 -6
- package/dist/types/config/model-selector-value.d.ts +17 -0
- package/dist/types/config/models-config-schema.d.ts +10 -6
- package/dist/types/config/settings-schema.d.ts +133 -6
- package/dist/types/config/settings.d.ts +63 -35
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -6
- package/dist/types/coordinator-mcp/server.d.ts +6 -49
- package/dist/types/daemon/builtin.d.ts +6 -5
- package/dist/types/daemon/control-types.d.ts +1 -1
- package/dist/types/discovery/mcp-json.d.ts +12 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
- package/dist/types/extensibility/extensions/compact-handler.d.ts +4 -2
- package/dist/types/extensibility/extensions/get-commands-handler.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +10 -1
- package/dist/types/extensibility/extensions/runner.d.ts +3 -3
- package/dist/types/extensibility/extensions/types.d.ts +141 -16
- package/dist/types/extensibility/hooks/types.d.ts +1 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -2
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +3 -4
- package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +13 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -1
- package/dist/types/gjc-runtime/windows-powershell-command.d.ts +9 -0
- package/dist/types/harness-control-plane/operate.d.ts +5 -5
- package/dist/types/harness-control-plane/owner.d.ts +18 -4
- package/dist/types/harness-control-plane/receipts.d.ts +0 -1
- package/dist/types/harness-control-plane/sdk-transport.d.ts +43 -0
- package/dist/types/harness-control-plane/session-transport.d.ts +48 -0
- package/dist/types/harness-control-plane/types.d.ts +3 -4
- package/dist/types/lsp/index.d.ts +1 -1
- package/dist/types/main.d.ts +32 -38
- package/dist/types/modes/acp/acp-agent.d.ts +224 -53
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -0
- package/dist/types/modes/acp/acp-mode.d.ts +6 -15
- package/dist/types/modes/acp/permission-mode.d.ts +4 -0
- package/dist/types/modes/acp/startup-options.d.ts +6 -0
- package/dist/types/modes/components/agent-dashboard.d.ts +3 -0
- package/dist/types/modes/components/model-selector.d.ts +1 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -4
- package/dist/types/modes/components/welcome.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +35 -1
- package/dist/types/modes/index.d.ts +0 -4
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/runtime-init.d.ts +2 -2
- package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +6 -9
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -6
- package/dist/types/modes/shared/agent-wire/event-contract.d.ts +6 -6
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +4 -9
- package/dist/types/modes/shared/agent-wire/event-observation.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +169 -30
- package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +7 -7
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +105 -0
- package/dist/types/modes/types.d.ts +5 -0
- package/dist/types/runtime-mcp/client.d.ts +2 -0
- package/dist/types/runtime-mcp/config.d.ts +5 -1
- package/dist/types/runtime-mcp/manager.d.ts +8 -1
- package/dist/types/runtime-mcp/types.d.ts +4 -0
- package/dist/types/sdk/acp/adapter.d.ts +67 -0
- package/dist/types/sdk/acp/index.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +53 -0
- package/dist/types/sdk/broker/discovery.d.ts +30 -0
- package/dist/types/sdk/broker/ensure.d.ts +33 -0
- package/dist/types/sdk/broker/identity.d.ts +3 -0
- package/dist/types/sdk/broker/index.d.ts +8 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +95 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +85 -0
- package/dist/types/sdk/broker/process-incarnation.d.ts +14 -0
- package/dist/types/sdk/broker/runtime.d.ts +33 -0
- package/dist/types/sdk/broker/session-index.d.ts +57 -0
- package/dist/types/sdk/broker/state-version.d.ts +8 -0
- package/dist/types/sdk/broker/transport.d.ts +9 -0
- package/dist/types/sdk/bus/chat-command-policy.d.ts +49 -0
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +21 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +111 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +77 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +84 -0
- package/dist/types/{notifications → sdk/bus}/config-commands.d.ts +7 -2
- package/dist/types/sdk/bus/config.d.ts +274 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +72 -0
- package/dist/types/sdk/bus/discord-conversation.d.ts +58 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +45 -0
- package/dist/types/sdk/bus/discord-live-provider.d.ts +70 -0
- package/dist/types/sdk/bus/discord-provider.d.ts +85 -0
- package/dist/types/{notifications → sdk/bus}/html-format.d.ts +2 -2
- package/dist/types/{notifications → sdk/bus}/index.d.ts +113 -13
- package/dist/types/{notifications → sdk/bus}/lifecycle-control-runtime.d.ts +4 -2
- package/dist/types/{notifications → sdk/bus}/lifecycle-orchestrator.d.ts +18 -21
- package/dist/types/{notifications → sdk/bus}/managed-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/native-runtime-compatibility.d.ts +13 -0
- package/dist/types/sdk/bus/notification-orchestration.d.ts +170 -0
- package/dist/types/{notifications → sdk/bus}/notification-service.d.ts +7 -1
- package/dist/types/sdk/bus/session-control.d.ts +83 -0
- package/dist/types/sdk/bus/slack-conversation.d.ts +56 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +60 -0
- package/dist/types/sdk/bus/slack-live-provider.d.ts +59 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +55 -0
- package/dist/types/{notifications → sdk/bus}/telegram-cli.d.ts +4 -4
- package/dist/types/{notifications → sdk/bus}/telegram-daemon-cli.d.ts +14 -5
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -0
- package/dist/types/{notifications → sdk/bus}/telegram-daemon-control.d.ts +2 -2
- package/dist/types/{notifications → sdk/bus}/telegram-daemon.d.ts +55 -17
- package/dist/types/{notifications → sdk/bus}/telegram-reference.d.ts +2 -2
- package/dist/types/sdk/bus/telegram-setup.d.ts +128 -0
- package/dist/types/{notifications → sdk/bus}/topic-registry.d.ts +11 -0
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/session-cli.d.ts +19 -0
- package/dist/types/sdk/client/client.d.ts +1 -0
- package/dist/types/sdk/client/discovery.d.ts +27 -0
- package/dist/types/sdk/client/index.d.ts +2 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +34 -0
- package/dist/types/sdk/host/control/index.d.ts +2 -0
- package/dist/types/sdk/host/control/operations.d.ts +65 -0
- package/dist/types/sdk/host/events.d.ts +35 -0
- package/dist/types/sdk/host/host.d.ts +35 -0
- package/dist/types/sdk/host/index.d.ts +4 -0
- package/dist/types/sdk/host/query/cursor.d.ts +62 -0
- package/dist/types/sdk/host/query/handlers.d.ts +70 -0
- package/dist/types/sdk/host/query/index.d.ts +3 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +60 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +45 -0
- package/dist/types/sdk/host/types.d.ts +32 -0
- package/dist/types/sdk/index.d.ts +6 -0
- package/dist/types/sdk/lifecycle-session.d.ts +14 -0
- package/dist/types/sdk/mcp/index.d.ts +1 -0
- package/dist/types/sdk/mcp/server.d.ts +37 -0
- package/dist/types/sdk/models.d.ts +44 -0
- package/dist/types/sdk/protocol/adapter-validation.d.ts +14 -0
- package/dist/types/sdk/protocol/operation-registry.d.ts +24 -0
- package/dist/types/{sdk.d.ts → sdk/session.d.ts} +44 -38
- package/dist/types/sdk/startup-capability.d.ts +50 -0
- package/dist/types/session/agent-session.d.ts +114 -21
- package/dist/types/session/default-model-selection.d.ts +25 -0
- package/dist/types/session/fallback-chain-controller.d.ts +52 -0
- package/dist/types/session/session-manager.d.ts +58 -1
- package/dist/types/setup/credential-auto-import.d.ts +36 -6
- package/dist/types/tools/ask-answer-registry.d.ts +1 -1
- package/dist/types/tools/browser/benchmark/detector-report.d.ts +63 -0
- package/dist/types/tools/browser/benchmark/run-suite.d.ts +18 -0
- package/dist/types/tools/browser/launch.d.ts +22 -0
- package/dist/types/tools/browser/profile-discovery.d.ts +31 -0
- package/dist/types/tools/browser/profile-posture.d.ts +37 -0
- package/dist/types/tools/browser/profile-reuse.d.ts +39 -0
- package/dist/types/tools/browser/profile-warmup.d.ts +39 -0
- package/dist/types/tools/browser/registry.d.ts +19 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/index.d.ts +8 -8
- package/package.json +34 -13
- package/scripts/build-sdk-package-smoke.ts +94 -0
- package/scripts/capture-notifications-settings-showcase.ts +435 -0
- package/scripts/compile-args.ts +4 -2
- package/scripts/g005-daemon-smoke.ts +4 -5
- package/scripts/g011-daemon-path-smoke.ts +5 -5
- package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
- package/scripts/generate-sdk-operation-inventory.ts +867 -0
- package/scripts/generate-telegram-baseline-manifest.ts +128 -0
- package/scripts/manifest-schema.ts +124 -0
- package/scripts/run-test-manifest.ts +153 -0
- package/scripts/run-threat-model-check.ts +157 -0
- package/scripts/verify-gjc-sdk-canonicalization.ts +3467 -0
- package/scripts/verify-gjc-sdk-rename.ts +90 -0
- package/src/append-only-mode.ts +1 -1
- package/src/cli/args.ts +43 -12
- package/src/cli/auth-gateway-cli.ts +2 -2
- package/src/cli/config-cli.ts +2 -1
- package/src/cli/daemon-cli.ts +63 -14
- package/src/cli/list-models.ts +1 -1
- package/src/cli/notify-cli.ts +288 -286
- package/src/cli.ts +21 -2
- package/src/commands/daemon.ts +50 -6
- package/src/commands/harness.ts +93 -36
- package/src/commands/launch.ts +5 -2
- package/src/commands/mcp-serve.ts +19 -12
- package/src/commands/notify.ts +36 -3
- package/src/commands/sdk.ts +426 -0
- package/src/commit/agentic/agent.ts +1 -0
- package/src/config/atomic-yaml-patch.ts +423 -0
- package/src/config/model-profile-activation.ts +240 -89
- package/src/config/model-profiles.ts +18 -15
- package/src/config/model-registry.ts +51 -26
- package/src/config/model-resolver.ts +138 -51
- package/src/config/model-selector-value.ts +50 -0
- package/src/config/models-config-schema.ts +14 -21
- package/src/config/settings-schema.ts +128 -10
- package/src/config/settings.ts +1120 -158
- package/src/coordinator-mcp/model-preset.ts +74 -20
- package/src/coordinator-mcp/server.ts +1597 -3052
- package/src/daemon/builtin.ts +8 -5
- package/src/daemon/control-types.ts +1 -1
- package/src/defaults/gjc/skills/team/SKILL.md +4 -4
- package/src/discovery/mcp-json.ts +327 -20
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/compact-handler.ts +3 -3
- package/src/extensibility/extensions/get-commands-handler.ts +2 -2
- package/src/extensibility/extensions/loader.ts +69 -5
- package/src/extensibility/extensions/runner.ts +112 -4
- package/src/extensibility/extensions/types.ts +176 -17
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/gjc-runtime/launch-tmux.ts +170 -86
- package/src/gjc-runtime/psmux-detect.ts +82 -4
- package/src/gjc-runtime/session-state-sidecar.ts +41 -31
- package/src/gjc-runtime/team-runtime.ts +54 -35
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +53 -17
- package/src/gjc-runtime/tmux-owner-isolation.ts +1 -1
- package/src/gjc-runtime/tmux-sessions.ts +19 -13
- package/src/gjc-runtime/ultragoal-guard.ts +53 -6
- package/src/gjc-runtime/ultragoal-runtime.ts +155 -3
- package/src/gjc-runtime/windows-powershell-command.ts +58 -0
- package/src/harness-control-plane/operate.ts +9 -9
- package/src/harness-control-plane/owner.ts +283 -39
- package/src/harness-control-plane/receipts.ts +0 -1
- package/src/harness-control-plane/sdk-transport.ts +395 -0
- package/src/harness-control-plane/session-transport.ts +94 -0
- package/src/harness-control-plane/types.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +34 -35
- package/src/lsp/client.ts +42 -6
- package/src/lsp/index.ts +2 -2
- package/src/main.ts +299 -204
- package/src/modes/DESIGN.md +258 -0
- package/src/modes/acp/acp-agent.ts +1228 -2612
- package/src/modes/acp/acp-client-bridge.ts +2 -20
- package/src/modes/acp/acp-event-mapper.ts +18 -2
- package/src/modes/acp/acp-mode.ts +8 -36
- package/src/modes/acp/permission-mode.ts +25 -0
- package/src/modes/acp/startup-options.ts +6 -0
- package/src/modes/components/agent-dashboard.ts +37 -17
- package/src/modes/components/model-selector.ts +93 -35
- package/src/modes/components/notifications-settings-editor.ts +1379 -0
- package/src/modes/components/settings-selector.ts +60 -11
- package/src/modes/components/welcome.ts +4 -4
- package/src/modes/controllers/command-controller.ts +1 -2
- package/src/modes/controllers/event-controller.ts +11 -16
- package/src/modes/controllers/extension-ui-controller.ts +348 -4
- package/src/modes/controllers/selector-controller.ts +674 -36
- package/src/modes/index.ts +0 -22
- package/src/modes/interactive-mode.ts +59 -20
- package/src/modes/runtime-init.ts +298 -6
- package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +10 -11
- package/src/modes/shared/agent-wire/event-contract.ts +7 -8
- package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
- package/src/modes/shared/agent-wire/event-observation.ts +16 -7
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1254 -87
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +15 -15
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +113 -0
- package/src/modes/types.ts +5 -0
- package/src/runtime-mcp/client.ts +94 -19
- package/src/runtime-mcp/config.ts +27 -15
- package/src/runtime-mcp/manager.ts +343 -105
- package/src/runtime-mcp/oauth-flow.ts +42 -12
- package/src/runtime-mcp/transports/http.ts +49 -19
- package/src/runtime-mcp/transports/stdio.ts +47 -28
- package/src/runtime-mcp/types.ts +10 -0
- package/src/sdk/acp/adapter.ts +418 -0
- package/src/sdk/acp/index.ts +2 -0
- package/src/sdk/broker/broker.ts +642 -0
- package/src/sdk/broker/discovery.ts +94 -0
- package/src/sdk/broker/ensure.ts +249 -0
- package/src/sdk/broker/identity.ts +42 -0
- package/src/sdk/broker/index.ts +8 -0
- package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
- package/src/sdk/broker/internal-source.bunfig.toml +1 -0
- package/src/sdk/broker/lifecycle-ledger.ts +286 -0
- package/src/sdk/broker/lifecycle.ts +2157 -0
- package/src/sdk/broker/process-incarnation.ts +159 -0
- package/src/sdk/broker/runtime.ts +164 -0
- package/src/sdk/broker/session-index.ts +318 -0
- package/src/sdk/broker/state-version.ts +23 -0
- package/src/sdk/broker/transport.ts +125 -0
- package/src/sdk/bus/chat-command-policy.ts +107 -0
- package/src/sdk/bus/chat-daemon-cli.ts +214 -0
- package/src/sdk/bus/chat-daemon-control.ts +713 -0
- package/src/sdk/bus/chat-daemon-runtime.ts +526 -0
- package/src/sdk/bus/chat-effect-journal.ts +353 -0
- package/src/{notifications → sdk/bus}/config-commands.ts +39 -10
- package/src/sdk/bus/config.ts +433 -0
- package/src/sdk/bus/conversation-store.ts +391 -0
- package/src/sdk/bus/discord-conversation.ts +85 -0
- package/src/sdk/bus/discord-daemon.ts +2275 -0
- package/src/sdk/bus/discord-live-provider.ts +521 -0
- package/src/sdk/bus/discord-provider.ts +56 -0
- package/src/{notifications → sdk/bus}/html-format.ts +2 -2
- package/src/sdk/bus/index.ts +3943 -0
- package/src/{notifications → sdk/bus}/lifecycle-commands.ts +2 -0
- package/src/{notifications → sdk/bus}/lifecycle-control-runtime.ts +225 -196
- package/src/{notifications → sdk/bus}/lifecycle-orchestrator.ts +161 -74
- package/src/{notifications → sdk/bus}/managed-daemon.ts +2 -2
- package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
- package/src/sdk/bus/natives-augment.d.ts +38 -0
- package/src/sdk/bus/notification-orchestration.ts +535 -0
- package/src/{notifications → sdk/bus}/notification-service.ts +106 -12
- package/src/sdk/bus/session-control.ts +333 -0
- package/src/sdk/bus/slack-conversation.ts +74 -0
- package/src/sdk/bus/slack-daemon.ts +2144 -0
- package/src/sdk/bus/slack-live-provider.ts +380 -0
- package/src/sdk/bus/slack-provider.ts +76 -0
- package/src/{notifications → sdk/bus}/telegram-cli.ts +8 -8
- package/src/sdk/bus/telegram-daemon-cli.ts +330 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +14 -0
- package/src/{notifications → sdk/bus}/telegram-daemon-control.ts +4 -4
- package/src/{notifications → sdk/bus}/telegram-daemon.ts +502 -54
- package/src/{notifications → sdk/bus}/telegram-reference.ts +2 -2
- package/src/sdk/bus/telegram-setup.ts +663 -0
- package/src/{notifications → sdk/bus}/topic-registry.ts +32 -0
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/session-cli.ts +275 -0
- package/src/sdk/client/client.ts +1 -0
- package/src/sdk/client/discovery.ts +108 -0
- package/src/sdk/client/index.ts +2 -0
- package/src/sdk/host/control/dispatch.ts +367 -0
- package/src/sdk/host/control/index.ts +10 -0
- package/src/sdk/host/control/operations.ts +63 -0
- package/src/sdk/host/events.ts +91 -0
- package/src/sdk/host/host.ts +296 -0
- package/src/sdk/host/index.ts +4 -0
- package/src/sdk/host/query/cursor.ts +189 -0
- package/src/sdk/host/query/handlers.ts +533 -0
- package/src/sdk/host/query/index.ts +3 -0
- package/src/sdk/host/query/revision-store.ts +1126 -0
- package/src/sdk/host/reverse-leases.ts +325 -0
- package/src/sdk/host/types.ts +29 -0
- package/src/sdk/index.ts +13 -0
- package/src/sdk/lifecycle-session.ts +39 -0
- package/src/sdk/mcp/index.ts +1 -0
- package/src/sdk/mcp/server.ts +332 -0
- package/src/sdk/models.ts +168 -0
- package/src/sdk/protocol/adapter-validation.ts +48 -0
- package/src/sdk/protocol/operation-inventory.generated.json +5654 -0
- package/src/sdk/protocol/operation-registry.ts +312 -0
- package/src/{sdk.ts → sdk/session.ts} +339 -212
- package/src/sdk/startup-capability.ts +189 -0
- package/src/session/agent-session.ts +2453 -894
- package/src/session/default-model-selection.ts +93 -0
- package/src/session/fallback-chain-controller.ts +136 -0
- package/src/session/session-manager.ts +438 -11
- package/src/session/session-storage.ts +8 -1
- package/src/setup/credential-auto-import.ts +290 -35
- package/src/setup/hermes/templates/operator-instructions.v1.md +3 -5
- package/src/slash-commands/builtin-registry.ts +2 -1
- package/src/task/executor.ts +62 -13
- package/src/tools/ask-answer-registry.ts +1 -1
- package/src/tools/ask.ts +13 -8
- package/src/tools/browser/STEALTH-THIRD-PARTY-NOTICES.md +26 -0
- package/src/tools/browser/benchmark/detector-report.ts +157 -0
- package/src/tools/browser/benchmark/run-suite.ts +39 -0
- package/src/tools/browser/launch.ts +124 -19
- package/src/tools/browser/profile-discovery.ts +63 -0
- package/src/tools/browser/profile-posture.ts +59 -0
- package/src/tools/browser/profile-reuse.ts +102 -0
- package/src/tools/browser/profile-warmup.ts +109 -0
- package/src/tools/browser/registry.ts +87 -11
- package/src/tools/browser/tab-protocol.ts +1 -0
- package/src/tools/browser/tab-supervisor.ts +1 -0
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/browser.ts +6 -0
- package/src/tools/index.ts +8 -8
- package/src/tools/puppeteer/14_stealth_permissions.txt +57 -0
- package/src/tools/puppeteer/15_stealth_webrtc.txt +60 -0
- package/src/tools/telegram-send.ts +2 -2
- package/vendor/insane-search/MANIFEST.json +2 -1
- package/vendor/insane-search/engine/bias_check.py +4 -1
- package/vendor/insane-search/engine/phase0.py +26 -1
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
- package/dist/types/modes/bridge/auth.d.ts +0 -12
- package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
- package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
- package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
- package/dist/types/modes/bridge/event-stream.d.ts +0 -8
- package/dist/types/modes/rpc/host-tools.d.ts +0 -1
- package/dist/types/modes/rpc/host-uris.d.ts +0 -1
- package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
- package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
- package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
- package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
- package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
- package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
- package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
- package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
- package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
- package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
- package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
- package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
- package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
- package/dist/types/notifications/config.d.ts +0 -132
- package/src/harness-control-plane/rpc-adapter.ts +0 -290
- package/src/modes/bridge/auth.ts +0 -41
- package/src/modes/bridge/bridge-client-bridge.ts +0 -47
- package/src/modes/bridge/bridge-mode.ts +0 -675
- package/src/modes/bridge/bridge-ui-context.ts +0 -200
- package/src/modes/bridge/event-stream.ts +0 -70
- package/src/modes/rpc/host-tools.ts +0 -1
- package/src/modes/rpc/host-uris.ts +0 -1
- package/src/modes/rpc/rpc-client.ts +0 -1166
- package/src/modes/rpc/rpc-mode.ts +0 -1029
- package/src/modes/rpc/rpc-socket-security.ts +0 -115
- package/src/modes/rpc/rpc-types.ts +0 -637
- package/src/modes/shared/agent-wire/command-contract.ts +0 -18
- package/src/modes/shared/agent-wire/command-dispatch.ts +0 -498
- package/src/modes/shared/agent-wire/command-validation.ts +0 -181
- package/src/modes/shared/agent-wire/handshake.ts +0 -157
- package/src/modes/shared/agent-wire/host-tool-bridge.ts +0 -197
- package/src/modes/shared/agent-wire/host-uri-bridge.ts +0 -236
- package/src/modes/shared/agent-wire/protocol.ts +0 -46
- package/src/modes/shared/agent-wire/responses.ts +0 -17
- package/src/modes/shared/agent-wire/scopes.ts +0 -94
- package/src/modes/shared/agent-wire/session-registry.ts +0 -109
- package/src/modes/shared/agent-wire/ui-request-broker.ts +0 -150
- package/src/modes/shared/agent-wire/ui-result.ts +0 -48
- package/src/modes/shared/agent-wire/unattended-action-policy.ts +0 -365
- package/src/modes/shared/agent-wire/unattended-audit.ts +0 -176
- package/src/modes/shared/agent-wire/unattended-run-controller.ts +0 -426
- package/src/modes/shared/agent-wire/unattended-session.ts +0 -528
- package/src/notifications/config.ts +0 -219
- package/src/notifications/index.ts +0 -1835
- package/src/notifications/telegram-daemon-cli.ts +0 -243
- package/src/tools/puppeteer/09_stealth_locale.txt +0 -46
- /package/dist/types/{notifications → sdk/bus}/attachment-registry.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/chat-adapters.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/daemon-paths.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/engine.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/helpers.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/lifecycle-commands.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/operator-runtime.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rate-limit-pool.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/recent-activity.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/reply-sent-store.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rich-draft.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rich-render.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/threaded-inbound.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/threaded-render.d.ts +0 -0
- /package/src/{notifications → sdk/bus}/attachment-registry.ts +0 -0
- /package/src/{notifications → sdk/bus}/chat-adapters.ts +0 -0
- /package/src/{notifications → sdk/bus}/daemon-paths.ts +0 -0
- /package/src/{notifications → sdk/bus}/engine.ts +0 -0
- /package/src/{notifications → sdk/bus}/helpers.ts +0 -0
- /package/src/{notifications → sdk/bus}/operator-runtime.ts +0 -0
- /package/src/{notifications → sdk/bus}/rate-limit-pool.ts +0 -0
- /package/src/{notifications → sdk/bus}/recent-activity.ts +0 -0
- /package/src/{notifications → sdk/bus}/reply-sent-store.ts +0 -0
- /package/src/{notifications → sdk/bus}/rich-draft.ts +0 -0
- /package/src/{notifications → sdk/bus}/rich-render.ts +0 -0
- /package/src/{notifications → sdk/bus}/threaded-inbound.ts +0 -0
- /package/src/{notifications → sdk/bus}/threaded-render.ts +0 -0
|
@@ -4,6 +4,7 @@ import * as fs from "node:fs";
|
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { type AgentMessage, canContinuePersistedHistory } from "@gajae-code/agent-core";
|
|
7
|
+
import type { ConfiguredModelChainEntry as SharedConfiguredModelChainEntry } from "@gajae-code/agent-core/compaction";
|
|
7
8
|
import type {
|
|
8
9
|
ImageContent,
|
|
9
10
|
Message,
|
|
@@ -221,6 +222,14 @@ export interface ModelChangeEntry extends SessionEntryBase {
|
|
|
221
222
|
thinkingLevel?: string | null;
|
|
222
223
|
}
|
|
223
224
|
|
|
225
|
+
/** Persisted configured fallback chain for one model role. */
|
|
226
|
+
export type ConfiguredModelChainEntry = SharedConfiguredModelChainEntry;
|
|
227
|
+
|
|
228
|
+
export type ConfiguredModelChain = Pick<
|
|
229
|
+
ConfiguredModelChainEntry,
|
|
230
|
+
"role" | "entries" | "origin" | "identity" | "explicitHead" | "cleared"
|
|
231
|
+
>;
|
|
232
|
+
|
|
224
233
|
export interface ServiceTierChangeEntry extends SessionEntryBase {
|
|
225
234
|
type: "service_tier_change";
|
|
226
235
|
serviceTier: ServiceTier | null;
|
|
@@ -353,11 +362,31 @@ export type SessionEntry =
|
|
|
353
362
|
| TtsrInjectionEntry
|
|
354
363
|
| MCPToolSelectionEntry
|
|
355
364
|
| SessionInitEntry
|
|
356
|
-
| ModeChangeEntry
|
|
365
|
+
| ModeChangeEntry
|
|
366
|
+
| ConfiguredModelChainEntry;
|
|
357
367
|
|
|
358
368
|
/** Raw file entry (includes header) */
|
|
359
369
|
export type FileEntry = SessionHeader | SessionEntry;
|
|
360
370
|
|
|
371
|
+
export type DefaultModelSelectionStage = {
|
|
372
|
+
readonly entryRevision: number;
|
|
373
|
+
readonly headerExportRevision: number;
|
|
374
|
+
readonly sessionId: string;
|
|
375
|
+
readonly sessionFile: string | undefined;
|
|
376
|
+
readonly entries: readonly FileEntry[];
|
|
377
|
+
readonly tempPath: string | undefined;
|
|
378
|
+
readonly persistsToExistingFile: boolean;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
export type DefaultModelSelectionPromotion =
|
|
382
|
+
| { readonly kind: "promoted" }
|
|
383
|
+
| { readonly kind: "not_promoted"; readonly error?: Error }
|
|
384
|
+
| { readonly kind: "unknown"; readonly error: Error };
|
|
385
|
+
|
|
386
|
+
type SessionFileReplacementSyncOutcome =
|
|
387
|
+
| { readonly kind: "replaced" }
|
|
388
|
+
| { readonly kind: "restored_previous"; readonly error: Error };
|
|
389
|
+
|
|
361
390
|
/** Tree node for getTree() - defensive copy of session structure */
|
|
362
391
|
export interface SessionTreeNode {
|
|
363
392
|
entry: SessionEntry;
|
|
@@ -372,6 +401,9 @@ export interface SessionContext {
|
|
|
372
401
|
serviceTier?: ServiceTier;
|
|
373
402
|
/** Model roles: { default: "provider/modelId", small: "provider/modelId", ... } */
|
|
374
403
|
models: Record<string, string>;
|
|
404
|
+
/** Configured fallback chains for model roles on the active branch. */
|
|
405
|
+
configuredModelChains: Record<string, ConfiguredModelChain>;
|
|
406
|
+
|
|
375
407
|
/** Names of TTSR rules that have been injected this session */
|
|
376
408
|
injectedTtsrRules: string[];
|
|
377
409
|
/** Rich TTSR rule injection records for repeat resume. */
|
|
@@ -428,6 +460,20 @@ export interface StrictSessionOpenFailure {
|
|
|
428
460
|
reason: ResumeTailError["reason"] | "identity-mismatch";
|
|
429
461
|
}
|
|
430
462
|
|
|
463
|
+
/** Exact transcript bytes and authority captured from one descriptor-bound read. */
|
|
464
|
+
export interface CapturedSessionTranscriptSnapshot {
|
|
465
|
+
sourcePath: string;
|
|
466
|
+
identity: ResumeSessionIdentity;
|
|
467
|
+
content: Uint8Array;
|
|
468
|
+
storage: SessionStorage;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export type StrictSessionCaptureResult =
|
|
472
|
+
| { kind: "captured"; snapshot: CapturedSessionTranscriptSnapshot }
|
|
473
|
+
| ResumeTailError;
|
|
474
|
+
|
|
475
|
+
export type StrictSessionForkResult = { kind: "forked"; manager: SessionManager } | StrictSessionOpenFailure;
|
|
476
|
+
|
|
431
477
|
/** Result of opening an inspected session without create-or-rewrite fallback. */
|
|
432
478
|
export type StrictSessionOpenResult = StrictSessionOpenSuccess | StrictSessionOpenFailure;
|
|
433
479
|
|
|
@@ -744,6 +790,31 @@ export function parseSessionEntries(content: string): FileEntry[] {
|
|
|
744
790
|
return parseJsonlLenient<FileEntry>(content);
|
|
745
791
|
}
|
|
746
792
|
|
|
793
|
+
function normalizeConfiguredModelChainEntry(entry: unknown): ConfiguredModelChain | undefined {
|
|
794
|
+
if (!entry || typeof entry !== "object") return undefined;
|
|
795
|
+
const candidate = entry as Partial<ConfiguredModelChainEntry>;
|
|
796
|
+
if (typeof candidate.role !== "string" || candidate.role.length === 0) return undefined;
|
|
797
|
+
if (candidate.cleared === true) {
|
|
798
|
+
return {
|
|
799
|
+
role: candidate.role,
|
|
800
|
+
entries: [],
|
|
801
|
+
origin: typeof candidate.origin === "string" ? candidate.origin : "session",
|
|
802
|
+
explicitHead: candidate.explicitHead === true,
|
|
803
|
+
cleared: true,
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
if (!Array.isArray(candidate.entries)) return undefined;
|
|
807
|
+
const entries = candidate.entries.filter((model): model is string => typeof model === "string" && model.length > 0);
|
|
808
|
+
if (entries.length === 0) return undefined;
|
|
809
|
+
return {
|
|
810
|
+
role: candidate.role,
|
|
811
|
+
entries,
|
|
812
|
+
origin: typeof candidate.origin === "string" ? candidate.origin : "session",
|
|
813
|
+
...(typeof candidate.identity === "string" ? { identity: candidate.identity } : {}),
|
|
814
|
+
explicitHead: candidate.explicitHead === true,
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
|
|
747
818
|
export function getLatestCompactionEntry(entries: SessionEntry[]): CompactionEntry | null {
|
|
748
819
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
749
820
|
if (entries[i].type === "compaction") {
|
|
@@ -781,6 +852,8 @@ export function buildSessionContext(
|
|
|
781
852
|
thinkingLevel: "off",
|
|
782
853
|
serviceTier: undefined,
|
|
783
854
|
models: {},
|
|
855
|
+
configuredModelChains: {},
|
|
856
|
+
|
|
784
857
|
injectedTtsrRules: [],
|
|
785
858
|
injectedTtsrRuleRecords: [],
|
|
786
859
|
ttsrMessageCount: 0,
|
|
@@ -804,6 +877,8 @@ export function buildSessionContext(
|
|
|
804
877
|
thinkingLevel: "off",
|
|
805
878
|
serviceTier: undefined,
|
|
806
879
|
models: {},
|
|
880
|
+
configuredModelChains: {},
|
|
881
|
+
|
|
807
882
|
injectedTtsrRules: [],
|
|
808
883
|
injectedTtsrRuleRecords: [],
|
|
809
884
|
ttsrMessageCount: 0,
|
|
@@ -829,6 +904,8 @@ export function buildSessionContext(
|
|
|
829
904
|
let thinkingLevel: string | undefined = "off";
|
|
830
905
|
let serviceTier: ServiceTier | undefined;
|
|
831
906
|
const models: Record<string, string> = {};
|
|
907
|
+
const configuredModelChains: Record<string, ConfiguredModelChain> = {};
|
|
908
|
+
|
|
832
909
|
let compaction: CompactionEntry | null = null;
|
|
833
910
|
const injectedTtsrRulesSet = new Set<string>();
|
|
834
911
|
const injectedTtsrRuleRecords = new Map<string, TtsrInjectionRecord>();
|
|
@@ -859,6 +936,13 @@ export function buildSessionContext(
|
|
|
859
936
|
hasExplicitDefaultModel = true;
|
|
860
937
|
}
|
|
861
938
|
}
|
|
939
|
+
} else if (entry.type === "configured_model_chain") {
|
|
940
|
+
const configuredChain = normalizeConfiguredModelChainEntry(entry);
|
|
941
|
+
if (configuredChain?.cleared) {
|
|
942
|
+
delete configuredModelChains[configuredChain.role];
|
|
943
|
+
} else if (configuredChain) {
|
|
944
|
+
configuredModelChains[configuredChain.role] = configuredChain;
|
|
945
|
+
}
|
|
862
946
|
} else if (entry.type === "service_tier_change") {
|
|
863
947
|
serviceTier = entry.serviceTier ?? undefined;
|
|
864
948
|
} else if (entry.type === "message" && entry.message.role === "assistant") {
|
|
@@ -897,6 +981,16 @@ export function buildSessionContext(
|
|
|
897
981
|
}
|
|
898
982
|
|
|
899
983
|
const injectedTtsrRules = Array.from(injectedTtsrRulesSet);
|
|
984
|
+
for (const [role, model] of Object.entries(models)) {
|
|
985
|
+
if (role in configuredModelChains) continue;
|
|
986
|
+
configuredModelChains[role] = {
|
|
987
|
+
role,
|
|
988
|
+
entries: [model],
|
|
989
|
+
origin: "legacy_session",
|
|
990
|
+
explicitHead: true,
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
|
|
900
994
|
const injectedTtsrRuleRecordsArray = Array.from(injectedTtsrRuleRecords.values());
|
|
901
995
|
|
|
902
996
|
// Build messages and collect corresponding entries
|
|
@@ -996,6 +1090,8 @@ export function buildSessionContext(
|
|
|
996
1090
|
thinkingLevel,
|
|
997
1091
|
serviceTier,
|
|
998
1092
|
models,
|
|
1093
|
+
configuredModelChains,
|
|
1094
|
+
|
|
999
1095
|
injectedTtsrRules,
|
|
1000
1096
|
injectedTtsrRuleRecords: injectedTtsrRuleRecordsArray,
|
|
1001
1097
|
ttsrMessageCount,
|
|
@@ -1014,6 +1110,13 @@ function cloneSessionContext(context: SessionContext): SessionContext {
|
|
|
1014
1110
|
...context,
|
|
1015
1111
|
messages,
|
|
1016
1112
|
models: { ...context.models },
|
|
1113
|
+
configuredModelChains: Object.fromEntries(
|
|
1114
|
+
Object.entries(context.configuredModelChains ?? {}).map(([role, chain]) => [
|
|
1115
|
+
role,
|
|
1116
|
+
{ ...chain, entries: [...chain.entries] },
|
|
1117
|
+
]),
|
|
1118
|
+
),
|
|
1119
|
+
|
|
1017
1120
|
injectedTtsrRules: [...context.injectedTtsrRules],
|
|
1018
1121
|
injectedTtsrRuleRecords: context.injectedTtsrRuleRecords?.map(record => ({ ...record })),
|
|
1019
1122
|
ttsrMessageCount: context.ttsrMessageCount,
|
|
@@ -1171,6 +1274,7 @@ function sameResumeStat(left: SessionStorageStat, right: SessionStorageStat): bo
|
|
|
1171
1274
|
|
|
1172
1275
|
interface ResumeInspectionSnapshot {
|
|
1173
1276
|
identity: ResumeSessionIdentity;
|
|
1277
|
+
content: Uint8Array;
|
|
1174
1278
|
entries: FileEntry[];
|
|
1175
1279
|
context: SessionContext;
|
|
1176
1280
|
migrationApplied: boolean;
|
|
@@ -1218,6 +1322,9 @@ function hasStrictSessionSchema(entries: FileEntry[]): boolean {
|
|
|
1218
1322
|
)
|
|
1219
1323
|
return false;
|
|
1220
1324
|
break;
|
|
1325
|
+
case "configured_model_chain":
|
|
1326
|
+
if (!normalizeConfiguredModelChainEntry(value)) return false;
|
|
1327
|
+
break;
|
|
1221
1328
|
case "compaction":
|
|
1222
1329
|
if (
|
|
1223
1330
|
typeof value.summary !== "string" ||
|
|
@@ -1344,7 +1451,7 @@ function inspectResumeSessionFile(
|
|
|
1344
1451
|
mtimeNs: snapshot.mtimeNs,
|
|
1345
1452
|
sha256: crypto.createHash("sha256").update(bytes).digest("hex"),
|
|
1346
1453
|
};
|
|
1347
|
-
return { identity, entries, context, migrationApplied };
|
|
1454
|
+
return { identity, content: Uint8Array.from(bytes), entries, context, migrationApplied };
|
|
1348
1455
|
} catch {
|
|
1349
1456
|
return { kind: "error", reason: "malformed" };
|
|
1350
1457
|
}
|
|
@@ -3131,6 +3238,7 @@ interface SessionManagerStateSnapshot {
|
|
|
3131
3238
|
titleSource: "auto" | "user" | undefined;
|
|
3132
3239
|
sessionFile: string | undefined;
|
|
3133
3240
|
flushed: boolean;
|
|
3241
|
+
ensuredOnDisk: boolean;
|
|
3134
3242
|
needsFullRewriteOnNextPersist: boolean;
|
|
3135
3243
|
fileEntries: FileEntry[];
|
|
3136
3244
|
materializedFileEntries: FileEntry[];
|
|
@@ -3323,6 +3431,7 @@ export class SessionManager {
|
|
|
3323
3431
|
titleSource: this.#titleSource,
|
|
3324
3432
|
sessionFile: this.#sessionFile,
|
|
3325
3433
|
flushed: this.#flushed,
|
|
3434
|
+
ensuredOnDisk: this.#ensuredOnDisk,
|
|
3326
3435
|
needsFullRewriteOnNextPersist: this.#needsFullRewriteOnNextPersist,
|
|
3327
3436
|
// Snapshot entry objects by reference: switch/reload replaces the active entry array,
|
|
3328
3437
|
// so rollback does not need structured cloning of extension/custom details.
|
|
@@ -3345,6 +3454,7 @@ export class SessionManager {
|
|
|
3345
3454
|
this.#titleSource = snapshot.titleSource;
|
|
3346
3455
|
this.#sessionFile = snapshot.sessionFile;
|
|
3347
3456
|
this.#flushed = snapshot.flushed;
|
|
3457
|
+
this.#ensuredOnDisk = snapshot.ensuredOnDisk;
|
|
3348
3458
|
this.#needsFullRewriteOnNextPersist = snapshot.needsFullRewriteOnNextPersist;
|
|
3349
3459
|
this.#fileEntries = restoredFileEntries;
|
|
3350
3460
|
if (!retainsPersistWriter) {
|
|
@@ -3636,7 +3746,7 @@ export class SessionManager {
|
|
|
3636
3746
|
}
|
|
3637
3747
|
|
|
3638
3748
|
/** Sync version for initial creation (no existing writer to close) */
|
|
3639
|
-
#newSessionSync(options?: NewSessionOptions): string | undefined {
|
|
3749
|
+
#newSessionSync(options?: NewSessionOptions, writeBreadcrumb = true): string | undefined {
|
|
3640
3750
|
this.#persistChain = Promise.resolve();
|
|
3641
3751
|
this.#persistError = undefined;
|
|
3642
3752
|
this.#persistErrorReported = false;
|
|
@@ -3671,7 +3781,7 @@ export class SessionManager {
|
|
|
3671
3781
|
if (this.persist) {
|
|
3672
3782
|
const fileTimestamp = timestamp.replace(/[:.]/g, "-");
|
|
3673
3783
|
this.#sessionFile = path.join(this.getSessionDir(), `${fileTimestamp}_${this.#sessionId}.jsonl`);
|
|
3674
|
-
writeTerminalBreadcrumb(this.cwd, this.#sessionFile);
|
|
3784
|
+
if (writeBreadcrumb) writeTerminalBreadcrumb(this.cwd, this.#sessionFile);
|
|
3675
3785
|
}
|
|
3676
3786
|
this.#resetResidentTextBlobStore();
|
|
3677
3787
|
return this.#sessionFile;
|
|
@@ -3795,7 +3905,11 @@ export class SessionManager {
|
|
|
3795
3905
|
// shared `*.bak` glob on both real and in-memory storage backends and promote it back to
|
|
3796
3906
|
// the primary on the next session-dir scan.
|
|
3797
3907
|
|
|
3798
|
-
#replaceSessionFileAfterEpermSync(
|
|
3908
|
+
#replaceSessionFileAfterEpermSync(
|
|
3909
|
+
tempPath: string,
|
|
3910
|
+
targetPath: string,
|
|
3911
|
+
renameError: unknown,
|
|
3912
|
+
): SessionFileReplacementSyncOutcome {
|
|
3799
3913
|
const dir = path.resolve(targetPath, "..");
|
|
3800
3914
|
const backupPath = path.join(dir, `${path.basename(targetPath)}.${Snowflake.next()}.bak`);
|
|
3801
3915
|
try {
|
|
@@ -3803,7 +3917,7 @@ export class SessionManager {
|
|
|
3803
3917
|
} catch (err) {
|
|
3804
3918
|
if (isEnoent(err)) {
|
|
3805
3919
|
this.storage.renameSync(tempPath, targetPath);
|
|
3806
|
-
return;
|
|
3920
|
+
return { kind: "replaced" };
|
|
3807
3921
|
}
|
|
3808
3922
|
throw toError(renameError);
|
|
3809
3923
|
}
|
|
@@ -3822,7 +3936,7 @@ export class SessionManager {
|
|
|
3822
3936
|
{ cause: originalError },
|
|
3823
3937
|
);
|
|
3824
3938
|
}
|
|
3825
|
-
|
|
3939
|
+
return { kind: "restored_previous", error: replaceError };
|
|
3826
3940
|
}
|
|
3827
3941
|
|
|
3828
3942
|
try {
|
|
@@ -3836,6 +3950,7 @@ export class SessionManager {
|
|
|
3836
3950
|
});
|
|
3837
3951
|
}
|
|
3838
3952
|
}
|
|
3953
|
+
return { kind: "replaced" };
|
|
3839
3954
|
}
|
|
3840
3955
|
|
|
3841
3956
|
async #replaceSessionFileAfterEperm(tempPath: string, targetPath: string, renameError: unknown): Promise<void> {
|
|
@@ -3890,13 +4005,13 @@ export class SessionManager {
|
|
|
3890
4005
|
}
|
|
3891
4006
|
}
|
|
3892
4007
|
|
|
3893
|
-
#replaceSessionFileSync(tempPath: string, targetPath: string):
|
|
4008
|
+
#replaceSessionFileSync(tempPath: string, targetPath: string): SessionFileReplacementSyncOutcome {
|
|
3894
4009
|
try {
|
|
3895
4010
|
this.storage.renameSync(tempPath, targetPath);
|
|
4011
|
+
return { kind: "replaced" };
|
|
3896
4012
|
} catch (err) {
|
|
3897
4013
|
if (hasFsCode(err, "EPERM")) {
|
|
3898
|
-
this.#replaceSessionFileAfterEpermSync(tempPath, targetPath, err);
|
|
3899
|
-
return;
|
|
4014
|
+
return this.#replaceSessionFileAfterEpermSync(tempPath, targetPath, err);
|
|
3900
4015
|
}
|
|
3901
4016
|
|
|
3902
4017
|
throw toError(err);
|
|
@@ -3913,7 +4028,8 @@ export class SessionManager {
|
|
|
3913
4028
|
writer.writeSync(entry);
|
|
3914
4029
|
}
|
|
3915
4030
|
writer.closeSync();
|
|
3916
|
-
this.#replaceSessionFileSync(tempPath, this.#sessionFile);
|
|
4031
|
+
const replacement = this.#replaceSessionFileSync(tempPath, this.#sessionFile);
|
|
4032
|
+
if (replacement.kind === "restored_previous") throw replacement.error;
|
|
3917
4033
|
} catch (err) {
|
|
3918
4034
|
// closeSync is now truthful and may throw; wrap the best-effort cleanup so
|
|
3919
4035
|
// the original error (write/close failure) is the one surfaced, not the
|
|
@@ -3956,6 +4072,38 @@ export class SessionManager {
|
|
|
3956
4072
|
}
|
|
3957
4073
|
}
|
|
3958
4074
|
|
|
4075
|
+
async #writeStagedDefaultModelSelection(
|
|
4076
|
+
entries: readonly FileEntry[],
|
|
4077
|
+
sessionFile: string,
|
|
4078
|
+
): Promise<string | undefined> {
|
|
4079
|
+
if (!this.storage.existsSync(sessionFile)) return undefined;
|
|
4080
|
+
const dir = path.resolve(sessionFile, "..");
|
|
4081
|
+
const tempPath = path.join(dir, `.${path.basename(sessionFile)}.${Snowflake.next()}.default-selection.tmp`);
|
|
4082
|
+
const writer = new NdjsonFileWriter(this.storage, tempPath, { flags: "w" });
|
|
4083
|
+
try {
|
|
4084
|
+
const persistedEntries = await Promise.all(
|
|
4085
|
+
materializeResidentEntriesForPersistenceSync([...entries], this.#residentBlobStores()).map(entry =>
|
|
4086
|
+
prepareEntryForPersistence(entry, this.#blobStore),
|
|
4087
|
+
),
|
|
4088
|
+
);
|
|
4089
|
+
for (const entry of persistedEntries) {
|
|
4090
|
+
await writer.write(entry);
|
|
4091
|
+
}
|
|
4092
|
+
await writer.flush();
|
|
4093
|
+
await writer.fsync();
|
|
4094
|
+
await writer.close();
|
|
4095
|
+
return tempPath;
|
|
4096
|
+
} catch (error) {
|
|
4097
|
+
try {
|
|
4098
|
+
await writer.close();
|
|
4099
|
+
} catch {}
|
|
4100
|
+
try {
|
|
4101
|
+
await this.storage.unlink(tempPath);
|
|
4102
|
+
} catch {}
|
|
4103
|
+
throw toError(error);
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
|
|
3959
4107
|
async #rewriteFile(): Promise<void> {
|
|
3960
4108
|
if (!this.persist || !this.#sessionFile) return;
|
|
3961
4109
|
await this.#queuePersistTask(async () => {
|
|
@@ -3988,6 +4136,109 @@ export class SessionManager {
|
|
|
3988
4136
|
return this.persist;
|
|
3989
4137
|
}
|
|
3990
4138
|
|
|
4139
|
+
async stageDefaultModelSelection(
|
|
4140
|
+
model: string,
|
|
4141
|
+
thinkingLevel: string | undefined,
|
|
4142
|
+
options?: { readonly appendThinkingLevel: boolean },
|
|
4143
|
+
): Promise<DefaultModelSelectionStage> {
|
|
4144
|
+
const entryRevision = this.#entryRevision;
|
|
4145
|
+
const headerExportRevision = this.#headerExportRevision;
|
|
4146
|
+
const sessionId = this.#sessionId;
|
|
4147
|
+
const sessionFile = this.#sessionFile;
|
|
4148
|
+
const entryIds = new Map(this.#byId);
|
|
4149
|
+
let parentId = this.#leafId;
|
|
4150
|
+
const entries = [...this.#fileEntries];
|
|
4151
|
+
const temporaryEntry: ModelChangeEntry = {
|
|
4152
|
+
type: "model_change",
|
|
4153
|
+
id: generateId(entryIds),
|
|
4154
|
+
parentId,
|
|
4155
|
+
timestamp: new Date().toISOString(),
|
|
4156
|
+
model,
|
|
4157
|
+
role: "temporary",
|
|
4158
|
+
};
|
|
4159
|
+
entryIds.set(temporaryEntry.id, temporaryEntry);
|
|
4160
|
+
entries.push(temporaryEntry);
|
|
4161
|
+
parentId = temporaryEntry.id;
|
|
4162
|
+
if (options?.appendThinkingLevel) {
|
|
4163
|
+
const thinkingEntry: ThinkingLevelChangeEntry = {
|
|
4164
|
+
type: "thinking_level_change",
|
|
4165
|
+
id: generateId(entryIds),
|
|
4166
|
+
parentId,
|
|
4167
|
+
timestamp: new Date().toISOString(),
|
|
4168
|
+
thinkingLevel: thinkingLevel ?? null,
|
|
4169
|
+
};
|
|
4170
|
+
entryIds.set(thinkingEntry.id, thinkingEntry);
|
|
4171
|
+
entries.push(thinkingEntry);
|
|
4172
|
+
parentId = thinkingEntry.id;
|
|
4173
|
+
}
|
|
4174
|
+
const modelEntry: ModelChangeEntry = {
|
|
4175
|
+
type: "model_change",
|
|
4176
|
+
id: generateId(entryIds),
|
|
4177
|
+
parentId,
|
|
4178
|
+
timestamp: new Date().toISOString(),
|
|
4179
|
+
model,
|
|
4180
|
+
role: "default",
|
|
4181
|
+
};
|
|
4182
|
+
entries.push(modelEntry);
|
|
4183
|
+
const persistsToExistingFile = this.persist && sessionFile !== undefined && this.storage.existsSync(sessionFile);
|
|
4184
|
+
const tempPath = persistsToExistingFile
|
|
4185
|
+
? await this.#writeStagedDefaultModelSelection(entries, sessionFile)
|
|
4186
|
+
: undefined;
|
|
4187
|
+
return {
|
|
4188
|
+
entryRevision,
|
|
4189
|
+
headerExportRevision,
|
|
4190
|
+
sessionId,
|
|
4191
|
+
sessionFile,
|
|
4192
|
+
entries,
|
|
4193
|
+
tempPath,
|
|
4194
|
+
persistsToExistingFile,
|
|
4195
|
+
};
|
|
4196
|
+
}
|
|
4197
|
+
|
|
4198
|
+
promoteDefaultModelSelection(stage: DefaultModelSelectionStage): DefaultModelSelectionPromotion {
|
|
4199
|
+
if (
|
|
4200
|
+
stage.entryRevision !== this.#entryRevision ||
|
|
4201
|
+
stage.headerExportRevision !== this.#headerExportRevision ||
|
|
4202
|
+
stage.sessionId !== this.#sessionId ||
|
|
4203
|
+
stage.sessionFile !== this.#sessionFile
|
|
4204
|
+
) {
|
|
4205
|
+
return { kind: "not_promoted" };
|
|
4206
|
+
}
|
|
4207
|
+
if (stage.persistsToExistingFile) {
|
|
4208
|
+
if (!stage.tempPath || !this.#sessionFile)
|
|
4209
|
+
return { kind: "unknown", error: new Error("Missing staged session replacement") };
|
|
4210
|
+
try {
|
|
4211
|
+
this.#closePersistWriterInternalSync();
|
|
4212
|
+
const replacement = this.#replaceSessionFileSync(stage.tempPath, this.#sessionFile);
|
|
4213
|
+
if (replacement.kind === "restored_previous") {
|
|
4214
|
+
return { kind: "not_promoted", error: replacement.error };
|
|
4215
|
+
}
|
|
4216
|
+
} catch (error) {
|
|
4217
|
+
if (this.#persistWriter?.getCloseState() === "close_failed_retryable") {
|
|
4218
|
+
return { kind: "not_promoted", error: new Error("Session replacement could not be completed.") };
|
|
4219
|
+
}
|
|
4220
|
+
return { kind: "unknown", error: toError(error) };
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
this.#fileEntries = [...stage.entries];
|
|
4224
|
+
this.#resetResidentTextBlobStore();
|
|
4225
|
+
this.#reexternalizeFileEntriesForResidentStore();
|
|
4226
|
+
this.#needsFullRewriteOnNextPersist = false;
|
|
4227
|
+
this.#flushed = stage.persistsToExistingFile;
|
|
4228
|
+
this.#ensuredOnDisk = stage.persistsToExistingFile;
|
|
4229
|
+
this.#bumpAllRevisions();
|
|
4230
|
+
return { kind: "promoted" };
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
async discardDefaultModelSelectionStage(stage: DefaultModelSelectionStage): Promise<void> {
|
|
4234
|
+
if (!stage.tempPath) return;
|
|
4235
|
+
try {
|
|
4236
|
+
await this.storage.unlink(stage.tempPath);
|
|
4237
|
+
} catch (error) {
|
|
4238
|
+
if (!isEnoent(error)) throw toError(error);
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
|
|
3991
4242
|
/**
|
|
3992
4243
|
* Force-persist all current entries to disk, even when no assistant message exists yet.
|
|
3993
4244
|
* Used by ACP mode where session/new must create a discoverable session immediately.
|
|
@@ -4396,6 +4647,24 @@ export class SessionManager {
|
|
|
4396
4647
|
}
|
|
4397
4648
|
}
|
|
4398
4649
|
|
|
4650
|
+
/** Append a configured fallback chain as child of current leaf, then advance leaf. Returns entry id. */
|
|
4651
|
+
appendConfiguredModelChain(chain: ConfiguredModelChain): string {
|
|
4652
|
+
const entry: ConfiguredModelChainEntry = {
|
|
4653
|
+
type: "configured_model_chain",
|
|
4654
|
+
id: generateId(this.#byId),
|
|
4655
|
+
parentId: this.#leafId,
|
|
4656
|
+
timestamp: new Date().toISOString(),
|
|
4657
|
+
role: chain.role,
|
|
4658
|
+
entries: [...chain.entries],
|
|
4659
|
+
origin: chain.origin,
|
|
4660
|
+
identity: chain.identity,
|
|
4661
|
+
explicitHead: chain.explicitHead,
|
|
4662
|
+
cleared: chain.cleared,
|
|
4663
|
+
};
|
|
4664
|
+
this.#appendEntry(entry);
|
|
4665
|
+
return entry.id;
|
|
4666
|
+
}
|
|
4667
|
+
|
|
4399
4668
|
/** Append a message as child of current leaf, then advance leaf. Returns entry id.
|
|
4400
4669
|
* Does not allow writing CompactionSummaryMessage and BranchSummaryMessage directly.
|
|
4401
4670
|
* Reason: we want these to be top-level entries in the session, not message session entries,
|
|
@@ -5429,6 +5698,11 @@ export class SessionManager {
|
|
|
5429
5698
|
return computeDefaultSessionDir(cwd, storage, getSessionsDir(agentDir));
|
|
5430
5699
|
}
|
|
5431
5700
|
|
|
5701
|
+
/** Resolve the default session directory without creating or migrating storage. */
|
|
5702
|
+
static getDefaultSessionDirReadOnly(cwd: string, agentDir?: string): string {
|
|
5703
|
+
const { encodedDirName } = getDefaultSessionDirName(cwd);
|
|
5704
|
+
return path.join(getSessionsDir(agentDir), encodedDirName);
|
|
5705
|
+
}
|
|
5432
5706
|
/**
|
|
5433
5707
|
* Create a new session.
|
|
5434
5708
|
* @param cwd Working directory (stored in session header)
|
|
@@ -5520,6 +5794,159 @@ export class SessionManager {
|
|
|
5520
5794
|
}
|
|
5521
5795
|
}
|
|
5522
5796
|
|
|
5797
|
+
/** Capture exact source content for a strict fork without granting write ownership. */
|
|
5798
|
+
static captureTranscriptStrict(
|
|
5799
|
+
filePath: string,
|
|
5800
|
+
storage: SessionStorage = new FileSessionStorage(),
|
|
5801
|
+
): StrictSessionCaptureResult {
|
|
5802
|
+
const inspected = inspectResumeSessionFile(filePath, storage);
|
|
5803
|
+
if ("kind" in inspected) return inspected;
|
|
5804
|
+
return {
|
|
5805
|
+
kind: "captured",
|
|
5806
|
+
snapshot: {
|
|
5807
|
+
sourcePath: path.resolve(filePath),
|
|
5808
|
+
identity: inspected.identity,
|
|
5809
|
+
content: Uint8Array.from(inspected.content),
|
|
5810
|
+
storage,
|
|
5811
|
+
},
|
|
5812
|
+
};
|
|
5813
|
+
}
|
|
5814
|
+
|
|
5815
|
+
/**
|
|
5816
|
+
* Fork strictly from captured source bytes. The source pathname is used only to
|
|
5817
|
+
* revalidate captured authority before destination initialization and transcript
|
|
5818
|
+
* persistence; destination history always comes from the captured bytes.
|
|
5819
|
+
*/
|
|
5820
|
+
static async forkFromCaptured(
|
|
5821
|
+
snapshot: CapturedSessionTranscriptSnapshot,
|
|
5822
|
+
cwd: string,
|
|
5823
|
+
sessionDir?: string,
|
|
5824
|
+
): Promise<StrictSessionForkResult> {
|
|
5825
|
+
if (crypto.createHash("sha256").update(snapshot.content).digest("hex") !== snapshot.identity.sha256) {
|
|
5826
|
+
return { kind: "error", reason: "identity-mismatch" };
|
|
5827
|
+
}
|
|
5828
|
+
|
|
5829
|
+
let forkEntries: FileEntry[] = [];
|
|
5830
|
+
try {
|
|
5831
|
+
const content = new TextDecoder("utf-8", { fatal: true }).decode(snapshot.content);
|
|
5832
|
+
for (const line of content.split(/\r?\n/)) {
|
|
5833
|
+
if (line.length > 0) JSON.parse(line);
|
|
5834
|
+
}
|
|
5835
|
+
forkEntries = parseSessionEntries(content);
|
|
5836
|
+
const sourceHeader = forkEntries[0] as SessionHeader | undefined;
|
|
5837
|
+
if (sourceHeader?.type !== "session" || sourceHeader.id !== snapshot.identity.sessionId)
|
|
5838
|
+
return { kind: "error", reason: "identity-mismatch" };
|
|
5839
|
+
migrateToCurrentVersion(forkEntries);
|
|
5840
|
+
if (!hasStrictSessionSchema(forkEntries)) return { kind: "error", reason: "malformed" };
|
|
5841
|
+
} catch {
|
|
5842
|
+
return { kind: "error", reason: "malformed" };
|
|
5843
|
+
}
|
|
5844
|
+
|
|
5845
|
+
const revalidated = inspectResumeSessionFile(snapshot.sourcePath, snapshot.storage);
|
|
5846
|
+
if ("kind" in revalidated) return revalidated;
|
|
5847
|
+
if (!sameResumeIdentity(snapshot.identity, revalidated.identity)) {
|
|
5848
|
+
return { kind: "error", reason: "identity-mismatch" };
|
|
5849
|
+
}
|
|
5850
|
+
|
|
5851
|
+
const dir = sessionDir ?? SessionManager.getDefaultSessionDir(cwd, undefined, snapshot.storage);
|
|
5852
|
+
const removeSessionDirOnFailure =
|
|
5853
|
+
snapshot.storage instanceof FileSessionStorage && !snapshot.storage.existsSync(dir);
|
|
5854
|
+
let manager: SessionManager | undefined;
|
|
5855
|
+
let authorityFailure: StrictSessionOpenFailure | undefined;
|
|
5856
|
+
try {
|
|
5857
|
+
manager = new SessionManager(cwd, dir, true, snapshot.storage);
|
|
5858
|
+
await resolveBlobRefsInEntries(forkEntries, manager.#blobStore);
|
|
5859
|
+
manager.#fileEntries = forkEntries;
|
|
5860
|
+
const sourceHeader = manager.#fileEntries.find(e => e.type === "session") as SessionHeader | undefined;
|
|
5861
|
+
const historyEntries = manager.#fileEntries.filter(entry => entry.type !== "session") as SessionEntry[];
|
|
5862
|
+
manager.#newSessionSync({ parentSession: sourceHeader?.id }, false);
|
|
5863
|
+
manager.#resetResidentTextBlobStore();
|
|
5864
|
+
const newHeader = manager.#fileEntries[0] as SessionHeader;
|
|
5865
|
+
newHeader.title = sourceHeader?.title;
|
|
5866
|
+
newHeader.titleSource = sourceHeader?.titleSource;
|
|
5867
|
+
const residentBlobStores = manager.#residentBlobStores();
|
|
5868
|
+
manager.#fileEntries = [
|
|
5869
|
+
newHeader,
|
|
5870
|
+
...historyEntries.map(entry => prepareEntryForResidentSync(entry, residentBlobStores) as SessionEntry),
|
|
5871
|
+
];
|
|
5872
|
+
manager.#sessionName = newHeader.title;
|
|
5873
|
+
manager.#titleSource = newHeader.titleSource;
|
|
5874
|
+
manager.sanitizeLoadedOpenAIResponsesReplayMetadata();
|
|
5875
|
+
manager.#buildIndex();
|
|
5876
|
+
manager.#bumpAllRevisions();
|
|
5877
|
+
const beforeWrite = inspectResumeSessionFile(snapshot.sourcePath, snapshot.storage);
|
|
5878
|
+
if ("kind" in beforeWrite) {
|
|
5879
|
+
authorityFailure = beforeWrite;
|
|
5880
|
+
throw new Error("Captured fork source authority changed before destination write.");
|
|
5881
|
+
}
|
|
5882
|
+
if (!sameResumeIdentity(snapshot.identity, beforeWrite.identity)) {
|
|
5883
|
+
authorityFailure = { kind: "error", reason: "identity-mismatch" };
|
|
5884
|
+
throw new Error("Captured fork source authority changed before destination write.");
|
|
5885
|
+
}
|
|
5886
|
+
await manager.#rewriteFile();
|
|
5887
|
+
if (manager.#sessionFile) writeTerminalBreadcrumb(manager.cwd, manager.#sessionFile);
|
|
5888
|
+
return { kind: "forked", manager };
|
|
5889
|
+
} catch (error) {
|
|
5890
|
+
if (manager) {
|
|
5891
|
+
const sessionFile = manager.#sessionFile;
|
|
5892
|
+
const cleanupErrors: Error[] = [];
|
|
5893
|
+
try {
|
|
5894
|
+
await manager.close();
|
|
5895
|
+
} catch (cleanupError) {
|
|
5896
|
+
cleanupErrors.push(toError(cleanupError));
|
|
5897
|
+
}
|
|
5898
|
+
if (sessionFile) {
|
|
5899
|
+
try {
|
|
5900
|
+
await manager.storage.unlink(sessionFile);
|
|
5901
|
+
} catch (cleanupError) {
|
|
5902
|
+
if (!isEnoent(cleanupError)) cleanupErrors.push(toError(cleanupError));
|
|
5903
|
+
}
|
|
5904
|
+
if (manager.storage instanceof FileSessionStorage) {
|
|
5905
|
+
try {
|
|
5906
|
+
await fs.promises.rm(sessionFile.slice(0, -6), { recursive: true, force: true });
|
|
5907
|
+
} catch (cleanupError) {
|
|
5908
|
+
cleanupErrors.push(toError(cleanupError));
|
|
5909
|
+
}
|
|
5910
|
+
try {
|
|
5911
|
+
const sessionFileName = path.basename(sessionFile);
|
|
5912
|
+
const transientPrefix = `.${sessionFileName}.`;
|
|
5913
|
+
const backupPrefix = `${sessionFileName}.`;
|
|
5914
|
+
for (const name of await fs.promises.readdir(path.dirname(sessionFile))) {
|
|
5915
|
+
if (
|
|
5916
|
+
(name.startsWith(transientPrefix) && name.endsWith(".tmp")) ||
|
|
5917
|
+
(name.startsWith(backupPrefix) && name.endsWith(".bak"))
|
|
5918
|
+
) {
|
|
5919
|
+
await fs.promises.rm(path.join(path.dirname(sessionFile), name), {
|
|
5920
|
+
recursive: true,
|
|
5921
|
+
force: true,
|
|
5922
|
+
});
|
|
5923
|
+
}
|
|
5924
|
+
}
|
|
5925
|
+
} catch (cleanupError) {
|
|
5926
|
+
if (!isEnoent(cleanupError)) cleanupErrors.push(toError(cleanupError));
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
}
|
|
5930
|
+
if (removeSessionDirOnFailure) {
|
|
5931
|
+
try {
|
|
5932
|
+
await fs.promises.rmdir(dir);
|
|
5933
|
+
} catch (cleanupError) {
|
|
5934
|
+
const code = (cleanupError as NodeJS.ErrnoException).code;
|
|
5935
|
+
if (code !== "ENOENT" && code !== "ENOTEMPTY" && code !== "EEXIST")
|
|
5936
|
+
cleanupErrors.push(toError(cleanupError));
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
if (cleanupErrors.length > 0) {
|
|
5940
|
+
throw new Error(`Failed to clean up fork destination: ${cleanupErrors[0]!.message}`, {
|
|
5941
|
+
cause: toError(error),
|
|
5942
|
+
});
|
|
5943
|
+
}
|
|
5944
|
+
if (authorityFailure) return authorityFailure;
|
|
5945
|
+
}
|
|
5946
|
+
throw error;
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
|
|
5523
5950
|
/** Inspect a selected session without acquiring write-capable ownership. */
|
|
5524
5951
|
static async inspectSessionTailReadOnly(
|
|
5525
5952
|
filePath: string,
|
|
@@ -4,6 +4,13 @@ import * as path from "node:path";
|
|
|
4
4
|
import { isEnoent, pathIsWithin, peekFile, toError } from "@gajae-code/utils";
|
|
5
5
|
|
|
6
6
|
const utf8Decoder = new TextDecoder("utf-8");
|
|
7
|
+
function canonicalPathSync(value: string): string {
|
|
8
|
+
try {
|
|
9
|
+
return fs.realpathSync.native(value);
|
|
10
|
+
} catch {
|
|
11
|
+
return path.resolve(value);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
7
14
|
|
|
8
15
|
export interface SessionStorageStat {
|
|
9
16
|
dev: bigint;
|
|
@@ -614,7 +621,7 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
614
621
|
if (typeof header.cwd !== "string") {
|
|
615
622
|
throw new SessionDeleteVerificationError("cwd", "Transcript header is missing a cwd");
|
|
616
623
|
}
|
|
617
|
-
if (
|
|
624
|
+
if (canonicalPathSync(header.cwd) !== canonicalPathSync(expectedCwd)) {
|
|
618
625
|
throw new SessionDeleteVerificationError("cwd", "Transcript header cwd does not match authorization");
|
|
619
626
|
}
|
|
620
627
|
return { snapshot };
|